From 6c04a6df1a120eead69fb90b192e26017e95f334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Thu, 21 Dec 2023 11:53:35 +0100 Subject: [PATCH 001/573] :bug: fix default render product name --- openpype/hosts/houdini/plugins/publish/collect_vray_rop.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/houdini/plugins/publish/collect_vray_rop.py b/openpype/hosts/houdini/plugins/publish/collect_vray_rop.py index a1f4554726..f60d8c664c 100644 --- a/openpype/hosts/houdini/plugins/publish/collect_vray_rop.py +++ b/openpype/hosts/houdini/plugins/publish/collect_vray_rop.py @@ -67,7 +67,7 @@ class CollectVrayROPRenderProducts(pyblish.api.InstancePlugin): beauty_product = self.get_render_product_name(default_prefix) render_products.append(beauty_product) files_by_aov = { - "RGB Color": self.generate_expected_files(instance, + "": self.generate_expected_files(instance, beauty_product)} if instance.data.get("RenderElement", True): @@ -75,7 +75,9 @@ class CollectVrayROPRenderProducts(pyblish.api.InstancePlugin): if render_element: for aov, renderpass in render_element.items(): render_products.append(renderpass) - files_by_aov[aov] = self.generate_expected_files(instance, renderpass) # noqa + files_by_aov[aov] = self.generate_expected_files( + instance, renderpass) + for product in render_products: self.log.debug("Found render product: %s" % product) From 0fc23ce2e7600c335bd05ccc642f742497a36282 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 21 Dec 2023 15:06:31 +0000 Subject: [PATCH 002/573] Changed logic for creation of output node --- openpype/hosts/blender/api/render_lib.py | 70 ++++++++++++++++-------- 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/openpype/hosts/blender/api/render_lib.py b/openpype/hosts/blender/api/render_lib.py index b437078ad8..cdccf13805 100644 --- a/openpype/hosts/blender/api/render_lib.py +++ b/openpype/hosts/blender/api/render_lib.py @@ -135,71 +135,95 @@ def set_render_passes(settings): return aov_list, custom_passes -def set_node_tree(output_path, name, aov_sep, ext, multilayer): +def set_node_tree(output_path, render_product, name, aov_sep, ext, multilayer): # Set the scene to use the compositor node tree to render bpy.context.scene.use_nodes = True tree = bpy.context.scene.node_tree + comp_layer_type = "CompositorNodeRLayers" + output_type = "CompositorNodeOutputFile" + # Get the Render Layers node rl_node = None for node in tree.nodes: - if node.bl_idname == "CompositorNodeRLayers": + if node.bl_idname == comp_layer_type: rl_node = node break # If there's not a Render Layers node, we create it if not rl_node: - rl_node = tree.nodes.new("CompositorNodeRLayers") + rl_node = tree.nodes.new(comp_layer_type) # Get the enabled output sockets, that are the active passes for the # render. # We also exclude some layers. - exclude_sockets = ["Image", "Alpha", "Noisy Image"] + exclude_sockets = ["Alpha", "Noisy Image"] passes = [ socket for socket in rl_node.outputs if socket.enabled and socket.name not in exclude_sockets ] - # Remove all output nodes + old_output = None + + # Remove all output nodes that inlcude "AYON" in the name. + # There should be only one. for node in tree.nodes: - if node.bl_idname == "CompositorNodeOutputFile": - tree.nodes.remove(node) + if node.bl_idname == output_type and "AYON" in node.name: + old_output = node + break # Create a new output node - output = tree.nodes.new("CompositorNodeOutputFile") + output = tree.nodes.new(output_type) image_settings = bpy.context.scene.render.image_settings output.format.file_format = image_settings.file_format + slots = None + # In case of a multilayer exr, we don't need to use the output node, # because the blender render already outputs a multilayer exr. - if ext == "exr" and multilayer: - output.layer_slots.clear() - return [] + multi_exr = ext == "exr" and multilayer + slots = output.layer_slots if multi_exr else output.file_slots + output.base_path = render_product if multi_exr else str(output_path) - output.file_slots.clear() - output.base_path = str(output_path) + slots.clear() aov_file_products = [] # For each active render pass, we add a new socket to the output node # and link it - for render_pass in passes: - filepath = f"{name}{aov_sep}{render_pass.name}.####" + for rpass in passes: + if rpass.name == "Image": + pass_name = "rgba" if multi_exr else "beauty" + else: + pass_name = rpass.name + filepath = f"{name}{aov_sep}{pass_name}.####" - output.file_slots.new(filepath) + slots.new(pass_name if multi_exr else filepath) filename = str(output_path / filepath.lstrip("/")) - aov_file_products.append((render_pass.name, filename)) + aov_file_products.append((rpass.name, filename)) - node_input = output.inputs[-1] + if not old_output: + node_input = output.inputs[-1] + tree.links.new(rpass, node_input) - tree.links.new(render_pass, node_input) + for link in tree.links: + if link.to_node == old_output: + socket_name = link.to_socket.name + new_socket = output.inputs.get(socket_name) + if new_socket: + tree.links.new(link.from_socket, new_socket) - return aov_file_products + if old_output: + output.location = old_output.location + tree.nodes.remove(old_output) + output.name = "AYON File Output" + + return [] if multi_exr else aov_file_products def imprint_render_settings(node, data): @@ -236,9 +260,11 @@ def prepare_rendering(asset_group): render_product = get_render_product(output_path, name, aov_sep) aov_file_product = set_node_tree( - output_path, name, aov_sep, ext, multilayer) + output_path, render_product, name, aov_sep, ext, multilayer) - bpy.context.scene.render.filepath = render_product + # Clear the render filepath, so that the output is handled only by the + # output node in the compositor. + bpy.context.scene.render.filepath = "" render_settings = { "render_folder": render_folder, From fa5b9777a98cca34f5cd5412500f34825a6ff3d8 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 21 Dec 2023 15:07:00 +0000 Subject: [PATCH 003/573] Create a new version of the workfile instead of overwriting current one --- openpype/hosts/blender/plugins/create/create_render.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/blender/plugins/create/create_render.py b/openpype/hosts/blender/plugins/create/create_render.py index 7fb3e5eb00..e728579286 100644 --- a/openpype/hosts/blender/plugins/create/create_render.py +++ b/openpype/hosts/blender/plugins/create/create_render.py @@ -1,8 +1,10 @@ """Create render.""" import bpy +from openpype.lib import version_up from openpype.hosts.blender.api import plugin from openpype.hosts.blender.api.render_lib import prepare_rendering +from openpype.hosts.blender.api.workio import save_file class CreateRenderlayer(plugin.BaseCreator): @@ -37,6 +39,7 @@ class CreateRenderlayer(plugin.BaseCreator): # settings. Even the validator to check that the file is saved will # detect the file as saved, even if it isn't. The only solution for # now it is to force the file to be saved. - bpy.ops.wm.save_as_mainfile(filepath=bpy.data.filepath) + filepath = version_up(bpy.data.filepath) + save_file(filepath, copy=False) return collection From 04dfe1863f267b210f6a3dc57b8f9890957e8d3c Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 8 Jan 2024 20:46:27 +0800 Subject: [PATCH 004/573] Implement the validator for camera attributes in Max host --- openpype/hosts/max/api/action.py | 39 +++++++++++ .../publish/validate_camera_attributes.py | 59 +++++++++++++++++ openpype/pipeline/publish/lib.py | 2 +- .../defaults/project_settings/max.json | 10 +++ .../schemas/schema_max_publish.json | 66 +++++++++++++++++++ .../max/server/settings/publishers.py | 26 +++++++- server_addon/max/server/version.py | 2 +- 7 files changed, 201 insertions(+), 3 deletions(-) create mode 100644 openpype/hosts/max/api/action.py create mode 100644 openpype/hosts/max/plugins/publish/validate_camera_attributes.py diff --git a/openpype/hosts/max/api/action.py b/openpype/hosts/max/api/action.py new file mode 100644 index 0000000000..506847652b --- /dev/null +++ b/openpype/hosts/max/api/action.py @@ -0,0 +1,39 @@ +from pymxs import runtime as rt + +import pyblish.api + +from openpype.pipeline.publish import get_errored_instances_from_context + + +class SelectInvalidAction(pyblish.api.Action): + """Select invalid objects in Blender when a publish plug-in failed.""" + label = "Select Invalid" + on = "failed" + icon = "search" + + def process(self, context, plugin): + errored_instances = get_errored_instances_from_context(context, + plugin=plugin) + + # Get the invalid nodes for the plug-ins + self.log.info("Finding invalid nodes...") + invalid = list() + for instance in errored_instances: + invalid_nodes = plugin.get_invalid(instance) + if invalid_nodes: + if isinstance(invalid_nodes, (list, tuple)): + invalid.extend(invalid_nodes) + else: + self.log.warning( + "Failed plug-in doesn't have any selectable objects." + ) + + if not invalid: + self.log.info("No invalid nodes found.") + return + invalid_names = [obj.name for obj in invalid] + self.log.info( + "Selecting invalid objects: %s", ", ".join(invalid_names) + ) + + rt.Select(invalid) diff --git a/openpype/hosts/max/plugins/publish/validate_camera_attributes.py b/openpype/hosts/max/plugins/publish/validate_camera_attributes.py new file mode 100644 index 0000000000..e279b41475 --- /dev/null +++ b/openpype/hosts/max/plugins/publish/validate_camera_attributes.py @@ -0,0 +1,59 @@ +import pyblish.api +from pymxs import runtime as rt + +from openpype.pipeline.publish import ( + OptionalPyblishPluginMixin, + PublishValidationError +) +from openpype.hosts.max.api.action import SelectInvalidAction + + +class ValidateCameraAttributes(OptionalPyblishPluginMixin, + pyblish.api.InstancePlugin): + """Validates Camera has no invalid attribute properties + or values.(For 3dsMax Cameras only) + + """ + + order = pyblish.api.ValidatorOrder + families = ['camera'] + hosts = ['max'] + label = 'Camera Attributes' + actions = [SelectInvalidAction] + optional = True + + DEFAULTS = ["fov", "nearrange", "farrange", + "nearclip","farclip"] + CAM_TYPE = ["Freecamera", "Targetcamera", + "Physical"] + + @classmethod + def get_invalid(cls, instance): + invalid = [] + cameras = instance.data["members"] + project_settings = instance.context.data["project_settings"].get("max") + cam_attr_settings = project_settings["publish"]["ValidateCameraAttributes"] + for camera in cameras: + if str(rt.ClassOf(camera)) not in cls.CAM_TYPE: + cls.log.debug( + "Skipping camera created from external plugin..") + continue + for attr in cls.DEFAULTS: + default_value = cam_attr_settings.get(attr) + if rt.getProperty(camera, attr) != default_value: + cls.log.error( + f"Invalid attribute value: {attr} " + f"(should be: {default_value}))") + invalid.append(camera) + + return invalid + + def process(self, instance): + if not self.is_active(instance.data): + self.log.debug("Skipping Validate Camera Attributes...") + return + invalid = self.get_invalid(instance) + + if invalid: + raise PublishValidationError( + f"Invalid camera attributes: {invalid}") diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index 4ea2f932f1..87ca3323cb 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -74,7 +74,7 @@ def get_template_name_profiles( project_settings ["global"] ["publish"] - ["IntegrateAssetNew"] + ["IntegrateHeroVersion"] ["template_name_profiles"] ) if legacy_profiles: diff --git a/openpype/settings/defaults/project_settings/max.json b/openpype/settings/defaults/project_settings/max.json index d1610610dc..a0a4fcf83d 100644 --- a/openpype/settings/defaults/project_settings/max.json +++ b/openpype/settings/defaults/project_settings/max.json @@ -56,6 +56,16 @@ "enabled": false, "attributes": {} }, + "ValidateCameraAttributes": { + "enabled": true, + "optional": true, + "active": false, + "fov": 45.0, + "nearrange": 0.0, + "farrange": 1000.0, + "nearclip": 1.0, + "farclip": 1000.0 + }, "ValidateLoadedPlugin": { "enabled": false, "optional": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json index b4d85bda98..90a5d2fc18 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json @@ -48,6 +48,72 @@ } ] }, + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "ValidateCameraAttributes", + "label": "Validate Camera Attributes", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "boolean", + "key": "active", + "label": "Active" + }, + { + "type": "number", + "key": "fov", + "label": "Focal Length", + "decimal": 1, + "minimum": 0, + "maximum": 100.0 + }, + { + "type": "number", + "key": "nearrange", + "label": "Near Range", + "decimal": 1, + "minimum": 0, + "maximum": 100.0 + }, + { + "type": "number", + "key": "farrange", + "label": "Far Range", + "decimal": 1, + "minimum": 0, + "maximum": 2000.0 + }, + { + "type": "number", + "key": "nearclip", + "label": "Near Clip", + "decimal": 1, + "minimum": 0, + "maximum": 100.0 + }, + { + "type": "number", + "key": "farclip", + "label": "Far Clip", + "decimal": 1, + "minimum": 0, + "maximum": 2000.0 + } + ] + }, + { "type": "dict", "collapsible": true, diff --git a/server_addon/max/server/settings/publishers.py b/server_addon/max/server/settings/publishers.py index d40d85a99b..340f344a9f 100644 --- a/server_addon/max/server/settings/publishers.py +++ b/server_addon/max/server/settings/publishers.py @@ -27,6 +27,17 @@ class ValidateAttributesModel(BaseSettingsModel): return value +class ValidateCameraAttributesModel(BaseSettingsModel): + enabled: bool = Field(title="Enabled") + optional: bool = Field(title="Optional") + active: bool = Field(title="Active") + fov: float = Field(0.0, title="Focal Length") + nearrange: float = Field(0.0, title="Near Range") + farrange: float = Field(0.0, title="Far Range") + nearclip: float = Field(0.0, title="Near Clip") + farclip: float = Field(0.0, title="Far Clip") + + class FamilyMappingItemModel(BaseSettingsModel): product_types: list[str] = Field( default_factory=list, @@ -63,7 +74,10 @@ class PublishersModel(BaseSettingsModel): default_factory=ValidateAttributesModel, title="Validate Attributes" ) - + ValidateCameraAttributes: ValidateCameraAttributesModel = Field( + default_factory=ValidateCameraAttributesModel, + title="Validate Camera Attributes" + ) ValidateLoadedPlugin: ValidateLoadedPluginModel = Field( default_factory=ValidateLoadedPluginModel, title="Validate Loaded Plugin" @@ -101,6 +115,16 @@ DEFAULT_PUBLISH_SETTINGS = { "enabled": False, "attributes": "{}" }, + "ValidateCameraAttributes": { + "enabled": True, + "optional": True, + "active": False, + "fov": 45.0, + "nearrange": 0.0, + "farrange": 1000.0, + "nearclip": 1.0, + "farclip": 1000.0 + }, "ValidateLoadedPlugin": { "enabled": False, "optional": True, diff --git a/server_addon/max/server/version.py b/server_addon/max/server/version.py index bbab0242f6..1276d0254f 100644 --- a/server_addon/max/server/version.py +++ b/server_addon/max/server/version.py @@ -1 +1 @@ -__version__ = "0.1.4" +__version__ = "0.1.5" From c29a4fdfd64dccb22375749b766b9e01aca9d815 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 8 Jan 2024 21:08:51 +0800 Subject: [PATCH 005/573] hound shut --- .../hosts/max/plugins/publish/validate_camera_attributes.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_camera_attributes.py b/openpype/hosts/max/plugins/publish/validate_camera_attributes.py index e279b41475..d97d5529e3 100644 --- a/openpype/hosts/max/plugins/publish/validate_camera_attributes.py +++ b/openpype/hosts/max/plugins/publish/validate_camera_attributes.py @@ -23,7 +23,7 @@ class ValidateCameraAttributes(OptionalPyblishPluginMixin, optional = True DEFAULTS = ["fov", "nearrange", "farrange", - "nearclip","farclip"] + "nearclip", "farclip"] CAM_TYPE = ["Freecamera", "Targetcamera", "Physical"] @@ -32,7 +32,9 @@ class ValidateCameraAttributes(OptionalPyblishPluginMixin, invalid = [] cameras = instance.data["members"] project_settings = instance.context.data["project_settings"].get("max") - cam_attr_settings = project_settings["publish"]["ValidateCameraAttributes"] + cam_attr_settings = ( + project_settings["publish"]["ValidateCameraAttributes"] + ) for camera in cameras: if str(rt.ClassOf(camera)) not in cls.CAM_TYPE: cls.log.debug( From 3c878ccc3e34d8bb6884e731cc035881a0449947 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 11 Jan 2024 13:27:32 +0800 Subject: [PATCH 006/573] make sure the validator skips checking when the value of the attribute sets to zero --- .../max/plugins/publish/validate_camera_attributes.py | 11 ++++++++--- .../projects_schema/schemas/schema_max_publish.json | 4 ++++ server_addon/max/server/settings/publishers.py | 6 +++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_camera_attributes.py b/openpype/hosts/max/plugins/publish/validate_camera_attributes.py index d97d5529e3..36f0beab65 100644 --- a/openpype/hosts/max/plugins/publish/validate_camera_attributes.py +++ b/openpype/hosts/max/plugins/publish/validate_camera_attributes.py @@ -18,7 +18,7 @@ class ValidateCameraAttributes(OptionalPyblishPluginMixin, order = pyblish.api.ValidatorOrder families = ['camera'] hosts = ['max'] - label = 'Camera Attributes' + label = 'Validate Camera Attributes' actions = [SelectInvalidAction] optional = True @@ -42,9 +42,14 @@ class ValidateCameraAttributes(OptionalPyblishPluginMixin, continue for attr in cls.DEFAULTS: default_value = cam_attr_settings.get(attr) + if default_value == 0: + cls.log.debug( + f"the value of {attr} in setting set to" + " zero. Skipping the check..") + continue if rt.getProperty(camera, attr) != default_value: cls.log.error( - f"Invalid attribute value: {attr} " + f"Invalid attribute value for {camera.name}:{attr} " f"(should be: {default_value}))") invalid.append(camera) @@ -58,4 +63,4 @@ class ValidateCameraAttributes(OptionalPyblishPluginMixin, if invalid: raise PublishValidationError( - f"Invalid camera attributes: {invalid}") + "Invalid camera attributes found. See log.") diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json index 90a5d2fc18..1e7a7c0c73 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json @@ -79,6 +79,10 @@ "minimum": 0, "maximum": 100.0 }, + { + "type": "label", + "label": "If the value of the camera attributes set to 0, the system automatically skips checking it" + }, { "type": "number", "key": "nearrange", diff --git a/server_addon/max/server/settings/publishers.py b/server_addon/max/server/settings/publishers.py index 340f344a9f..a092f54a62 100644 --- a/server_addon/max/server/settings/publishers.py +++ b/server_addon/max/server/settings/publishers.py @@ -76,7 +76,11 @@ class PublishersModel(BaseSettingsModel): ) ValidateCameraAttributes: ValidateCameraAttributesModel = Field( default_factory=ValidateCameraAttributesModel, - title="Validate Camera Attributes" + title="Validate Camera Attributes", + description=( + "If the value of the camera attributes set to 0, " + "the system automatically skips checking it" + ) ) ValidateLoadedPlugin: ValidateLoadedPluginModel = Field( default_factory=ValidateLoadedPluginModel, From 9baff5eccfea5e2c7a5dc8c15efc4b75a916799f Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 11 Jan 2024 18:04:19 +0800 Subject: [PATCH 007/573] add repair actions for validate camera attributes --- .../publish/validate_camera_attributes.py | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_camera_attributes.py b/openpype/hosts/max/plugins/publish/validate_camera_attributes.py index 36f0beab65..94aa5742d8 100644 --- a/openpype/hosts/max/plugins/publish/validate_camera_attributes.py +++ b/openpype/hosts/max/plugins/publish/validate_camera_attributes.py @@ -2,6 +2,7 @@ import pyblish.api from pymxs import runtime as rt from openpype.pipeline.publish import ( + RepairAction, OptionalPyblishPluginMixin, PublishValidationError ) @@ -19,7 +20,7 @@ class ValidateCameraAttributes(OptionalPyblishPluginMixin, families = ['camera'] hosts = ['max'] label = 'Validate Camera Attributes' - actions = [SelectInvalidAction] + actions = [SelectInvalidAction, RepairAction] optional = True DEFAULTS = ["fov", "nearrange", "farrange", @@ -42,10 +43,11 @@ class ValidateCameraAttributes(OptionalPyblishPluginMixin, continue for attr in cls.DEFAULTS: default_value = cam_attr_settings.get(attr) - if default_value == 0: + cls.log.debug(f"default value: {default_value}") + if default_value == float(0): cls.log.debug( f"the value of {attr} in setting set to" - " zero. Skipping the check..") + " zero. Skipping the check.") continue if rt.getProperty(camera, attr) != default_value: cls.log.error( @@ -57,10 +59,26 @@ class ValidateCameraAttributes(OptionalPyblishPluginMixin, def process(self, instance): if not self.is_active(instance.data): - self.log.debug("Skipping Validate Camera Attributes...") + self.log.debug("Skipping Validate Camera Attributes.") return invalid = self.get_invalid(instance) if invalid: raise PublishValidationError( "Invalid camera attributes found. See log.") + + @classmethod + def repair(cls, instance): + invalid_cameras = cls.get_invalid(instance) + project_settings = instance.context.data["project_settings"].get("max") + cam_attr_settings = ( + project_settings["publish"]["ValidateCameraAttributes"] + ) + for camera in invalid_cameras: + for attr in cls.DEFAULTS: + expected_value = cam_attr_settings.get(attr) + if expected_value == float(0): + cls.log.debug( + f"the value of {attr} in setting set to zero.") + continue + rt.setProperty(camera, attr, expected_value) From 15167fa0b805b03657857097db52dd554c163620 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 11 Jan 2024 22:46:21 +0800 Subject: [PATCH 008/573] the validator posts on warning message if the users dont use generic types & round 1 decimal places for all check values --- .../max/plugins/publish/validate_camera_attributes.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_camera_attributes.py b/openpype/hosts/max/plugins/publish/validate_camera_attributes.py index 94aa5742d8..4eec1951e5 100644 --- a/openpype/hosts/max/plugins/publish/validate_camera_attributes.py +++ b/openpype/hosts/max/plugins/publish/validate_camera_attributes.py @@ -31,6 +31,11 @@ class ValidateCameraAttributes(OptionalPyblishPluginMixin, @classmethod def get_invalid(cls, instance): invalid = [] + if rt.units.DisplayType != rt.Name("Generic"): + cls.log.warning( + "Generic Type is not used as a scene unit\n\n" + "sure you tweak the settings with your own values\n\n" + "before validation.") cameras = instance.data["members"] project_settings = instance.context.data["project_settings"].get("max") cam_attr_settings = ( @@ -43,13 +48,12 @@ class ValidateCameraAttributes(OptionalPyblishPluginMixin, continue for attr in cls.DEFAULTS: default_value = cam_attr_settings.get(attr) - cls.log.debug(f"default value: {default_value}") if default_value == float(0): cls.log.debug( f"the value of {attr} in setting set to" " zero. Skipping the check.") continue - if rt.getProperty(camera, attr) != default_value: + if round(rt.getProperty(camera, attr), 1) != default_value: cls.log.error( f"Invalid attribute value for {camera.name}:{attr} " f"(should be: {default_value}))") From efc1cc257258da3e024d2db2609f1dbc83288de9 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 16 Jan 2024 18:00:34 +0800 Subject: [PATCH 009/573] refactor the validate deadline publish and rename to validate render passes --- .../publish/validate_deadline_publish.py | 43 ------ .../plugins/publish/validate_renderpasses.py | 137 ++++++++++++++++++ .../defaults/project_settings/max.json | 5 + .../schemas/schema_max_publish.json | 25 ++++ .../max/server/settings/publishers.py | 9 ++ server_addon/max/server/version.py | 2 +- 6 files changed, 177 insertions(+), 44 deletions(-) delete mode 100644 openpype/hosts/max/plugins/publish/validate_deadline_publish.py create mode 100644 openpype/hosts/max/plugins/publish/validate_renderpasses.py diff --git a/openpype/hosts/max/plugins/publish/validate_deadline_publish.py b/openpype/hosts/max/plugins/publish/validate_deadline_publish.py deleted file mode 100644 index b2f0e863f4..0000000000 --- a/openpype/hosts/max/plugins/publish/validate_deadline_publish.py +++ /dev/null @@ -1,43 +0,0 @@ -import os -import pyblish.api -from pymxs import runtime as rt -from openpype.pipeline.publish import ( - RepairAction, - ValidateContentsOrder, - PublishValidationError, - OptionalPyblishPluginMixin -) -from openpype.hosts.max.api.lib_rendersettings import RenderSettings - - -class ValidateDeadlinePublish(pyblish.api.InstancePlugin, - OptionalPyblishPluginMixin): - """Validates Render File Directory is - not the same in every submission - """ - - order = ValidateContentsOrder - families = ["maxrender"] - hosts = ["max"] - label = "Render Output for Deadline" - optional = True - actions = [RepairAction] - - def process(self, instance): - if not self.is_active(instance.data): - return - file = rt.maxFileName - filename, ext = os.path.splitext(file) - if filename not in rt.rendOutputFilename: - raise PublishValidationError( - "Render output folder " - "doesn't match the max scene name! " - "Use Repair action to " - "fix the folder file path.." - ) - - @classmethod - def repair(cls, instance): - container = instance.data.get("instance_node") - RenderSettings().render_output(container) - cls.log.debug("Reset the render output folder...") diff --git a/openpype/hosts/max/plugins/publish/validate_renderpasses.py b/openpype/hosts/max/plugins/publish/validate_renderpasses.py new file mode 100644 index 0000000000..9e65f305a2 --- /dev/null +++ b/openpype/hosts/max/plugins/publish/validate_renderpasses.py @@ -0,0 +1,137 @@ +import os +import pyblish.api +from pymxs import runtime as rt +from openpype.pipeline.publish import ( + RepairAction, + ValidateContentsOrder, + PublishValidationError, + OptionalPyblishPluginMixin +) +from openpype.hosts.max.api.lib import get_current_renderer +from openpype.hosts.max.api.lib_rendersettings import RenderSettings + + +class ValidateRenderPasses(OptionalPyblishPluginMixin, + pyblish.api.InstancePlugin): + """Validates Render Passes before Deadline Submission + """ + + order = ValidateContentsOrder + families = ["maxrender"] + hosts = ["max"] + label = "Validate Render Passes" + optional = True + actions = [RepairAction] + + def process(self, instance): + if not self.is_active(instance.data): + return + invalid = self.get_invalid(instance) + if invalid: + bullet_point_invalid_statement = "\n".join( + f"- {err_type}: {filepath}" for err_type, filepath + in invalid + ) + report = ( + "Invalid render passes found.\n\n" + f"{bullet_point_invalid_statement}\n\n" + "You can use repair action to fix the invalid filepath." + ) + raise PublishValidationError( + report, title="Invalid Render Passes") + + @classmethod + def get_invalid(cls, instance): + """Function to get invalid beauty render outputs and + render elements + + Args: + instance (pyblish.api.Instance): instance + filename (str): filename of the Max scene + + Returns: + list: list of invalid filename which doesn't match + with the project name + """ + invalid = [] + file = rt.maxFileName + filename, ext = os.path.splitext(file) + if filename not in rt.rendOutputFilename: + cls.log.error( + "Render output folder " + "doesn't match the max scene name! " + ) + invalid_folder_name = os.path.dirname( + rt.rendOutputFilename).replace( + "\\", "/").split("/")[-1] + invalid.append(("Invalid Render Output Folder", + invalid_folder_name)) + beauty_fname = os.path.basename(rt.rendOutputFilename) + ext = os.path.splitext(beauty_fname)[-1].lstrip(".") + invalid_image_format = cls.get_invalid_image_format( + cls, instance, ext) + invalid.extend(invalid_image_format) + renderer_class = get_current_renderer() + renderer = str(renderer_class).split(":")[0] + if renderer in [ + "ART_Renderer", + "Redshift_Renderer", + "V_Ray_6_Hotfix_3", + "V_Ray_GPU_6_Hotfix_3", + "Default_Scanline_Renderer", + "Quicksilver_Hardware_Renderer", + ]: + render_elem = rt.maxOps.GetCurRenderElementMgr() + render_elem_num = render_elem.NumRenderElements() + for i in range(render_elem_num): + renderlayer_name = render_elem.GetRenderElement(i) + renderpass = str(renderlayer_name).split(":")[-1] + rend_file = render_elem.GetRenderElementFilename(i) + if not rend_file: + cls.log.error(f"No filepath for {renderpass}") + invalid.append((f"Invalid {renderpass}", + "No filepath")) + rend_fname, ext = os.path.splitext( + os.path.basename(rend_file)) + if not rend_fname.lstrip(".").endswith(renderpass): + err_msg = ( + f"Filename for {renderpass} should be " + f"ended with {renderpass}" + ) + cls.log.error(err_msg) + invalid.append((f"Invalid {renderpass}", + os.path.basename(rend_file))) + invalid_image_format = cls.get_invalid_image_format( + cls, instance, ext) + invalid.extend(invalid_image_format) + elif renderer == "Arnold": + cls.log.debug( + "Temporarily not support to check on Arnold render.") + + return invalid + + def get_invalid_image_format(self, instance, ext): + """Function to check if the image format of the render outputs + aligns with that in the setting. + + Args: + instance (pyblish.api.Instance): instance + ext (str): image extension + + Returns: + list: list of files with invalid image format + """ + invalid = [] + settings = instance.context.data["project_settings"].get("max") + image_format = settings["RenderSettings"]["image_format"] + if ext.lstrip(".") != image_format: + msg = "Invalid image format for render outputs" + self.log.error(msg) + invalid.append((msg, ext.lstrip("."))) + return invalid + + @classmethod + def repair(cls, instance): + container = instance.data.get("instance_node") + RenderSettings().render_output(container) + cls.log.debug("Reset the render output folder...") diff --git a/openpype/settings/defaults/project_settings/max.json b/openpype/settings/defaults/project_settings/max.json index d1610610dc..293515f1a2 100644 --- a/openpype/settings/defaults/project_settings/max.json +++ b/openpype/settings/defaults/project_settings/max.json @@ -61,6 +61,11 @@ "optional": true, "family_plugins_mapping": [] }, + "ValidateRenderPasses": { + "enabled": true, + "optional": true, + "active": true + }, "ExtractModelObj": { "enabled": true, "optional": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json index b4d85bda98..4dd3d519e8 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json @@ -88,6 +88,31 @@ } ] } + }, + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "ValidateRenderPasses", + "label": "Validate Render Passes", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "boolean", + "key": "active", + "label": "Active" + } + ] } ] }, diff --git a/server_addon/max/server/settings/publishers.py b/server_addon/max/server/settings/publishers.py index d40d85a99b..a10e72b36a 100644 --- a/server_addon/max/server/settings/publishers.py +++ b/server_addon/max/server/settings/publishers.py @@ -68,6 +68,10 @@ class PublishersModel(BaseSettingsModel): default_factory=ValidateLoadedPluginModel, title="Validate Loaded Plugin" ) + ValidateRenderPasses: BasicValidateModel = Field( + default_factory=BasicValidateModel, + title="Validate Render Passes" + ) ExtractModelObj: BasicValidateModel = Field( default_factory=BasicValidateModel, title="Extract OBJ", @@ -106,6 +110,11 @@ DEFAULT_PUBLISH_SETTINGS = { "optional": True, "family_plugins_mapping": [] }, + "ValidateRenderPasses": { + "enabled": True, + "optional": True, + "active": True + }, "ExtractModelObj": { "enabled": True, "optional": True, diff --git a/server_addon/max/server/version.py b/server_addon/max/server/version.py index bbab0242f6..1276d0254f 100644 --- a/server_addon/max/server/version.py +++ b/server_addon/max/server/version.py @@ -1 +1 @@ -__version__ = "0.1.4" +__version__ = "0.1.5" From f0b8f254a113b51e96bfbc7f70ed1d440684412c Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 16 Jan 2024 18:33:37 +0800 Subject: [PATCH 010/573] add docstrings & fix the bug on the setting of validate render pass in legacy OP --- .../plugins/publish/validate_renderpasses.py | 21 ++++++++- .../schemas/schema_max_publish.json | 46 +++++++++---------- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_renderpasses.py b/openpype/hosts/max/plugins/publish/validate_renderpasses.py index 9e65f305a2..f1bba81ecc 100644 --- a/openpype/hosts/max/plugins/publish/validate_renderpasses.py +++ b/openpype/hosts/max/plugins/publish/validate_renderpasses.py @@ -43,7 +43,26 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, @classmethod def get_invalid(cls, instance): """Function to get invalid beauty render outputs and - render elements + render elements. + + 1. Checking Render Output Folder matches the name of + the current Max Scene + e.g. The name of the current Max scene: + John_Doe.max + The expected render output directory: + {root[work]}/{project[name]}/{hierarchy} + /{asset}/work/{task[name]}/render/3dsmax/John_Doe/ + + 2. Checking the image extension(s) of the render output(s) + does not match with image format in OP/AYON setting. + e.g. The current image format in the setting: png + The expected render outputs: Joe_Doe.png + + 3. Checking the filename of render element is not ended + with the name of render element from the 3dsMax Render + Element Manager. + e.g. The name of render element: RsCryptomatte + The expected filename: {InstanceName}_RsCryptomatte.png Args: instance (pyblish.api.Instance): instance diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json index 4dd3d519e8..d0da4029f5 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json @@ -88,31 +88,31 @@ } ] } + } + ] + }, + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "ValidateRenderPasses", + "label": "Validate Render Passes", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" }, { - "type": "dict", - "collapsible": true, - "checkbox_key": "enabled", - "key": "ValidateRenderPasses", - "label": "Validate Render Passes", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "optional", - "label": "Optional" - }, - { - "type": "boolean", - "key": "active", - "label": "Active" - } - ] + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "boolean", + "key": "active", + "label": "Active" } ] }, From 2590648f93e8f99e82ff0f6fcdd1af8abe134eb6 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 16 Jan 2024 19:27:01 +0800 Subject: [PATCH 011/573] big roy's comment - docstring tweaks and some log message tweaks --- .../plugins/publish/validate_renderpasses.py | 55 ++++++++++--------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_renderpasses.py b/openpype/hosts/max/plugins/publish/validate_renderpasses.py index f1bba81ecc..8472b56336 100644 --- a/openpype/hosts/max/plugins/publish/validate_renderpasses.py +++ b/openpype/hosts/max/plugins/publish/validate_renderpasses.py @@ -7,7 +7,6 @@ from openpype.pipeline.publish import ( PublishValidationError, OptionalPyblishPluginMixin ) -from openpype.hosts.max.api.lib import get_current_renderer from openpype.hosts.max.api.lib_rendersettings import RenderSettings @@ -45,23 +44,22 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, """Function to get invalid beauty render outputs and render elements. - 1. Checking Render Output Folder matches the name of - the current Max Scene - e.g. The name of the current Max scene: - John_Doe.max - The expected render output directory: - {root[work]}/{project[name]}/{hierarchy} - /{asset}/work/{task[name]}/render/3dsmax/John_Doe/ + 1. Check Render Output Folder matches the name of + the current Max Scene, e.g. + The name of the current Max scene: + John_Doe.max + The expected render output directory: + {root[work]}/{project[name]}/{hierarchy}/{asset}/ + work/{task[name]}/render/3dsmax/John_Doe/ - 2. Checking the image extension(s) of the render output(s) - does not match with image format in OP/AYON setting. - e.g. The current image format in the setting: png - The expected render outputs: Joe_Doe.png + 2. Check image extension(s) of the render output(s) + matches the image format in OP/AYON setting, e.g. + The current image format in settings: png + The expected render outputs: John_Doe.png - 3. Checking the filename of render element is not ended - with the name of render element from the 3dsMax Render - Element Manager. - e.g. The name of render element: RsCryptomatte + 3. Check filename of render element ends with the name of + render element from the 3dsMax Render Element Manager. + e.g. The name of render element: RsCryptomatte The expected filename: {InstanceName}_RsCryptomatte.png Args: @@ -90,8 +88,7 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, invalid_image_format = cls.get_invalid_image_format( cls, instance, ext) invalid.extend(invalid_image_format) - renderer_class = get_current_renderer() - renderer = str(renderer_class).split(":")[0] + renderer = instance.data["renderer"] if renderer in [ "ART_Renderer", "Redshift_Renderer", @@ -107,17 +104,17 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, renderpass = str(renderlayer_name).split(":")[-1] rend_file = render_elem.GetRenderElementFilename(i) if not rend_file: - cls.log.error(f"No filepath for {renderpass}") + cls.log.error( + f"No filepath for render element {renderpass}") invalid.append((f"Invalid {renderpass}", "No filepath")) rend_fname, ext = os.path.splitext( os.path.basename(rend_file)) if not rend_fname.lstrip(".").endswith(renderpass): - err_msg = ( - f"Filename for {renderpass} should be " - f"ended with {renderpass}" + cls.log.error( + f"Filename for {renderpass} should " + f"end with {renderpass}" ) - cls.log.error(err_msg) invalid.append((f"Invalid {renderpass}", os.path.basename(rend_file))) invalid_image_format = cls.get_invalid_image_format( @@ -125,7 +122,8 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, invalid.extend(invalid_image_format) elif renderer == "Arnold": cls.log.debug( - "Temporarily not support to check on Arnold render.") + "Renderpass validation not supported Arnold yet," + " validation skipped...") return invalid @@ -143,10 +141,13 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, invalid = [] settings = instance.context.data["project_settings"].get("max") image_format = settings["RenderSettings"]["image_format"] - if ext.lstrip(".") != image_format: - msg = "Invalid image format for render outputs" + ext = ext.lstrip(".") + if ext != image_format: + msg = ( + f"Invalid image format {ext} for render outputs" + f"Should be : {image_format}") self.log.error(msg) - invalid.append((msg, ext.lstrip("."))) + invalid.append((msg, ext)) return invalid @classmethod From 38c6683756b4dbfd3dd9e276cbcdcc9070a91296 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 16 Jan 2024 20:13:12 +0800 Subject: [PATCH 012/573] codes tweaks on log message if it errors out --- .../max/plugins/publish/validate_renderpasses.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_renderpasses.py b/openpype/hosts/max/plugins/publish/validate_renderpasses.py index 8472b56336..2ad8f5601a 100644 --- a/openpype/hosts/max/plugins/publish/validate_renderpasses.py +++ b/openpype/hosts/max/plugins/publish/validate_renderpasses.py @@ -76,7 +76,7 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, if filename not in rt.rendOutputFilename: cls.log.error( "Render output folder " - "doesn't match the max scene name! " + f"doesn't match the max scene name {filename} " ) invalid_folder_name = os.path.dirname( rt.rendOutputFilename).replace( @@ -85,8 +85,7 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, invalid_folder_name)) beauty_fname = os.path.basename(rt.rendOutputFilename) ext = os.path.splitext(beauty_fname)[-1].lstrip(".") - invalid_image_format = cls.get_invalid_image_format( - cls, instance, ext) + invalid_image_format = cls.get_invalid_image_format(instance, ext) invalid.extend(invalid_image_format) renderer = instance.data["renderer"] if renderer in [ @@ -117,8 +116,7 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, ) invalid.append((f"Invalid {renderpass}", os.path.basename(rend_file))) - invalid_image_format = cls.get_invalid_image_format( - cls, instance, ext) + invalid_image_format = cls.get_invalid_image_format(instance, ext) invalid.extend(invalid_image_format) elif renderer == "Arnold": cls.log.debug( @@ -127,7 +125,8 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, return invalid - def get_invalid_image_format(self, instance, ext): + @classmethod + def get_invalid_image_format(cls, instance, ext): """Function to check if the image format of the render outputs aligns with that in the setting. @@ -146,12 +145,13 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, msg = ( f"Invalid image format {ext} for render outputs" f"Should be : {image_format}") - self.log.error(msg) + cls.log.error(msg) invalid.append((msg, ext)) return invalid @classmethod def repair(cls, instance): container = instance.data.get("instance_node") + # TODO: need to rename the function of render_output RenderSettings().render_output(container) - cls.log.debug("Reset the render output folder...") + cls.log.debug("Finished repairing the render output folder and filenames.") From 204504b6406c14a3cec118756ba7d530f6c3366c Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 16 Jan 2024 20:15:05 +0800 Subject: [PATCH 013/573] hound shut --- openpype/hosts/max/plugins/publish/validate_renderpasses.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_renderpasses.py b/openpype/hosts/max/plugins/publish/validate_renderpasses.py index 2ad8f5601a..9b3064c393 100644 --- a/openpype/hosts/max/plugins/publish/validate_renderpasses.py +++ b/openpype/hosts/max/plugins/publish/validate_renderpasses.py @@ -116,7 +116,8 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, ) invalid.append((f"Invalid {renderpass}", os.path.basename(rend_file))) - invalid_image_format = cls.get_invalid_image_format(instance, ext) + invalid_image_format = cls.get_invalid_image_format( + instance, ext) invalid.extend(invalid_image_format) elif renderer == "Arnold": cls.log.debug( @@ -154,4 +155,5 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, container = instance.data.get("instance_node") # TODO: need to rename the function of render_output RenderSettings().render_output(container) - cls.log.debug("Finished repairing the render output folder and filenames.") + cls.log.debug("Finished repairing the render output " + "folder and filenames.") From bb6155738398f2e71c27f4012b72601d7ab3d911 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 17 Jan 2024 20:13:28 +0800 Subject: [PATCH 014/573] Debug message code tweaks --- openpype/hosts/max/plugins/publish/validate_renderpasses.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_renderpasses.py b/openpype/hosts/max/plugins/publish/validate_renderpasses.py index 9b3064c393..7b85763c74 100644 --- a/openpype/hosts/max/plugins/publish/validate_renderpasses.py +++ b/openpype/hosts/max/plugins/publish/validate_renderpasses.py @@ -144,8 +144,8 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, ext = ext.lstrip(".") if ext != image_format: msg = ( - f"Invalid image format {ext} for render outputs" - f"Should be : {image_format}") + f"Invalid image format {ext} for render outputs.\n" + f"Should be: {image_format}") cls.log.error(msg) invalid.append((msg, ext)) return invalid From 0534c9c55d9eb5d0f193b2c64c3a870b6082a092 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 18 Jan 2024 10:13:24 +0000 Subject: [PATCH 015/573] Fix error when getting setting value in Ayon --- openpype/hosts/blender/api/render_lib.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/blender/api/render_lib.py b/openpype/hosts/blender/api/render_lib.py index cdccf13805..904cabfc05 100644 --- a/openpype/hosts/blender/api/render_lib.py +++ b/openpype/hosts/blender/api/render_lib.py @@ -2,6 +2,7 @@ from pathlib import Path import bpy +from openpype import AYON_SERVER_ENABLED from openpype.settings import get_project_settings from openpype.pipeline import get_current_project_name @@ -124,13 +125,14 @@ def set_render_passes(settings): aovs_names = [aov.name for aov in vl.aovs] for cp in custom_passes: - cp_name = cp[0] + cp_name = cp["attribute"] if AYON_SERVER_ENABLED else cp[0] if cp_name not in aovs_names: aov = vl.aovs.add() aov.name = cp_name else: aov = vl.aovs[cp_name] - aov.type = cp[1].get("type", "VALUE") + aov.type = (cp["value"] + if AYON_SERVER_ENABLED else cp[1].get("type", "VALUE")) return aov_list, custom_passes From 1eb07bcedb97edef9bbf57fa7c605bf85794a33f Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 18 Jan 2024 10:16:05 +0000 Subject: [PATCH 016/573] Added setting to choose render engine --- openpype/hosts/blender/api/render_lib.py | 11 ++++++++++- .../blender/server/settings/render_settings.py | 13 +++++++++++++ server_addon/blender/server/version.py | 2 +- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/blender/api/render_lib.py b/openpype/hosts/blender/api/render_lib.py index 904cabfc05..06353f8e8b 100644 --- a/openpype/hosts/blender/api/render_lib.py +++ b/openpype/hosts/blender/api/render_lib.py @@ -48,6 +48,14 @@ def get_multilayer(settings): ["multilayer_exr"]) +def get_renderer(settings): + """Get renderer from blender settings.""" + + return (settings["blender"] + ["RenderSettings"] + ["renderer"]) + + def get_render_product(output_path, name, aov_sep): """ Generate the path to the render product. Blender interprets the `#` @@ -254,9 +262,10 @@ def prepare_rendering(asset_group): aov_sep = get_aov_separator(settings) ext = get_image_format(settings) multilayer = get_multilayer(settings) + renderer = get_renderer(settings) set_render_format(ext, multilayer) - aov_list, custom_passes = set_render_passes(settings) + bpy.context.scene.render.engine = renderer output_path = Path.joinpath(dirpath, render_folder, file_name) diff --git a/server_addon/blender/server/settings/render_settings.py b/server_addon/blender/server/settings/render_settings.py index f62013982e..53cefd145d 100644 --- a/server_addon/blender/server/settings/render_settings.py +++ b/server_addon/blender/server/settings/render_settings.py @@ -25,6 +25,13 @@ def image_format_enum(): ] +def renderers_enum(): + return [ + {"value": "CYCLES", "label": "Cycles"}, + {"value": "BLENDER_EEVEE", "label": "Eevee"}, + ] + + def aov_list_enum(): return [ {"value": "empty", "label": "< none >"}, @@ -83,6 +90,11 @@ class RenderSettingsModel(BaseSettingsModel): multilayer_exr: bool = Field( title="Multilayer (EXR)" ) + renderer: str = Field( + "CYCLES", + title="Renderer", + enum_resolver=renderers_enum + ) aov_list: list[str] = Field( default_factory=list, enum_resolver=aov_list_enum, @@ -104,6 +116,7 @@ DEFAULT_RENDER_SETTINGS = { "aov_separator": "underscore", "image_format": "exr", "multilayer_exr": True, + "renderer": "CYCLES", "aov_list": [], "custom_passes": [] } diff --git a/server_addon/blender/server/version.py b/server_addon/blender/server/version.py index 1276d0254f..0a8da88258 100644 --- a/server_addon/blender/server/version.py +++ b/server_addon/blender/server/version.py @@ -1 +1 @@ -__version__ = "0.1.5" +__version__ = "0.1.6" From 2f58708f584a63a68ffcbe2c429704dca566f43b Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 18 Jan 2024 10:16:31 +0000 Subject: [PATCH 017/573] Updated aov list --- openpype/hosts/blender/api/render_lib.py | 62 ++++++++++++++----- .../server/settings/render_settings.py | 50 ++++++++++++--- 2 files changed, 90 insertions(+), 22 deletions(-) diff --git a/openpype/hosts/blender/api/render_lib.py b/openpype/hosts/blender/api/render_lib.py index 06353f8e8b..44ee2be208 100644 --- a/openpype/hosts/blender/api/render_lib.py +++ b/openpype/hosts/blender/api/render_lib.py @@ -100,36 +100,69 @@ def set_render_format(ext, multilayer): image_settings.file_format = "TIFF" -def set_render_passes(settings): - aov_list = (settings["blender"] - ["RenderSettings"] - ["aov_list"]) - - custom_passes = (settings["blender"] - ["RenderSettings"] - ["custom_passes"]) +def set_render_passes(settings, renderer): + aov_list = settings["blender"]["RenderSettings"]["aov_list"] + custom_passes = settings["blender"]["RenderSettings"]["custom_passes"] + # Common passes for both renderers vl = bpy.context.view_layer + # Data Passes vl.use_pass_combined = "combined" in aov_list vl.use_pass_z = "z" in aov_list vl.use_pass_mist = "mist" in aov_list vl.use_pass_normal = "normal" in aov_list + + # Light Passes vl.use_pass_diffuse_direct = "diffuse_light" in aov_list vl.use_pass_diffuse_color = "diffuse_color" in aov_list vl.use_pass_glossy_direct = "specular_light" in aov_list vl.use_pass_glossy_color = "specular_color" in aov_list - vl.eevee.use_pass_volume_direct = "volume_light" in aov_list vl.use_pass_emit = "emission" in aov_list vl.use_pass_environment = "environment" in aov_list - vl.use_pass_shadow = "shadow" in aov_list vl.use_pass_ambient_occlusion = "ao" in aov_list - cycles = vl.cycles + # Cryptomatte Passes + vl.use_pass_cryptomatte_object = "cryptomatte_object" in aov_list + vl.use_pass_cryptomatte_material = "cryptomatte_material" in aov_list + vl.use_pass_cryptomatte_asset = "cryptomatte_asset" in aov_list - cycles.denoising_store_passes = "denoising" in aov_list - cycles.use_pass_volume_direct = "volume_direct" in aov_list - cycles.use_pass_volume_indirect = "volume_indirect" in aov_list + if renderer == "BLENDER_EEVEE": + # Eevee exclusive passes + eevee = vl.eevee + + # Light Passes + vl.use_pass_shadow = "shadow" in aov_list + eevee.use_pass_volume_direct = "volume_light" in aov_list + + # Effects Passes + eevee.use_pass_bloom = "bloom" in aov_list + eevee.use_pass_transparent = "transparent" in aov_list + + # Cryptomatte Passes + vl.use_pass_cryptomatte_accurate = "cryptomatte_accurate" in aov_list + elif renderer == "CYCLES": + # Cycles exclusive passes + cycles = vl.cycles + + # Data Passes + vl.use_pass_position = "position" in aov_list + vl.use_pass_vector = "vector" in aov_list + vl.use_pass_uv = "uv" in aov_list + cycles.denoising_store_passes = "denoising" in aov_list + vl.use_pass_object_index = "object_index" in aov_list + vl.use_pass_material_index = "material_index" in aov_list + cycles.pass_debug_sample_count = "sample_count" in aov_list + + # Light Passes + vl.use_pass_diffuse_indirect = "diffuse_indirect" in aov_list + vl.use_pass_glossy_indirect = "specular_indirect" in aov_list + vl.use_pass_transmission_direct = "transmission_direct" in aov_list + vl.use_pass_transmission_indirect = "transmission_indirect" in aov_list + vl.use_pass_transmission_color = "transmission_color" in aov_list + cycles.use_pass_volume_direct = "volume_light" in aov_list + cycles.use_pass_volume_indirect = "volume_indirect" in aov_list + cycles.use_pass_shadow_catcher = "shadow" in aov_list aovs_names = [aov.name for aov in vl.aovs] for cp in custom_passes: @@ -266,6 +299,7 @@ def prepare_rendering(asset_group): set_render_format(ext, multilayer) bpy.context.scene.render.engine = renderer + aov_list, custom_passes = set_render_passes(settings, renderer) output_path = Path.joinpath(dirpath, render_folder, file_name) diff --git a/server_addon/blender/server/settings/render_settings.py b/server_addon/blender/server/settings/render_settings.py index 53cefd145d..3ab720fc6a 100644 --- a/server_addon/blender/server/settings/render_settings.py +++ b/server_addon/blender/server/settings/render_settings.py @@ -39,18 +39,52 @@ def aov_list_enum(): {"value": "z", "label": "Z"}, {"value": "mist", "label": "Mist"}, {"value": "normal", "label": "Normal"}, - {"value": "diffuse_light", "label": "Diffuse Light"}, + {"value": "position", "label": "Position (Cycles Only)"}, + {"value": "vector", "label": "Vector (Cycles Only)"}, + {"value": "uv", "label": "UV (Cycles Only)"}, + {"value": "denoising", "label": "Denoising Data (Cycles Only)"}, + {"value": "object_index", "label": "Object Index (Cycles Only)"}, + {"value": "material_index", "label": "Material Index (Cycles Only)"}, + {"value": "sample_count", "label": "Sample Count (Cycles Only)"}, + {"value": "diffuse_light", "label": "Diffuse Light/Direct"}, + { + "value": "diffuse_indirect", + "label": "Diffuse Indirect (Cycles Only)" + }, {"value": "diffuse_color", "label": "Diffuse Color"}, - {"value": "specular_light", "label": "Specular Light"}, - {"value": "specular_color", "label": "Specular Color"}, - {"value": "volume_light", "label": "Volume Light"}, + {"value": "specular_light", "label": "Specular (Glossy) Light/Direct"}, + { + "value": "specular_indirect", + "label": "Specular (Glossy) Indirect (Cycles Only)" + }, + {"value": "specular_color", "label": "Specular (Glossy) Color"}, + { + "value": "transmission_light", + "label": "Transmission Light/Direct (Cycles Only)" + }, + { + "value": "transmission_indirect", + "label": "Transmission Indirect (Cycles Only)" + }, + { + "value": "transmission_color", + "label": "Transmission Color (Cycles Only)" + }, + {"value": "volume_light", "label": "Volume Light/Direct"}, + {"value": "volume_indirect", "label": "Volume Indirect (Cycles Only)"}, {"value": "emission", "label": "Emission"}, {"value": "environment", "label": "Environment"}, - {"value": "shadow", "label": "Shadow"}, + {"value": "shadow", "label": "Shadow/Shadow Catcher"}, {"value": "ao", "label": "Ambient Occlusion"}, - {"value": "denoising", "label": "Denoising"}, - {"value": "volume_direct", "label": "Direct Volumetric Scattering"}, - {"value": "volume_indirect", "label": "Indirect Volumetric Scattering"} + {"value": "bloom", "label": "Bloom (Eevee Only)"}, + {"value": "transparent", "label": "Transparent (Eevee Only)"}, + {"value": "cryptomatte_object", "label": "Cryptomatte Object"}, + {"value": "cryptomatte_material", "label": "Cryptomatte Material"}, + {"value": "cryptomatte_asset", "label": "Cryptomatte Asset"}, + { + "value": "cryptomatte_accurate", + "label": "Cryptomatte Accurate Mode (Eevee Only)" + }, ] From 91f67ca9b0a199e110650fd899d562eae3cdcd16 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Fri, 19 Jan 2024 10:00:58 +0000 Subject: [PATCH 018/573] Updated default settings for render passes --- server_addon/blender/server/settings/render_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/blender/server/settings/render_settings.py b/server_addon/blender/server/settings/render_settings.py index 3ab720fc6a..580547e510 100644 --- a/server_addon/blender/server/settings/render_settings.py +++ b/server_addon/blender/server/settings/render_settings.py @@ -151,6 +151,6 @@ DEFAULT_RENDER_SETTINGS = { "image_format": "exr", "multilayer_exr": True, "renderer": "CYCLES", - "aov_list": [], + "aov_list": ["combined"], "custom_passes": [] } From dd0533ecb36b19988ccb67b0b2e83a50f7090be1 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Fri, 19 Jan 2024 10:03:05 +0000 Subject: [PATCH 019/573] Added composite output --- openpype/hosts/blender/api/render_lib.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/blender/api/render_lib.py b/openpype/hosts/blender/api/render_lib.py index 44ee2be208..fc47f5a659 100644 --- a/openpype/hosts/blender/api/render_lib.py +++ b/openpype/hosts/blender/api/render_lib.py @@ -186,12 +186,17 @@ def set_node_tree(output_path, render_product, name, aov_sep, ext, multilayer): comp_layer_type = "CompositorNodeRLayers" output_type = "CompositorNodeOutputFile" + compositor_type = "CompositorNodeComposite" - # Get the Render Layers node + # Get the Render Layer and Composite nodes rl_node = None + comp_node = None for node in tree.nodes: if node.bl_idname == comp_layer_type: rl_node = node + elif node.bl_idname == compositor_type: + comp_node = node + if rl_node and comp_node: break # If there's not a Render Layers node, we create it @@ -242,29 +247,38 @@ def set_node_tree(output_path, render_product, name, aov_sep, ext, multilayer): pass_name = "rgba" if multi_exr else "beauty" else: pass_name = rpass.name - filepath = f"{name}{aov_sep}{pass_name}.####" + filename = f"{name}{aov_sep}{pass_name}.####" - slots.new(pass_name if multi_exr else filepath) + slots.new(pass_name if multi_exr else filename) - filename = str(output_path / filepath.lstrip("/")) + filepath = str(output_path / filename.lstrip("/")) - aov_file_products.append((rpass.name, filename)) + aov_file_products.append((rpass.name, filepath)) if not old_output: node_input = output.inputs[-1] tree.links.new(rpass, node_input) + # Create a new socket for the composite output + pass_name = "composite" + filename = f"{name}{aov_sep}{pass_name}.####" + comp_socket = slots.new(pass_name if multi_exr else filename) + aov_file_products.append(("Composite", filepath)) + for link in tree.links: if link.to_node == old_output: socket_name = link.to_socket.name new_socket = output.inputs.get(socket_name) if new_socket: tree.links.new(link.from_socket, new_socket) + elif comp_node and link.to_node == comp_node: + tree.links.new(link.from_socket, comp_socket) if old_output: output.location = old_output.location tree.nodes.remove(old_output) output.name = "AYON File Output" + output.label = "AYON File Output" return [] if multi_exr else aov_file_products From c911c67dae109a3c769fa4fe584f925f3afe7a2e Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Fri, 19 Jan 2024 11:04:25 +0000 Subject: [PATCH 020/573] Updated OpenPype Settings --- .../defaults/project_settings/blender.json | 3 +- .../schema_project_blender.json | 43 +++++++++++++++---- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/openpype/settings/defaults/project_settings/blender.json b/openpype/settings/defaults/project_settings/blender.json index 385e97ef91..48f3ef8ef0 100644 --- a/openpype/settings/defaults/project_settings/blender.json +++ b/openpype/settings/defaults/project_settings/blender.json @@ -22,7 +22,8 @@ "aov_separator": "underscore", "image_format": "exr", "multilayer_exr": true, - "aov_list": [], + "renderer": "CYCLES", + "aov_list": ["combined"], "custom_passes": [] }, "workfile_builder": { diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_blender.json b/openpype/settings/entities/schemas/projects_schema/schema_project_blender.json index 535d9434a3..bbed881ab0 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_blender.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_blender.json @@ -103,6 +103,17 @@ "type": "label", "label": "Note: Multilayer EXR is only used when output format type set to EXR." }, + { + "key": "renderer", + "label": "Renderer", + "type": "enum", + "multiselection": false, + "defaults": "CYCLES", + "enum_items": [ + {"CYCLES": "Cycles"}, + {"BLENDER_EEVEE": "Eevee"} + ] + }, { "key": "aov_list", "label": "AOVs to create", @@ -115,18 +126,34 @@ {"z": "Z"}, {"mist": "Mist"}, {"normal": "Normal"}, - {"diffuse_light": "Diffuse Light"}, + {"position": "Position (Cycles Only)"}, + {"vector": "Vector (Cycles Only)"}, + {"uv": "UV (Cycles Only)"}, + {"denoising": "Denoising Data (Cycles Only)"}, + {"object_index": "Object Index (Cycles Only)"}, + {"material_index": "Material Index (Cycles Only)"}, + {"sample_count": "Sample Count (Cycles Only)"}, + {"diffuse_light": "Diffuse Light/Direct"}, + {"diffuse_indirect": "Diffuse Indirect (Cycles Only)"}, {"diffuse_color": "Diffuse Color"}, - {"specular_light": "Specular Light"}, - {"specular_color": "Specular Color"}, - {"volume_light": "Volume Light"}, + {"specular_light": "Specular (Glossy) Light/Direct"}, + {"specular_indirect": "Specular (Glossy) Indirect (Cycles Only)"}, + {"specular_color": "Specular (Glossy) Color"}, + {"transmission_light": "Transmission Light/Direct (Cycles Only)"}, + {"transmission_indirect": "Transmission Indirect (Cycles Only)"}, + {"transmission_color": "Transmission Color (Cycles Only)"}, + {"volume_light": "Volume Light/Direct"}, + {"volume_indirect": "Volume Indirect (Cycles Only)"}, {"emission": "Emission"}, {"environment": "Environment"}, - {"shadow": "Shadow"}, + {"shadow": "Shadow/Shadow Catcher"}, {"ao": "Ambient Occlusion"}, - {"denoising": "Denoising"}, - {"volume_direct": "Direct Volumetric Scattering"}, - {"volume_indirect": "Indirect Volumetric Scattering"} + {"bloom": "Bloom (Eevee Only)"}, + {"transparent": "Transparent (Eevee Only)"}, + {"cryptomatte_object": "Cryptomatte Object"}, + {"cryptomatte_material": "Cryptomatte Material"}, + {"cryptomatte_asset": "Cryptomatte Asset"}, + {"cryptomatte_accurate": "Cryptomatte Accurate Mode (Eevee Only)"} ] }, { From 45bb933e801d703f527bc5813c649f29b3c5312e Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 25 Jan 2024 17:09:44 +0800 Subject: [PATCH 021/573] implement validator for model name in 3dsmax --- openpype/hosts/max/api/action.py | 39 ++++++++ .../plugins/publish/validate_model_name.py | 93 +++++++++++++++++++ .../defaults/project_settings/max.json | 6 ++ .../schemas/schema_max_publish.json | 24 +++++ .../max/server/settings/publishers.py | 20 ++++ server_addon/max/server/version.py | 2 +- 6 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 openpype/hosts/max/api/action.py create mode 100644 openpype/hosts/max/plugins/publish/validate_model_name.py diff --git a/openpype/hosts/max/api/action.py b/openpype/hosts/max/api/action.py new file mode 100644 index 0000000000..506847652b --- /dev/null +++ b/openpype/hosts/max/api/action.py @@ -0,0 +1,39 @@ +from pymxs import runtime as rt + +import pyblish.api + +from openpype.pipeline.publish import get_errored_instances_from_context + + +class SelectInvalidAction(pyblish.api.Action): + """Select invalid objects in Blender when a publish plug-in failed.""" + label = "Select Invalid" + on = "failed" + icon = "search" + + def process(self, context, plugin): + errored_instances = get_errored_instances_from_context(context, + plugin=plugin) + + # Get the invalid nodes for the plug-ins + self.log.info("Finding invalid nodes...") + invalid = list() + for instance in errored_instances: + invalid_nodes = plugin.get_invalid(instance) + if invalid_nodes: + if isinstance(invalid_nodes, (list, tuple)): + invalid.extend(invalid_nodes) + else: + self.log.warning( + "Failed plug-in doesn't have any selectable objects." + ) + + if not invalid: + self.log.info("No invalid nodes found.") + return + invalid_names = [obj.name for obj in invalid] + self.log.info( + "Selecting invalid objects: %s", ", ".join(invalid_names) + ) + + rt.Select(invalid) diff --git a/openpype/hosts/max/plugins/publish/validate_model_name.py b/openpype/hosts/max/plugins/publish/validate_model_name.py new file mode 100644 index 0000000000..3da9c9e0eb --- /dev/null +++ b/openpype/hosts/max/plugins/publish/validate_model_name.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +"""Validate model nodes names.""" +import re + +import pyblish.api +from pymxs import runtime as rt + +from openpype.hosts.max.api.action import SelectInvalidAction + +from openpype.pipeline.publish import ( + OptionalPyblishPluginMixin, + PublishValidationError, + ValidateContentsOrder) + + +class ValidateModelName(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): + """Validate name of model + + starts with (somename)_###_(materialID)_GEO + + """ + optional = True + order = ValidateContentsOrder + hosts = ["max"] + families = ["model"] + label = "Validate Model Name" + actions = [SelectInvalidAction] + regex = "" + + @classmethod + def get_invalid(cls, instance): + invalid = [] + #TODO: validation regex for validation + model_names = [model.name for model in instance.data.get("members")] + cls.log.debug(model_names) + if not model_names: + cls.log.error("No Model found in the OP Data.") + invalid.append(model_names) + for name in model_names: + invalid_model_name = cls.get_invalid_model_name(instance, name) + invalid.extend(invalid_model_name) + + return invalid + + @classmethod + def get_invalid_model_name(cls, instance, name): + invalid = [] + + regex = cls.regex + reg = re.compile(regex) + matched_name = reg.match(name) + project_name = instance.context.data["projectName"] + current_asset_name = instance.context.data["asset"] + if matched_name is None: + cls.log.error("invalid model name on: {}".format(name)) + cls.log.error("name doesn't match regex {}".format(regex)) + invalid.append((rt.getNodeByName(name), + "Model name doesn't match regex")) + else: + if "asset" in reg.groupindex: + if matched_name.group("asset") != current_asset_name: + cls.log.error( + "Invalid asset name of the model {}.".format(name) + ) + invalid.append((rt.getNodeByName(name), + "Model with invalid asset name")) + if "subset" in reg.groupindex: + if matched_name.group("subset") != instance.name: + cls.log.error( + "Invalid subset name of the model {}.".format(name) + ) + invalid.append((rt.getNodeByName(name), + "Model with invalid subset name")) + if "project" in reg.groupindex: + if matched_name.group("project") != project_name: + cls.log.error( + "Invalid project name of the model {}.".format(name) + ) + invalid.append((rt.getNodeByName(name), + "Model with invalid project name")) + return invalid + + def process(self, instance): + if not self.is_active(instance.data): + self.log.debug("Skipping Validate Frame Range...") + return + + invalid = self.get_invalid(instance) + + if invalid: + raise PublishValidationError( + "Model naming is invalid. See the log.") diff --git a/openpype/settings/defaults/project_settings/max.json b/openpype/settings/defaults/project_settings/max.json index d1610610dc..f719e7a156 100644 --- a/openpype/settings/defaults/project_settings/max.json +++ b/openpype/settings/defaults/project_settings/max.json @@ -56,6 +56,12 @@ "enabled": false, "attributes": {} }, + "ValidateModelName": { + "enabled": true, + "optional": true, + "active": false, + "regex": "(?P.*)_(GEO)" + }, "ValidateLoadedPlugin": { "enabled": false, "optional": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json index b4d85bda98..1e42b017ff 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json @@ -48,6 +48,30 @@ } ] }, + { + "type": "dict", + "collapsible": true, + "key": "ValidateModelName", + "label": "Validate Model Name", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "text", + "key": "regex", + "label": "validation regex" + } + ] + }, { "type": "dict", "collapsible": true, diff --git a/server_addon/max/server/settings/publishers.py b/server_addon/max/server/settings/publishers.py index d40d85a99b..9b11946195 100644 --- a/server_addon/max/server/settings/publishers.py +++ b/server_addon/max/server/settings/publishers.py @@ -38,6 +38,20 @@ class FamilyMappingItemModel(BaseSettingsModel): ) +class ValidateModelName(BaseSettingsModel): + enabled: bool = Field(title="Enabled") + optional: bool = Field(title="Optional") + active: bool = Field(title="Active") + regex: str = Field( + "(?P.*)_(GEO)", + title="Validation regex", + description=( + "Regex for validating model name. You can use named " + " capturing groups:(?P.*) for Asset name" + ) + ) + + class ValidateLoadedPluginModel(BaseSettingsModel): enabled: bool = Field(title="Enabled") optional: bool = Field(title="Optional") @@ -101,6 +115,12 @@ DEFAULT_PUBLISH_SETTINGS = { "enabled": False, "attributes": "{}" }, + "ValidateModelName": { + "enabled": True, + "optional": True, + "active": False, + "regex": "(?P.*)_(GEO)" + }, "ValidateLoadedPlugin": { "enabled": False, "optional": True, diff --git a/server_addon/max/server/version.py b/server_addon/max/server/version.py index bbab0242f6..1276d0254f 100644 --- a/server_addon/max/server/version.py +++ b/server_addon/max/server/version.py @@ -1 +1 @@ -__version__ = "0.1.4" +__version__ = "0.1.5" From 9a8700977df33ccd239a1c6d4f091636d76ca649 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 26 Jan 2024 18:26:41 +0800 Subject: [PATCH 022/573] make sure also validate the instance name inside the filename and make sure renderpass validated right --- .../plugins/publish/validate_renderpasses.py | 45 +++++++++++++++---- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_renderpasses.py b/openpype/hosts/max/plugins/publish/validate_renderpasses.py index 7b85763c74..b3f5c0325b 100644 --- a/openpype/hosts/max/plugins/publish/validate_renderpasses.py +++ b/openpype/hosts/max/plugins/publish/validate_renderpasses.py @@ -84,8 +84,12 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, invalid.append(("Invalid Render Output Folder", invalid_folder_name)) beauty_fname = os.path.basename(rt.rendOutputFilename) - ext = os.path.splitext(beauty_fname)[-1].lstrip(".") - invalid_image_format = cls.get_invalid_image_format(instance, ext) + beauty_name, ext = os.path.splitext(beauty_fname) + invalid_filenames = cls.get_invalid_filenames( + instance, beauty_name) + invalid.extend(invalid_filenames) + invalid_image_format = cls.get_invalid_image_format( + instance, ext.lstrip(".")) invalid.extend(invalid_image_format) renderer = instance.data["renderer"] if renderer in [ @@ -109,13 +113,9 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, "No filepath")) rend_fname, ext = os.path.splitext( os.path.basename(rend_file)) - if not rend_fname.lstrip(".").endswith(renderpass): - cls.log.error( - f"Filename for {renderpass} should " - f"end with {renderpass}" - ) - invalid.append((f"Invalid {renderpass}", - os.path.basename(rend_file))) + invalid_filenames = cls.get_invalid_filenames( + instance, rend_fname, renderpass=renderpass) + invalid.extend(invalid_filenames) invalid_image_format = cls.get_invalid_image_format( instance, ext) invalid.extend(invalid_image_format) @@ -126,6 +126,33 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, return invalid + @classmethod + def get_invalid_filenames(cls, instance, file_name, renderpass=None): + """Function to get invalid filenames from render outputs. + + Args: + instance (pyblish.api.Instance): instance + file_name (str): name of the file + renderpass (str, optional): name of the renderpass. Defaults to None. + + Returns: + list: invalid filenames + """ + invalid = [] + if instance.name not in file_name: + cls.log.error("The renderpass should have instance name inside.") + invalid.append((f"Invalid instance name", + file_name)) + if renderpass is not None: + if not file_name.rstrip(".").endswith(renderpass): + cls.log.error( + f"Filename for {renderpass} should " + f"end with {renderpass}" + ) + invalid.append((f"Invalid {renderpass}", + os.path.basename(file_name))) + return invalid + @classmethod def get_invalid_image_format(cls, instance, ext): """Function to check if the image format of the render outputs From 2b6f47035b1e9b21dc15c975c9ceb8584855ef57 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 26 Jan 2024 18:29:13 +0800 Subject: [PATCH 023/573] hound shut --- openpype/hosts/max/plugins/publish/validate_renderpasses.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/max/plugins/publish/validate_renderpasses.py b/openpype/hosts/max/plugins/publish/validate_renderpasses.py index b3f5c0325b..e636b4e8fe 100644 --- a/openpype/hosts/max/plugins/publish/validate_renderpasses.py +++ b/openpype/hosts/max/plugins/publish/validate_renderpasses.py @@ -133,7 +133,8 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, Args: instance (pyblish.api.Instance): instance file_name (str): name of the file - renderpass (str, optional): name of the renderpass. Defaults to None. + renderpass (str, optional): name of the renderpass. + Defaults to None. Returns: list: invalid filenames From d387dca0272c9be2a80beb52ac8272cabb10f426 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 26 Jan 2024 10:44:13 +0000 Subject: [PATCH 024/573] Use duration from streams as its more precise --- openpype/plugins/publish/extract_thumbnail.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_thumbnail.py b/openpype/plugins/publish/extract_thumbnail.py index 10eb261482..37f7ea7737 100644 --- a/openpype/plugins/publish/extract_thumbnail.py +++ b/openpype/plugins/publish/extract_thumbnail.py @@ -445,7 +445,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): # Set video input attributes max_int = str(2147483647) video_data = get_ffprobe_data(video_file_path, logger=self.log) - duration = float(video_data["format"]["duration"]) + duration = float(video_data["streams"][0]["duration"]) cmd_args = [ "-y", From e6818f94f5fead64b6ff8767462064fef8e10a41 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 29 Jan 2024 09:17:07 +0000 Subject: [PATCH 025/573] Illicit suggestion --- openpype/plugins/publish/extract_thumbnail.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_thumbnail.py b/openpype/plugins/publish/extract_thumbnail.py index 37f7ea7737..8a1e9fd12d 100644 --- a/openpype/plugins/publish/extract_thumbnail.py +++ b/openpype/plugins/publish/extract_thumbnail.py @@ -445,7 +445,11 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): # Set video input attributes max_int = str(2147483647) video_data = get_ffprobe_data(video_file_path, logger=self.log) - duration = float(video_data["streams"][0]["duration"]) + duration = max( + float(stream.get("duration", 0)) + for stream in video_data["streams"] + if stream.get("codec_type") == "video" + ) cmd_args = [ "-y", From 1d83c6aaa8adcb77ba9b5395cceae064e4aa8e0a Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 29 Jan 2024 17:19:04 +0800 Subject: [PATCH 026/573] add validate model name settings for ayon --- server_addon/max/server/settings/publishers.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/server_addon/max/server/settings/publishers.py b/server_addon/max/server/settings/publishers.py index 672f422fce..fcd034bcf0 100644 --- a/server_addon/max/server/settings/publishers.py +++ b/server_addon/max/server/settings/publishers.py @@ -38,11 +38,11 @@ class FamilyMappingItemModel(BaseSettingsModel): ) -class ValidateModelName(BaseSettingsModel): - enabled: bool = Field(title="Enabled") - optional: bool = Field(title="Optional") - active: bool = Field(title="Active") - regex: str = Field( +class ValidateModelNameModel(BaseSettingsModel): + enabled: bool = SettingsField(title="Enabled") + optional: bool = SettingsField(title="Optional") + active: bool = SettingsField(title="Active") + regex: str = SettingsField( "(?P.*)_(GEO)", title="Validation regex", description=( @@ -82,6 +82,10 @@ class PublishersModel(BaseSettingsModel): default_factory=ValidateLoadedPluginModel, title="Validate Loaded Plugin" ) + ValidateModelName: ValidateModelNameModel = SettingsField( + default_factory=ValidateModelNameModel, + title="Validate Model Name" + ) ExtractModelObj: BasicValidateModel = SettingsField( default_factory=BasicValidateModel, title="Extract OBJ", From 43aadb215b631d5ed6f149d945ba020652425046 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 29 Jan 2024 17:20:08 +0800 Subject: [PATCH 027/573] resolve hound --- openpype/hosts/max/plugins/publish/validate_model_name.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/max/plugins/publish/validate_model_name.py b/openpype/hosts/max/plugins/publish/validate_model_name.py index 3da9c9e0eb..c85b4729c5 100644 --- a/openpype/hosts/max/plugins/publish/validate_model_name.py +++ b/openpype/hosts/max/plugins/publish/validate_model_name.py @@ -31,7 +31,6 @@ class ValidateModelName(pyblish.api.InstancePlugin, @classmethod def get_invalid(cls, instance): invalid = [] - #TODO: validation regex for validation model_names = [model.name for model in instance.data.get("members")] cls.log.debug(model_names) if not model_names: From b822dd0e8d762c6641f1637fad4f9220bdfc7480 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 29 Jan 2024 17:27:22 +0800 Subject: [PATCH 028/573] add docstring --- openpype/hosts/max/plugins/publish/validate_model_name.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_model_name.py b/openpype/hosts/max/plugins/publish/validate_model_name.py index c85b4729c5..9603c45150 100644 --- a/openpype/hosts/max/plugins/publish/validate_model_name.py +++ b/openpype/hosts/max/plugins/publish/validate_model_name.py @@ -15,9 +15,11 @@ from openpype.pipeline.publish import ( class ValidateModelName(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): - """Validate name of model + """Validate Model Name + Validation regex is (?P.*)_(GEO) by default. + e.g. (subset_name)_GEO should be your model name - starts with (somename)_###_(materialID)_GEO + starts with (somename)_GEO """ optional = True @@ -45,7 +47,6 @@ class ValidateModelName(pyblish.api.InstancePlugin, @classmethod def get_invalid_model_name(cls, instance, name): invalid = [] - regex = cls.regex reg = re.compile(regex) matched_name = reg.match(name) From 5e8af09c4b116476cc80d5ca7ba22f969ae534e7 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 29 Jan 2024 17:53:35 +0800 Subject: [PATCH 029/573] update regex name --- openpype/hosts/max/plugins/publish/validate_model_name.py | 2 +- openpype/settings/defaults/project_settings/max.json | 2 +- server_addon/max/server/settings/publishers.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_model_name.py b/openpype/hosts/max/plugins/publish/validate_model_name.py index 9603c45150..a264221372 100644 --- a/openpype/hosts/max/plugins/publish/validate_model_name.py +++ b/openpype/hosts/max/plugins/publish/validate_model_name.py @@ -83,7 +83,7 @@ class ValidateModelName(pyblish.api.InstancePlugin, def process(self, instance): if not self.is_active(instance.data): - self.log.debug("Skipping Validate Frame Range...") + self.log.debug("Skipping Validate Model Name...") return invalid = self.get_invalid(instance) diff --git a/openpype/settings/defaults/project_settings/max.json b/openpype/settings/defaults/project_settings/max.json index f719e7a156..e8fb2d31c4 100644 --- a/openpype/settings/defaults/project_settings/max.json +++ b/openpype/settings/defaults/project_settings/max.json @@ -60,7 +60,7 @@ "enabled": true, "optional": true, "active": false, - "regex": "(?P.*)_(GEO)" + "regex": "(.*)_(?P.*)_(GEO)" }, "ValidateLoadedPlugin": { "enabled": false, diff --git a/server_addon/max/server/settings/publishers.py b/server_addon/max/server/settings/publishers.py index fcd034bcf0..761f4c54f1 100644 --- a/server_addon/max/server/settings/publishers.py +++ b/server_addon/max/server/settings/publishers.py @@ -43,7 +43,7 @@ class ValidateModelNameModel(BaseSettingsModel): optional: bool = SettingsField(title="Optional") active: bool = SettingsField(title="Active") regex: str = SettingsField( - "(?P.*)_(GEO)", + "(.*)_(?P.*)_(GEO)", title="Validation regex", description=( "Regex for validating model name. You can use named " @@ -123,7 +123,7 @@ DEFAULT_PUBLISH_SETTINGS = { "enabled": True, "optional": True, "active": False, - "regex": "(?P.*)_(GEO)" + "regex": "*_(?P.*)_(GEO)" }, "ValidateLoadedPlugin": { "enabled": False, From 3f8830e27d9c7f4002985ebcf05f7fe395fc3e17 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Mon, 29 Jan 2024 10:13:18 +0000 Subject: [PATCH 030/573] Update openpype/plugins/publish/extract_thumbnail.py --- openpype/plugins/publish/extract_thumbnail.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/plugins/publish/extract_thumbnail.py b/openpype/plugins/publish/extract_thumbnail.py index 8a1e9fd12d..2e272b061b 100644 --- a/openpype/plugins/publish/extract_thumbnail.py +++ b/openpype/plugins/publish/extract_thumbnail.py @@ -445,6 +445,10 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): # Set video input attributes max_int = str(2147483647) video_data = get_ffprobe_data(video_file_path, logger=self.log) + # Use duration of the individual streams since it is returned with + # higher decimal precision than 'format.duration'. We need this + # more precise value for calculating the correct amount of frames + # for higher FPS ranges or decimal ranges, e.g. 29.97 FPS duration = max( float(stream.get("duration", 0)) for stream in video_data["streams"] From 3807172faed3addc22790c6831065d7424e08035 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Mon, 29 Jan 2024 10:13:40 +0000 Subject: [PATCH 031/573] Update openpype/plugins/publish/extract_thumbnail.py --- openpype/plugins/publish/extract_thumbnail.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_thumbnail.py b/openpype/plugins/publish/extract_thumbnail.py index 2e272b061b..cbeada6bac 100644 --- a/openpype/plugins/publish/extract_thumbnail.py +++ b/openpype/plugins/publish/extract_thumbnail.py @@ -448,7 +448,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): # Use duration of the individual streams since it is returned with # higher decimal precision than 'format.duration'. We need this # more precise value for calculating the correct amount of frames - # for higher FPS ranges or decimal ranges, e.g. 29.97 FPS + # for higher FPS ranges or decimal ranges, e.g. 29.97 FPS duration = max( float(stream.get("duration", 0)) for stream in video_data["streams"] From 44282f86df721a271a291bc005abd07f896acb49 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 1 Feb 2024 10:42:34 +0000 Subject: [PATCH 032/573] Fixed deadline validator to check compositor tree output --- .../publish/validate_deadline_publish.py | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/blender/plugins/publish/validate_deadline_publish.py b/openpype/hosts/blender/plugins/publish/validate_deadline_publish.py index bb243f08cc..f7860dd75d 100644 --- a/openpype/hosts/blender/plugins/publish/validate_deadline_publish.py +++ b/openpype/hosts/blender/plugins/publish/validate_deadline_publish.py @@ -28,15 +28,27 @@ class ValidateDeadlinePublish(pyblish.api.InstancePlugin, def process(self, instance): if not self.is_active(instance.data): return + + tree = bpy.context.scene.node_tree + output_type = "CompositorNodeOutputFile" + output_node = None + # Remove all output nodes that inlcude "AYON" in the name. + # There should be only one. + for node in tree.nodes: + if node.bl_idname == output_type and "AYON" in node.name: + output_node = node + break + if not output_node: + raise PublishValidationError( + "No output node found in the compositor tree." + ) filepath = bpy.data.filepath file = os.path.basename(filepath) filename, ext = os.path.splitext(file) - if filename not in bpy.context.scene.render.filepath: + if filename not in output_node.base_path: raise PublishValidationError( - "Render output folder " - "doesn't match the blender scene name! " - "Use Repair action to " - "fix the folder file path." + "Render output folder doesn't match the blender scene name! " + "Use Repair action to fix the folder file path." ) @classmethod From 6b9da9b1e83a1be30197d00d4aae198050590d33 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 2 Feb 2024 16:14:08 +0800 Subject: [PATCH 033/573] update docstring --- .../max/plugins/publish/validate_model_name.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_model_name.py b/openpype/hosts/max/plugins/publish/validate_model_name.py index a264221372..b0e5677592 100644 --- a/openpype/hosts/max/plugins/publish/validate_model_name.py +++ b/openpype/hosts/max/plugins/publish/validate_model_name.py @@ -16,10 +16,17 @@ from openpype.pipeline.publish import ( class ValidateModelName(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): """Validate Model Name - Validation regex is (?P.*)_(GEO) by default. - e.g. (subset_name)_GEO should be your model name + Validation regex is (.*)_(?P.*)_(GEO) by default. + e.g. {SOME_RANDOM_NAME}_{YOUR_SUBSET_NAME}_GEO should be your + default model name - starts with (somename)_GEO + The regex of (?P.*) can be replaced by (?P.*) + and (?P.*). + e.g. + - (.*)_(?P.*)_(GEO) check if your model name is + {SOME_RANDOM_NAME}_{CURRENT_ASSET_NAME}_GEO + - (.*)_(?P.*)_(GEO) check if your model name is + {SOME_RANDOM_NAME}_{CURRENT_PROJECT_NAME}_GEO """ optional = True From b7aa3e323d74a9524a30bfb472a07b77939ef8a2 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 2 Feb 2024 11:38:23 +0200 Subject: [PATCH 034/573] fix default redshift version --- .../deadline/plugins/publish/submit_houdini_render_deadline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py b/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py index bf7fb45a8b..e0e52fe6a6 100644 --- a/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py @@ -45,7 +45,7 @@ class VrayRenderPluginInfo(): @attr.s class RedshiftRenderPluginInfo(): SceneFile = attr.ib(default=None) - Version = attr.ib(default=None) + Version = attr.ib(default="1") class HoudiniSubmitDeadline( From c2dbf604d8eb1fec466182f434daa332e952341f Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 2 Feb 2024 14:42:28 +0200 Subject: [PATCH 035/573] add comment about using v1 as RS default version --- .../deadline/plugins/publish/submit_houdini_render_deadline.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py b/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py index e0e52fe6a6..582f7d081b 100644 --- a/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py @@ -45,6 +45,9 @@ class VrayRenderPluginInfo(): @attr.s class RedshiftRenderPluginInfo(): SceneFile = attr.ib(default=None) + # "1" was selected as the default RS version as it's the default + # version in RS deadline plugin. + # https://docs.thinkboxsoftware.com/products/deadline/10.1/1_User%20Manual/manual/app-redshift.html#plug-in-configuration Version = attr.ib(default="1") From 7d586f59061cabab548516c3fcde289ad8dc3501 Mon Sep 17 00:00:00 2001 From: Mustafa Taher Date: Fri, 2 Feb 2024 15:06:12 +0200 Subject: [PATCH 036/573] remove RS deadline plugin doc link Co-authored-by: Roy Nieterau --- .../deadline/plugins/publish/submit_houdini_render_deadline.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py b/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py index 582f7d081b..cf8e1617f2 100644 --- a/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py @@ -47,7 +47,6 @@ class RedshiftRenderPluginInfo(): SceneFile = attr.ib(default=None) # "1" was selected as the default RS version as it's the default # version in RS deadline plugin. - # https://docs.thinkboxsoftware.com/products/deadline/10.1/1_User%20Manual/manual/app-redshift.html#plug-in-configuration Version = attr.ib(default="1") From 4e114b80324e71c808e65b872c965ce693c42923 Mon Sep 17 00:00:00 2001 From: Mustafa Taher Date: Fri, 2 Feb 2024 15:07:10 +0200 Subject: [PATCH 037/573] update comment about default RS version Co-authored-by: Roy Nieterau --- .../plugins/publish/submit_houdini_render_deadline.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py b/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py index cf8e1617f2..402dcaf256 100644 --- a/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py @@ -45,8 +45,9 @@ class VrayRenderPluginInfo(): @attr.s class RedshiftRenderPluginInfo(): SceneFile = attr.ib(default=None) - # "1" was selected as the default RS version as it's the default - # version in RS deadline plugin. + # Use "1" as the default Redshift version just because it + # default fallback version in Deadline's Redshift plugin + # if no version was specified Version = attr.ib(default="1") From 02dea53a17449572897fb1ee30255f8c4aa6ddfc Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 5 Feb 2024 18:54:26 +0800 Subject: [PATCH 038/573] optional validator to check invalid context data --- openpype/hosts/max/api/action.py | 39 ++++++ .../publish/validate_instance_in_context.py | 111 ++++++++++++++++++ .../defaults/project_settings/max.json | 5 + .../schemas/schema_max_publish.json | 25 ++++ .../max/server/settings/publishers.py | 8 +- server_addon/max/server/version.py | 2 +- 6 files changed, 187 insertions(+), 3 deletions(-) create mode 100644 openpype/hosts/max/api/action.py create mode 100644 openpype/hosts/max/plugins/publish/validate_instance_in_context.py diff --git a/openpype/hosts/max/api/action.py b/openpype/hosts/max/api/action.py new file mode 100644 index 0000000000..506847652b --- /dev/null +++ b/openpype/hosts/max/api/action.py @@ -0,0 +1,39 @@ +from pymxs import runtime as rt + +import pyblish.api + +from openpype.pipeline.publish import get_errored_instances_from_context + + +class SelectInvalidAction(pyblish.api.Action): + """Select invalid objects in Blender when a publish plug-in failed.""" + label = "Select Invalid" + on = "failed" + icon = "search" + + def process(self, context, plugin): + errored_instances = get_errored_instances_from_context(context, + plugin=plugin) + + # Get the invalid nodes for the plug-ins + self.log.info("Finding invalid nodes...") + invalid = list() + for instance in errored_instances: + invalid_nodes = plugin.get_invalid(instance) + if invalid_nodes: + if isinstance(invalid_nodes, (list, tuple)): + invalid.extend(invalid_nodes) + else: + self.log.warning( + "Failed plug-in doesn't have any selectable objects." + ) + + if not invalid: + self.log.info("No invalid nodes found.") + return + invalid_names = [obj.name for obj in invalid] + self.log.info( + "Selecting invalid objects: %s", ", ".join(invalid_names) + ) + + rt.Select(invalid) diff --git a/openpype/hosts/max/plugins/publish/validate_instance_in_context.py b/openpype/hosts/max/plugins/publish/validate_instance_in_context.py new file mode 100644 index 0000000000..afe5ca5a7f --- /dev/null +++ b/openpype/hosts/max/plugins/publish/validate_instance_in_context.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- +"""Validate if instance asset is the same as context asset.""" +from __future__ import absolute_import + +import pyblish.api +from openpype import AYON_SERVER_ENABLED +from openpype.pipeline.publish import ( + RepairAction, + ValidateContentsOrder, + PublishValidationError, + OptionalPyblishPluginMixin +) +from openpype.hosts.max.api.action import SelectInvalidAction +from pymxs import runtime as rt + + +class ValidateInstanceInContext(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): + """Validator to check if instance asset match context asset. + + When working in per-shot style you always publish data in context of + current asset (shot). This validator checks if this is so. It is optional + so it can be disabled when needed. + + Action on this validator will select invalid instances. + """ + + order = ValidateContentsOrder + label = "Instance in same Context" + optional = True + hosts = ["max"] + actions = [SelectInvalidAction, RepairAction] + + def process(self, instance): + if not self.is_active(instance.data): + return + + instance_node = rt.getNodeByName(instance.data.get( + "instance_node", "")) + if not instance_node: + return + asset_name_attr = "folderPath" if AYON_SERVER_ENABLED else "asset" + asset = rt.getUserProp(instance_node, asset_name_attr) + context_asset = self.get_context_asset(instance) + task = rt.getUserProp(instance_node, "task") + context_task = self.get_context_task(instance) + if asset != context_asset: + raise PublishValidationError( + message=( + "Instance '{}' publishes to different asset than current " + "context: {}. Current context: {}".format( + instance.name, asset, context_asset + ) + ), + description=( + "## Publishing to a different asset\n" + "There are publish instances present which are publishing " + "into a different asset than your current context.\n\n" + "Usually this is not what you want but there can be cases " + "where you might want to publish into another asset or " + "shot. If that's the case you can disable the validation " + "on the instance to ignore it." + ) + ) + if task != context_task: + raise PublishValidationError( + message=( + "Instance '{}' publishes to different task than current " + "context: {}. Current context: {}".format( + instance.name, task, context_task + ) + ), + description=( + "## Publishing to a different asset\n" + "There are publish instances present which are publishing " + "into a different asset than your current context.\n\n" + "Usually this is not what you want but there can be cases " + "where you might want to publish into another asset or " + "shot. If that's the case you can disable the validation " + "on the instance to ignore it." + ) + ) + + @classmethod + def get_invalid(cls, instance): + asset_name_attr = "folderPath" if AYON_SERVER_ENABLED else "asset" + node = rt.getNodeByName(instance.data["instance_node"]) + asset = rt.getUserProp(node, asset_name_attr) + context_asset = cls.get_context_asset(instance) + if asset != context_asset: + return instance.data["instance_node"] + + @classmethod + def repair(cls, instance): + context_asset = cls.get_context_asset(instance) + context_task = cls.get_context_task(instance) + instance_node = rt.getNodeByName(instance.data.get( + "instance_node", "")) + if not instance_node: + return + asset_name_attr = "folderPath" if AYON_SERVER_ENABLED else "asset" + rt.SetUserProp(instance_node, asset_name_attr, context_asset) + rt.SetUserProp(instance_node, "task", context_task) + + @staticmethod + def get_context_asset(instance): + return instance.context.data["asset"] + + @staticmethod + def get_context_task(instance): + return instance.context.data["task"] diff --git a/openpype/settings/defaults/project_settings/max.json b/openpype/settings/defaults/project_settings/max.json index d1610610dc..eb4667c36e 100644 --- a/openpype/settings/defaults/project_settings/max.json +++ b/openpype/settings/defaults/project_settings/max.json @@ -47,6 +47,11 @@ } }, "publish": { + "ValidateInstanceInContext": { + "enabled": true, + "optional": true, + "active": true + }, "ValidateFrameRange": { "enabled": true, "optional": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json index b4d85bda98..fc563ff372 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json @@ -4,6 +4,31 @@ "key": "publish", "label": "Publish plugins", "children": [ + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "ValidateInstanceInContext", + "label": "Validate Instance In Context", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "boolean", + "key": "active", + "label": "Active" + } + ] + }, { "type": "dict", "collapsible": true, diff --git a/server_addon/max/server/settings/publishers.py b/server_addon/max/server/settings/publishers.py index da782cb494..03cfefa303 100644 --- a/server_addon/max/server/settings/publishers.py +++ b/server_addon/max/server/settings/publishers.py @@ -54,10 +54,14 @@ class BasicValidateModel(BaseSettingsModel): class PublishersModel(BaseSettingsModel): + ValidateInstanceInContext: BasicValidateModel = SettingsField( + default_factory=BasicValidateModel, + title="Validate Instance In Context", + section="Validators" + ) ValidateFrameRange: BasicValidateModel = SettingsField( default_factory=BasicValidateModel, - title="Validate Frame Range", - section="Validators" + title="Validate Frame Range" ) ValidateAttributes: ValidateAttributesModel = SettingsField( default_factory=ValidateAttributesModel, diff --git a/server_addon/max/server/version.py b/server_addon/max/server/version.py index bbab0242f6..1276d0254f 100644 --- a/server_addon/max/server/version.py +++ b/server_addon/max/server/version.py @@ -1 +1 @@ -__version__ = "0.1.4" +__version__ = "0.1.5" From 78a3ec2118c9963e31ea0b4ca2907d0b84a14b39 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Mon, 5 Feb 2024 12:06:19 +0000 Subject: [PATCH 039/573] Fixed issue with double entries in the json manifest --- openpype/hosts/blender/api/render_lib.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/blender/api/render_lib.py b/openpype/hosts/blender/api/render_lib.py index fc47f5a659..c2792103e5 100644 --- a/openpype/hosts/blender/api/render_lib.py +++ b/openpype/hosts/blender/api/render_lib.py @@ -244,7 +244,9 @@ def set_node_tree(output_path, render_product, name, aov_sep, ext, multilayer): # and link it for rpass in passes: if rpass.name == "Image": - pass_name = "rgba" if multi_exr else "beauty" + if not multi_exr: + continue + pass_name = "rgba" else: pass_name = rpass.name filename = f"{name}{aov_sep}{pass_name}.####" @@ -263,6 +265,7 @@ def set_node_tree(output_path, render_product, name, aov_sep, ext, multilayer): pass_name = "composite" filename = f"{name}{aov_sep}{pass_name}.####" comp_socket = slots.new(pass_name if multi_exr else filename) + filepath = str(output_path / filename.lstrip("/")) aov_file_products.append(("Composite", filepath)) for link in tree.links: From 12f78b17668c8f5034c60f8ba6921a5aa507c015 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 5 Feb 2024 20:12:22 +0800 Subject: [PATCH 040/573] update regex in ayon settings --- server_addon/max/server/settings/publishers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/max/server/settings/publishers.py b/server_addon/max/server/settings/publishers.py index 761f4c54f1..05d084aac5 100644 --- a/server_addon/max/server/settings/publishers.py +++ b/server_addon/max/server/settings/publishers.py @@ -123,7 +123,7 @@ DEFAULT_PUBLISH_SETTINGS = { "enabled": True, "optional": True, "active": False, - "regex": "*_(?P.*)_(GEO)" + "regex": "(.*)_(?P.*)_(GEO)" }, "ValidateLoadedPlugin": { "enabled": False, From 09342f56338456d3a2e8245a7738d0d23c6da281 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 5 Feb 2024 23:29:18 +0800 Subject: [PATCH 041/573] fix action.py --- openpype/hosts/max/api/action.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/max/api/action.py b/openpype/hosts/max/api/action.py index 506847652b..818d4db6e5 100644 --- a/openpype/hosts/max/api/action.py +++ b/openpype/hosts/max/api/action.py @@ -31,9 +31,10 @@ class SelectInvalidAction(pyblish.api.Action): if not invalid: self.log.info("No invalid nodes found.") return - invalid_names = [obj.name for obj in invalid] + self.log.debug(f"invalid{invalid}") + invalid_names = [obj for obj, _ in invalid] self.log.info( - "Selecting invalid objects: %s", ", ".join(invalid_names) + f"Selecting invalid objects: {invalid_names}" ) - rt.Select(invalid) + rt.Select(invalid_names) From 1a63065c13ef70762e994519aaec1d4294ecd81c Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 5 Feb 2024 23:34:30 +0800 Subject: [PATCH 042/573] fix action.py --- openpype/hosts/max/api/action.py | 7 +++---- .../hosts/max/plugins/publish/validate_model_name.py | 12 ++++-------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/openpype/hosts/max/api/action.py b/openpype/hosts/max/api/action.py index 818d4db6e5..506847652b 100644 --- a/openpype/hosts/max/api/action.py +++ b/openpype/hosts/max/api/action.py @@ -31,10 +31,9 @@ class SelectInvalidAction(pyblish.api.Action): if not invalid: self.log.info("No invalid nodes found.") return - self.log.debug(f"invalid{invalid}") - invalid_names = [obj for obj, _ in invalid] + invalid_names = [obj.name for obj in invalid] self.log.info( - f"Selecting invalid objects: {invalid_names}" + "Selecting invalid objects: %s", ", ".join(invalid_names) ) - rt.Select(invalid_names) + rt.Select(invalid) diff --git a/openpype/hosts/max/plugins/publish/validate_model_name.py b/openpype/hosts/max/plugins/publish/validate_model_name.py index b0e5677592..3cd7265077 100644 --- a/openpype/hosts/max/plugins/publish/validate_model_name.py +++ b/openpype/hosts/max/plugins/publish/validate_model_name.py @@ -62,30 +62,26 @@ class ValidateModelName(pyblish.api.InstancePlugin, if matched_name is None: cls.log.error("invalid model name on: {}".format(name)) cls.log.error("name doesn't match regex {}".format(regex)) - invalid.append((rt.getNodeByName(name), - "Model name doesn't match regex")) + invalid.append((name,"Model name doesn't match regex")) else: if "asset" in reg.groupindex: if matched_name.group("asset") != current_asset_name: cls.log.error( "Invalid asset name of the model {}.".format(name) ) - invalid.append((rt.getNodeByName(name), - "Model with invalid asset name")) + invalid.append((name, "Model with invalid asset name")) if "subset" in reg.groupindex: if matched_name.group("subset") != instance.name: cls.log.error( "Invalid subset name of the model {}.".format(name) ) - invalid.append((rt.getNodeByName(name), - "Model with invalid subset name")) + invalid.append((name, "Model with invalid subset name")) if "project" in reg.groupindex: if matched_name.group("project") != project_name: cls.log.error( "Invalid project name of the model {}.".format(name) ) - invalid.append((rt.getNodeByName(name), - "Model with invalid project name")) + invalid.append((name, "Model with invalid project name")) return invalid def process(self, instance): From bfd952007feb1f37eec2924d510637cfbee01c60 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 5 Feb 2024 23:35:41 +0800 Subject: [PATCH 043/573] hound --- openpype/hosts/max/plugins/publish/validate_model_name.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_model_name.py b/openpype/hosts/max/plugins/publish/validate_model_name.py index 3cd7265077..a36947931c 100644 --- a/openpype/hosts/max/plugins/publish/validate_model_name.py +++ b/openpype/hosts/max/plugins/publish/validate_model_name.py @@ -3,7 +3,6 @@ import re import pyblish.api -from pymxs import runtime as rt from openpype.hosts.max.api.action import SelectInvalidAction @@ -62,7 +61,7 @@ class ValidateModelName(pyblish.api.InstancePlugin, if matched_name is None: cls.log.error("invalid model name on: {}".format(name)) cls.log.error("name doesn't match regex {}".format(regex)) - invalid.append((name,"Model name doesn't match regex")) + invalid.append((name, "Model name doesn't match regex")) else: if "asset" in reg.groupindex: if matched_name.group("asset") != current_asset_name: From 83c8239a4918804af3379b44bf45698a965a377d Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 6 Feb 2024 14:32:04 +0800 Subject: [PATCH 044/573] add missing ayon settings in json for validate instance in context --- server_addon/max/server/settings/publishers.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server_addon/max/server/settings/publishers.py b/server_addon/max/server/settings/publishers.py index 03cfefa303..58c71d977d 100644 --- a/server_addon/max/server/settings/publishers.py +++ b/server_addon/max/server/settings/publishers.py @@ -96,6 +96,11 @@ class PublishersModel(BaseSettingsModel): DEFAULT_PUBLISH_SETTINGS = { + "ValidateInstanceInContext": { + "enabled": True, + "optional": True, + "active": True + }, "ValidateFrameRange": { "enabled": True, "optional": True, From f2429680ff5aa68b358ececeba02d24e9d86ad0c Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Tue, 6 Feb 2024 15:43:18 +0000 Subject: [PATCH 045/573] Fix old links and duplicated entries in the manifest --- openpype/hosts/blender/api/render_lib.py | 114 ++++++++++++----------- 1 file changed, 62 insertions(+), 52 deletions(-) diff --git a/openpype/hosts/blender/api/render_lib.py b/openpype/hosts/blender/api/render_lib.py index c2792103e5..17b9d926ec 100644 --- a/openpype/hosts/blender/api/render_lib.py +++ b/openpype/hosts/blender/api/render_lib.py @@ -178,6 +178,14 @@ def set_render_passes(settings, renderer): return aov_list, custom_passes +def _create_aov_slot(name, aov_sep, slots, rpass_name, multi_exr, output_path): + filename = f"{name}{aov_sep}{rpass_name}.####" + slot = slots.new(rpass_name if multi_exr else filename) + filepath = str(output_path / filename.lstrip("/")) + + return slot, filepath + + def set_node_tree(output_path, render_product, name, aov_sep, ext, multilayer): # Set the scene to use the compositor node tree to render bpy.context.scene.use_nodes = True @@ -188,40 +196,34 @@ def set_node_tree(output_path, render_product, name, aov_sep, ext, multilayer): output_type = "CompositorNodeOutputFile" compositor_type = "CompositorNodeComposite" - # Get the Render Layer and Composite nodes - rl_node = None - comp_node = None + # Get the Render Layer, Composite and the previous output nodes + render_layer_node = None + composite_node = None + old_output_node = None for node in tree.nodes: if node.bl_idname == comp_layer_type: - rl_node = node + render_layer_node = node elif node.bl_idname == compositor_type: - comp_node = node - if rl_node and comp_node: + composite_node = node + elif node.bl_idname == output_type and "AYON" in node.name: + old_output_node = node + if render_layer_node and composite_node and old_output_node: break # If there's not a Render Layers node, we create it - if not rl_node: - rl_node = tree.nodes.new(comp_layer_type) + if not render_layer_node: + render_layer_node = tree.nodes.new(comp_layer_type) # Get the enabled output sockets, that are the active passes for the # render. # We also exclude some layers. - exclude_sockets = ["Alpha", "Noisy Image"] + exclude_sockets = ["Image", "Alpha", "Noisy Image"] passes = [ socket - for socket in rl_node.outputs + for socket in render_layer_node.outputs if socket.enabled and socket.name not in exclude_sockets ] - old_output = None - - # Remove all output nodes that inlcude "AYON" in the name. - # There should be only one. - for node in tree.nodes: - if node.bl_idname == output_type and "AYON" in node.name: - old_output = node - break - # Create a new output node output = tree.nodes.new(output_type) @@ -240,46 +242,54 @@ def set_node_tree(output_path, render_product, name, aov_sep, ext, multilayer): aov_file_products = [] - # For each active render pass, we add a new socket to the output node - # and link it - for rpass in passes: - if rpass.name == "Image": - if not multi_exr: - continue - pass_name = "rgba" - else: - pass_name = rpass.name - filename = f"{name}{aov_sep}{pass_name}.####" + old_links = { + link.from_socket.name: link for link in tree.links + if link.to_node == old_output_node} - slots.new(pass_name if multi_exr else filename) - - filepath = str(output_path / filename.lstrip("/")) - - aov_file_products.append((rpass.name, filepath)) - - if not old_output: - node_input = output.inputs[-1] - tree.links.new(rpass, node_input) + # Create a new socket for the beauty output + pass_name = "rgba" if multi_exr else "beauty" + slot, _ = _create_aov_slot( + name, aov_sep, slots, pass_name, multi_exr, output_path) + tree.links.new(render_layer_node.outputs["Image"], slot) # Create a new socket for the composite output pass_name = "composite" - filename = f"{name}{aov_sep}{pass_name}.####" - comp_socket = slots.new(pass_name if multi_exr else filename) - filepath = str(output_path / filename.lstrip("/")) + comp_socket, filepath = _create_aov_slot( + name, aov_sep, slots, pass_name, multi_exr, output_path) aov_file_products.append(("Composite", filepath)) - for link in tree.links: - if link.to_node == old_output: - socket_name = link.to_socket.name - new_socket = output.inputs.get(socket_name) - if new_socket: - tree.links.new(link.from_socket, new_socket) - elif comp_node and link.to_node == comp_node: - tree.links.new(link.from_socket, comp_socket) + # For each active render pass, we add a new socket to the output node + # and link it + for rpass in passes: + slot, filepath = _create_aov_slot( + name, aov_sep, slots, rpass.name, multi_exr, output_path) + aov_file_products.append((rpass.name, filepath)) + + # If the rpass was not connected with the old output node, we connect + # it with the new one. + if not old_links.get(rpass.name): + tree.links.new(rpass, slot) + + for link in list(old_links.values()): + # Check if the socket is still available in the new output node. + socket = output.inputs.get(link.to_socket.name) + # If it is, we connect it with the new output node. + if socket: + tree.links.new(link.from_socket, socket) + # Then, we remove the old link. + tree.links.remove(link) + + # If there's a composite node, we connect its input with the new output + if composite_node: + for link in tree.links: + if link.to_node == composite_node: + tree.links.new(link.from_socket, comp_socket) + break + + if old_output_node: + output.location = old_output_node.location + tree.nodes.remove(old_output_node) - if old_output: - output.location = old_output.location - tree.nodes.remove(old_output) output.name = "AYON File Output" output.label = "AYON File Output" From 14fc4ce6be35f02096ecc56cc865300ef67a705c Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 7 Feb 2024 17:51:52 +0800 Subject: [PATCH 046/573] Implementation of workfile creator --- .../max/plugins/create/create_workfile.py | 130 ++++++++++++++++++ .../max/plugins/publish/collect_members.py | 5 +- .../max/plugins/publish/collect_workfile.py | 14 +- 3 files changed, 140 insertions(+), 9 deletions(-) create mode 100644 openpype/hosts/max/plugins/create/create_workfile.py diff --git a/openpype/hosts/max/plugins/create/create_workfile.py b/openpype/hosts/max/plugins/create/create_workfile.py new file mode 100644 index 0000000000..b43a353136 --- /dev/null +++ b/openpype/hosts/max/plugins/create/create_workfile.py @@ -0,0 +1,130 @@ +# -*- coding: utf-8 -*- +"""Creator plugin for creating workfiles.""" +from openpype import AYON_SERVER_ENABLED +from openpype.pipeline import CreatedInstance, AutoCreator, CreatorError +from openpype.client import get_asset_by_name, get_asset_name_identifier +from openpype.hosts.max.api import plugin +from openpype.hosts.max.api.lib import read, imprint +from pymxs import runtime as rt + + +class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): + """Workfile auto-creator.""" + identifier = "io.openpype.creators.max.workfile" + label = "Workfile" + family = "workfile" + icon = "fa5.file" + + default_variant = "Main" + + def create(self): + variant = self.default_variant + current_instance = next( + ( + instance for instance in self.create_context.instances + if instance.creator_identifier == self.identifier + ), None) + project_name = self.project_name + asset_name = self.create_context.get_current_asset_name() + task_name = self.create_context.get_current_task_name() + host_name = self.create_context.host_name + + if current_instance is None: + current_instance_asset = None + elif AYON_SERVER_ENABLED: + current_instance_asset = current_instance["folderPath"] + else: + current_instance_asset = current_instance["asset"] + + if current_instance is None: + asset_doc = get_asset_by_name(project_name, asset_name) + data = { + "task": task_name, + "variant": variant + } + if AYON_SERVER_ENABLED: + data["folderPath"] = asset_name + else: + data["asset"] = asset_name + + data.update( + self.get_dynamic_data( + variant, task_name, asset_doc, + project_name, host_name, current_instance) + ) + subset_name = self.get_subset_name( + variant, task_name, asset_doc, project_name, host_name + ) + self.log.info("Auto-creating workfile instance...") + instance_node = self.create_node(subset_name) + data["instance_node"] = instance_node.name + current_instance = CreatedInstance( + self.family, subset_name, data, self + ) + self._add_instance_to_context(current_instance) + imprint(instance_node.name, current_instance.data) + elif ( + current_instance_asset != asset_name + or current_instance["task"] != task_name + ): + # Update instance context if is not the same + asset_doc = get_asset_by_name(project_name, asset_name) + subset_name = self.get_subset_name( + variant, task_name, asset_doc, project_name, host_name + ) + asset_name = get_asset_name_identifier(asset_doc) + + if AYON_SERVER_ENABLED: + current_instance["folderPath"] = asset_name + else: + current_instance["asset"] = asset_name + current_instance["task"] = task_name + current_instance["subset"] = subset_name + + def collect_instances(self): + self.cache_subsets(self.collection_shared_data) + for instance in self.collection_shared_data["max_cached_subsets"].get(self.identifier, []): # noqa + if not rt.getNodeByName(instance): + continue + created_instance = CreatedInstance.from_existing( + read(rt.GetNodeByName(instance)), self + ) + self._add_instance_to_context(created_instance) + + + def update_instances(self, update_list): + for created_inst, changes in update_list: + instance_node = created_inst.get("instance_node") + new_values = { + key: changes[key].new_value + for key in changes.changed_keys + } + + imprint( + instance_node, + created_inst.data_to_store() + ) + + def remove_instances(self, instances): + """Remove specified instance from the scene. + + This is only removing `id` parameter so instance is no longer + instance, because it might contain valuable data for artist. + + """ + for instance in instances: + instance_node = rt.GetNodeByName( + instance.data.get("instance_node")) + if instance_node: + rt.Delete(instance_node) + + self._remove_instance_from_context(instance) + + + def create_node(self, subset_name): + if rt.getNodeByName(subset_name): + node = rt.getNodeByName(subset_name) + return node + node = rt.Container(name=subset_name) + node.isHidden = True + return node diff --git a/openpype/hosts/max/plugins/publish/collect_members.py b/openpype/hosts/max/plugins/publish/collect_members.py index 2970cf0e24..7cd646e0e7 100644 --- a/openpype/hosts/max/plugins/publish/collect_members.py +++ b/openpype/hosts/max/plugins/publish/collect_members.py @@ -12,7 +12,10 @@ class CollectMembers(pyblish.api.InstancePlugin): hosts = ['max'] def process(self, instance): - + if instance.data["family"] == "workfile": + self.log.debug("Skipping Actions for workfile family.") + self.log.debug("{}".format(instance.data["subset"])) + return if instance.data.get("instance_node"): container = rt.GetNodeByName(instance.data["instance_node"]) instance.data["members"] = [ diff --git a/openpype/hosts/max/plugins/publish/collect_workfile.py b/openpype/hosts/max/plugins/publish/collect_workfile.py index 0eb4bb731e..446175c0ed 100644 --- a/openpype/hosts/max/plugins/publish/collect_workfile.py +++ b/openpype/hosts/max/plugins/publish/collect_workfile.py @@ -6,15 +6,16 @@ import pyblish.api from pymxs import runtime as rt -class CollectWorkfile(pyblish.api.ContextPlugin): +class CollectWorkfile(pyblish.api.InstancePlugin): """Inject the current working file into context""" order = pyblish.api.CollectorOrder - 0.01 label = "Collect 3dsmax Workfile" hosts = ['max'] - def process(self, context): + def process(self, instance): """Inject the current working file.""" + context = instance.context folder = rt.maxFilePath file = rt.maxFileName if not folder or not file: @@ -23,15 +24,12 @@ class CollectWorkfile(pyblish.api.ContextPlugin): context.data['currentFile'] = current_file - filename, ext = os.path.splitext(file) - - task = context.data["task"] + ext = os.path.splitext(file)[-1].lstrip(".") data = {} # create instance - instance = context.create_instance(name=filename) - subset = 'workfile' + task.capitalize() + subset = instance.data["subset"] data.update({ "subset": subset, @@ -55,7 +53,7 @@ class CollectWorkfile(pyblish.api.ContextPlugin): }] instance.data.update(data) - + self.log.info('Collected data: {}'.format(data)) self.log.info('Collected instance: {}'.format(file)) self.log.info('Scene path: {}'.format(current_file)) self.log.info('staging Dir: {}'.format(folder)) From 8bac54fcf0745645489a29fc2e78bac1d9d4c284 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 7 Feb 2024 17:54:39 +0800 Subject: [PATCH 047/573] move back the subset name before dynamic data --- openpype/hosts/max/plugins/create/create_workfile.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/max/plugins/create/create_workfile.py b/openpype/hosts/max/plugins/create/create_workfile.py index b43a353136..70944b3e2c 100644 --- a/openpype/hosts/max/plugins/create/create_workfile.py +++ b/openpype/hosts/max/plugins/create/create_workfile.py @@ -38,6 +38,9 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): if current_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) + subset_name = self.get_subset_name( + variant, task_name, asset_doc, project_name, host_name + ) data = { "task": task_name, "variant": variant @@ -52,9 +55,6 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): variant, task_name, asset_doc, project_name, host_name, current_instance) ) - subset_name = self.get_subset_name( - variant, task_name, asset_doc, project_name, host_name - ) self.log.info("Auto-creating workfile instance...") instance_node = self.create_node(subset_name) data["instance_node"] = instance_node.name From af022f3561ac96b3d891b52238219ddd913d5f5c Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 7 Feb 2024 17:59:20 +0800 Subject: [PATCH 048/573] hound shut --- openpype/hosts/max/plugins/create/create_workfile.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/openpype/hosts/max/plugins/create/create_workfile.py b/openpype/hosts/max/plugins/create/create_workfile.py index 70944b3e2c..25213871d8 100644 --- a/openpype/hosts/max/plugins/create/create_workfile.py +++ b/openpype/hosts/max/plugins/create/create_workfile.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Creator plugin for creating workfiles.""" from openpype import AYON_SERVER_ENABLED -from openpype.pipeline import CreatedInstance, AutoCreator, CreatorError +from openpype.pipeline import CreatedInstance, AutoCreator from openpype.client import get_asset_by_name, get_asset_name_identifier from openpype.hosts.max.api import plugin from openpype.hosts.max.api.lib import read, imprint @@ -91,15 +91,9 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): ) self._add_instance_to_context(created_instance) - def update_instances(self, update_list): for created_inst, changes in update_list: instance_node = created_inst.get("instance_node") - new_values = { - key: changes[key].new_value - for key in changes.changed_keys - } - imprint( instance_node, created_inst.data_to_store() @@ -120,7 +114,6 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): self._remove_instance_from_context(instance) - def create_node(self, subset_name): if rt.getNodeByName(subset_name): node = rt.getNodeByName(subset_name) From 2248240306fdf01dc2f756982b11304c48304cc5 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 7 Feb 2024 18:00:08 +0800 Subject: [PATCH 049/573] hound shut --- openpype/hosts/max/plugins/create/create_workfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/max/plugins/create/create_workfile.py b/openpype/hosts/max/plugins/create/create_workfile.py index 25213871d8..4ec3c6d3ad 100644 --- a/openpype/hosts/max/plugins/create/create_workfile.py +++ b/openpype/hosts/max/plugins/create/create_workfile.py @@ -92,7 +92,7 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): self._add_instance_to_context(created_instance) def update_instances(self, update_list): - for created_inst, changes in update_list: + for created_inst, _ in update_list: instance_node = created_inst.get("instance_node") imprint( instance_node, From bd8135f4dcea13240c8a894b9f70472b7a535851 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 7 Feb 2024 18:01:15 +0800 Subject: [PATCH 050/573] hound shut --- openpype/hosts/max/plugins/create/create_workfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/max/plugins/create/create_workfile.py b/openpype/hosts/max/plugins/create/create_workfile.py index 4ec3c6d3ad..30692ccd06 100644 --- a/openpype/hosts/max/plugins/create/create_workfile.py +++ b/openpype/hosts/max/plugins/create/create_workfile.py @@ -93,7 +93,7 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): def update_instances(self, update_list): for created_inst, _ in update_list: - instance_node = created_inst.get("instance_node") + instance_node = created_inst.get("instance_node") imprint( instance_node, created_inst.data_to_store() From 05b97437b8f28bb43dc6ed303cbb6049f42c1316 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 8 Feb 2024 19:49:38 +0800 Subject: [PATCH 051/573] fix Libor's mentioned bug on action.py --- openpype/hosts/max/api/action.py | 5 ++++- .../hosts/max/plugins/publish/validate_model_name.py | 11 +++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/max/api/action.py b/openpype/hosts/max/api/action.py index 506847652b..c3c1957af1 100644 --- a/openpype/hosts/max/api/action.py +++ b/openpype/hosts/max/api/action.py @@ -31,7 +31,10 @@ class SelectInvalidAction(pyblish.api.Action): if not invalid: self.log.info("No invalid nodes found.") return - invalid_names = [obj.name for obj in invalid] + invalid_names = [obj.name for obj in invalid if isinstance(obj, str)] + if not invalid_names: + invalid_names = [obj.name for obj, _ in invalid] + invalid = [obj for obj, _ in invalid] self.log.info( "Selecting invalid objects: %s", ", ".join(invalid_names) ) diff --git a/openpype/hosts/max/plugins/publish/validate_model_name.py b/openpype/hosts/max/plugins/publish/validate_model_name.py index a36947931c..bf4f29fa97 100644 --- a/openpype/hosts/max/plugins/publish/validate_model_name.py +++ b/openpype/hosts/max/plugins/publish/validate_model_name.py @@ -3,6 +3,7 @@ import re import pyblish.api +from pymxs import runtime as rt from openpype.hosts.max.api.action import SelectInvalidAction @@ -61,26 +62,28 @@ class ValidateModelName(pyblish.api.InstancePlugin, if matched_name is None: cls.log.error("invalid model name on: {}".format(name)) cls.log.error("name doesn't match regex {}".format(regex)) - invalid.append((name, "Model name doesn't match regex")) + invalid.append((rt.getNodeByName(name), + "Model name doesn't match regex")) else: if "asset" in reg.groupindex: if matched_name.group("asset") != current_asset_name: cls.log.error( "Invalid asset name of the model {}.".format(name) ) - invalid.append((name, "Model with invalid asset name")) + invalid.append((rt.getNodeByName(name), + "Model with invalid asset name")) if "subset" in reg.groupindex: if matched_name.group("subset") != instance.name: cls.log.error( "Invalid subset name of the model {}.".format(name) ) - invalid.append((name, "Model with invalid subset name")) + invalid.append((rt.getNodeByName(name), "Model with invalid subset name")) if "project" in reg.groupindex: if matched_name.group("project") != project_name: cls.log.error( "Invalid project name of the model {}.".format(name) ) - invalid.append((name, "Model with invalid project name")) + invalid.append((rt.getNodeByName(name), "Model with invalid project name")) return invalid def process(self, instance): From c9db6449dac57cc0a60210724bac8061c9d88da1 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 8 Feb 2024 19:50:48 +0800 Subject: [PATCH 052/573] hound shut --- openpype/hosts/max/plugins/publish/validate_model_name.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_model_name.py b/openpype/hosts/max/plugins/publish/validate_model_name.py index bf4f29fa97..455c12a85b 100644 --- a/openpype/hosts/max/plugins/publish/validate_model_name.py +++ b/openpype/hosts/max/plugins/publish/validate_model_name.py @@ -77,13 +77,15 @@ class ValidateModelName(pyblish.api.InstancePlugin, cls.log.error( "Invalid subset name of the model {}.".format(name) ) - invalid.append((rt.getNodeByName(name), "Model with invalid subset name")) + invalid.append((rt.getNodeByName(name), + "Model with invalid subset name")) if "project" in reg.groupindex: if matched_name.group("project") != project_name: cls.log.error( "Invalid project name of the model {}.".format(name) ) - invalid.append((rt.getNodeByName(name), "Model with invalid project name")) + invalid.append((rt.getNodeByName(name), + "Model with invalid project name")) return invalid def process(self, instance): From 3c47798d121215505c7b0349482f7947f1b79dbe Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 8 Feb 2024 20:07:16 +0800 Subject: [PATCH 053/573] edit the action.py too regarding to #6164 --- openpype/hosts/max/api/action.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/max/api/action.py b/openpype/hosts/max/api/action.py index 506847652b..c3c1957af1 100644 --- a/openpype/hosts/max/api/action.py +++ b/openpype/hosts/max/api/action.py @@ -31,7 +31,10 @@ class SelectInvalidAction(pyblish.api.Action): if not invalid: self.log.info("No invalid nodes found.") return - invalid_names = [obj.name for obj in invalid] + invalid_names = [obj.name for obj in invalid if isinstance(obj, str)] + if not invalid_names: + invalid_names = [obj.name for obj, _ in invalid] + invalid = [obj for obj, _ in invalid] self.log.info( "Selecting invalid objects: %s", ", ".join(invalid_names) ) From e27b6e902822c7295f948b5f0e687abf344413c3 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 8 Feb 2024 20:09:40 +0800 Subject: [PATCH 054/573] change action.py as regard to #6164 --- openpype/hosts/max/api/action.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/max/api/action.py b/openpype/hosts/max/api/action.py index 506847652b..c3c1957af1 100644 --- a/openpype/hosts/max/api/action.py +++ b/openpype/hosts/max/api/action.py @@ -31,7 +31,10 @@ class SelectInvalidAction(pyblish.api.Action): if not invalid: self.log.info("No invalid nodes found.") return - invalid_names = [obj.name for obj in invalid] + invalid_names = [obj.name for obj in invalid if isinstance(obj, str)] + if not invalid_names: + invalid_names = [obj.name for obj, _ in invalid] + invalid = [obj for obj, _ in invalid] self.log.info( "Selecting invalid objects: %s", ", ".join(invalid_names) ) From 0aea2ae39f4f947716c7ef69f49bda5dd13ad5c1 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 9 Feb 2024 08:11:11 +0000 Subject: [PATCH 055/573] Add submodule --- client/ayon_core/hosts/unreal/integration | 1 + 1 file changed, 1 insertion(+) create mode 160000 client/ayon_core/hosts/unreal/integration diff --git a/client/ayon_core/hosts/unreal/integration b/client/ayon_core/hosts/unreal/integration new file mode 160000 index 0000000000..6d2793170e --- /dev/null +++ b/client/ayon_core/hosts/unreal/integration @@ -0,0 +1 @@ +Subproject commit 6d2793170ed57187842f683a943593973abcc337 From da5a0589e6a0e2af1f0971442d46a7b99cf91d97 Mon Sep 17 00:00:00 2001 From: JackP Date: Fri, 9 Feb 2024 15:50:06 +0000 Subject: [PATCH 056/573] feat: implemented has_unsaved_changes --- client/ayon_core/hosts/max/api/pipeline.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/max/api/pipeline.py b/client/ayon_core/hosts/max/api/pipeline.py index ff5ef0640b..b0c85c070b 100644 --- a/client/ayon_core/hosts/max/api/pipeline.py +++ b/client/ayon_core/hosts/max/api/pipeline.py @@ -61,8 +61,7 @@ class MaxHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): lib.check_colorspace) def has_unsaved_changes(self): - # TODO: how to get it from 3dsmax? - return True + return rt.getSaveRequired() def get_workfile_extensions(self): return [".max"] From 8bd7fde33e7fc331c268bfa4142e39e94b2298c4 Mon Sep 17 00:00:00 2001 From: JackP Date: Fri, 9 Feb 2024 15:50:43 +0000 Subject: [PATCH 057/573] feat: save_scene to use host functions to check if saving is required --- .../hosts/max/plugins/publish/save_scene.py | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/save_scene.py b/client/ayon_core/hosts/max/plugins/publish/save_scene.py index a40788ab41..37ff38bc59 100644 --- a/client/ayon_core/hosts/max/plugins/publish/save_scene.py +++ b/client/ayon_core/hosts/max/plugins/publish/save_scene.py @@ -1,11 +1,9 @@ import pyblish.api -import os +from openpype.pipeline import registered_host class SaveCurrentScene(pyblish.api.ContextPlugin): - """Save current scene - - """ + """Save current scene""" label = "Save current file" order = pyblish.api.ExtractorOrder - 0.49 @@ -13,9 +11,13 @@ class SaveCurrentScene(pyblish.api.ContextPlugin): families = ["maxrender", "workfile"] def process(self, context): - from pymxs import runtime as rt - folder = rt.maxFilePath - file = rt.maxFileName - current = os.path.join(folder, file) - assert context.data["currentFile"] == current - rt.saveMaxFile(current) + host = registered_host() + current_file = host.get_current_workfile() + + assert context.data["currentFile"] == current_file + + if host.workfile_has_unsaved_changes(): + self.log.info(f"Saving current file: {current_file}") + host.save_workfile(current_file) + else: + self.log.debug("No unsaved changes, skipping file save..")") \ No newline at end of file From 3cddbb809e581cf27f2ee562f398af99aba45b8f Mon Sep 17 00:00:00 2001 From: JackP Date: Fri, 9 Feb 2024 15:51:04 +0000 Subject: [PATCH 058/573] feat: startup.ms to restore default view menu --- client/ayon_core/hosts/max/startup/startup.ms | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ayon_core/hosts/max/startup/startup.ms b/client/ayon_core/hosts/max/startup/startup.ms index b80ead4b74..4c597901f3 100644 --- a/client/ayon_core/hosts/max/startup/startup.ms +++ b/client/ayon_core/hosts/max/startup/startup.ms @@ -8,5 +8,8 @@ local pythonpath = systemTools.getEnvVariable "MAX_PYTHONPATH" systemTools.setEnvVariable "PYTHONPATH" pythonpath + /*opens the create menu on startup to ensure users are presented with a useful default view.*/ + max create mode + python.ExecuteFile startup ) \ No newline at end of file From 1d9a4dd737b12a33a67acdf66ad75e3e9fad764d Mon Sep 17 00:00:00 2001 From: JackP Date: Fri, 9 Feb 2024 15:56:31 +0000 Subject: [PATCH 059/573] refactor: replaced has_unsaved_changes with workfile_has_unsaved_changes --- client/ayon_core/hosts/max/api/pipeline.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/hosts/max/api/pipeline.py b/client/ayon_core/hosts/max/api/pipeline.py index b0c85c070b..5f20891329 100644 --- a/client/ayon_core/hosts/max/api/pipeline.py +++ b/client/ayon_core/hosts/max/api/pipeline.py @@ -61,6 +61,7 @@ class MaxHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): lib.check_colorspace) def has_unsaved_changes(self): + def workfile_has_unsaved_changes(self): return rt.getSaveRequired() def get_workfile_extensions(self): From 57815d19a1e9e42d7abe24721b121d6eb1b5ff45 Mon Sep 17 00:00:00 2001 From: JackP Date: Fri, 9 Feb 2024 16:03:02 +0000 Subject: [PATCH 060/573] chore: removed has_unsaved_changes --- client/ayon_core/hosts/max/api/pipeline.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/hosts/max/api/pipeline.py b/client/ayon_core/hosts/max/api/pipeline.py index 5f20891329..2dc3eb6da8 100644 --- a/client/ayon_core/hosts/max/api/pipeline.py +++ b/client/ayon_core/hosts/max/api/pipeline.py @@ -60,7 +60,6 @@ class MaxHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): rt.callbacks.addScript(rt.Name('filePostOpen'), lib.check_colorspace) - def has_unsaved_changes(self): def workfile_has_unsaved_changes(self): return rt.getSaveRequired() From c608685f102d285ffcce1092ddfa8a1bd74bdd04 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 12 Feb 2024 15:06:12 +0100 Subject: [PATCH 061/573] apply changes from OpenPype PR --- client/ayon_core/tools/tray/__init__.py | 1 + client/ayon_core/tools/tray/dialogs.py | 155 +++++++++++ client/ayon_core/tools/tray/tray.py | 347 +++++++++++++++--------- 3 files changed, 378 insertions(+), 125 deletions(-) create mode 100644 client/ayon_core/tools/tray/dialogs.py diff --git a/client/ayon_core/tools/tray/__init__.py b/client/ayon_core/tools/tray/__init__.py index f5e558e0bb..49130e660a 100644 --- a/client/ayon_core/tools/tray/__init__.py +++ b/client/ayon_core/tools/tray/__init__.py @@ -1,5 +1,6 @@ from .tray import main + __all__ = ( "main", ) diff --git a/client/ayon_core/tools/tray/dialogs.py b/client/ayon_core/tools/tray/dialogs.py new file mode 100644 index 0000000000..67348284a1 --- /dev/null +++ b/client/ayon_core/tools/tray/dialogs.py @@ -0,0 +1,155 @@ +import os + +from qtpy import QtWidgets, QtCore, QtGui + +from ayon_core import resources, style +from ayon_core.tools.utils import paint_image_with_color + + +class PixmapLabel(QtWidgets.QLabel): + """Label resizing image to height of font.""" + def __init__(self, pixmap, parent): + super(PixmapLabel, self).__init__(parent) + self._empty_pixmap = QtGui.QPixmap(0, 0) + self._source_pixmap = pixmap + + def set_source_pixmap(self, pixmap): + """Change source image.""" + self._source_pixmap = pixmap + self._set_resized_pix() + + def _get_pix_size(self): + size = self.fontMetrics().height() * 3 + return size, size + + def _set_resized_pix(self): + if self._source_pixmap is None: + self.setPixmap(self._empty_pixmap) + return + width, height = self._get_pix_size() + self.setPixmap( + self._source_pixmap.scaled( + width, + height, + QtCore.Qt.KeepAspectRatio, + QtCore.Qt.SmoothTransformation + ) + ) + + def resizeEvent(self, event): + self._set_resized_pix() + super(PixmapLabel, self).resizeEvent(event) + + +class UpdateDialog(QtWidgets.QDialog): + restart_requested = QtCore.Signal() + ignore_requested = QtCore.Signal() + + _min_width = 400 + _min_height = 130 + + def __init__(self, parent=None): + super(UpdateDialog, self).__init__(parent) + + icon = QtGui.QIcon(resources.get_ayon_icon_filepath()) + self.setWindowIcon(icon) + self.setWindowTitle("AYON update") + self.setWindowFlags( + self.windowFlags() + | QtCore.Qt.WindowStaysOnTopHint + ) + + self.setMinimumWidth(self._min_width) + self.setMinimumHeight(self._min_height) + + top_widget = QtWidgets.QWidget(self) + + gift_pixmap = self._get_gift_pixmap() + gift_icon_label = PixmapLabel(gift_pixmap, top_widget) + + label_widget = QtWidgets.QLabel( + ( + "Your AYON needs to update." + "

Please restart AYON launcher and all running" + " applications as soon as possible." + ), + top_widget + ) + label_widget.setWordWrap(True) + + top_layout = QtWidgets.QHBoxLayout(top_widget) + top_layout.setSpacing(10) + top_layout.addWidget(gift_icon_label, 0, QtCore.Qt.AlignCenter) + top_layout.addWidget(label_widget, 1) + + ignore_btn = QtWidgets.QPushButton("Ignore", self) + restart_btn = QtWidgets.QPushButton("Restart && Change", self) + restart_btn.setObjectName("TrayRestartButton") + + btns_layout = QtWidgets.QHBoxLayout() + btns_layout.addStretch(1) + btns_layout.addWidget(ignore_btn, 0) + btns_layout.addWidget(restart_btn, 0) + + layout = QtWidgets.QVBoxLayout(self) + layout.addWidget(top_widget, 0) + layout.addStretch(1) + layout.addLayout(btns_layout, 0) + + ignore_btn.clicked.connect(self._on_ignore) + restart_btn.clicked.connect(self._on_reset) + + self._label_widget = label_widget + self._gift_icon_label = gift_icon_label + self._ignore_btn = ignore_btn + self._restart_btn = restart_btn + + self._restart_accepted = False + self._current_is_higher = False + + self._close_silently = False + + self.setStyleSheet(style.load_stylesheet()) + + def close_silently(self): + self._close_silently = True + self.close() + + def showEvent(self, event): + super(UpdateDialog, self).showEvent(event) + self._close_silently = False + self._restart_accepted = False + + def closeEvent(self, event): + super(UpdateDialog, self).closeEvent(event) + if self._restart_accepted or self._current_is_higher: + return + + if self._close_silently: + return + + # Trigger ignore requested only if restart was not clicked and current + # version is lower + self.ignore_requested.emit() + + def _on_ignore(self): + self.reject() + + def _on_reset(self): + self._restart_accepted = True + self.restart_requested.emit() + self.accept() + + def _get_gift_pixmap(self): + image_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "images", + "gifts.png" + ) + src_image = QtGui.QImage(image_path) + color_value = style.get_objected_colors("font") + + return paint_image_with_color( + src_image, + color_value.get_qcolor() + ) diff --git a/client/ayon_core/tools/tray/tray.py b/client/ayon_core/tools/tray/tray.py index 3a70d68466..c16f1513be 100644 --- a/client/ayon_core/tools/tray/tray.py +++ b/client/ayon_core/tools/tray/tray.py @@ -1,10 +1,11 @@ -import collections import os import sys +import collections import atexit import platform +import ayon_api from qtpy import QtCore, QtGui, QtWidgets from ayon_core import resources, style @@ -12,57 +13,24 @@ from ayon_core.lib import ( Logger, get_ayon_launcher_args, run_detached_process, + is_dev_mode_enabled, + is_staging_enabled, + is_running_from_build, ) -from ayon_core.lib import is_running_from_build from ayon_core.addon import ( ITrayAction, ITrayService, TrayAddonsManager, ) -from ayon_core.settings import get_system_settings from ayon_core.tools.utils import ( WrappedCallbackItem, get_ayon_qt_app, ) from .info_widget import InfoWidget - - -# TODO PixmapLabel should be moved to 'utils' in other future PR so should be -# imported from there -class PixmapLabel(QtWidgets.QLabel): - """Label resizing image to height of font.""" - def __init__(self, pixmap, parent): - super(PixmapLabel, self).__init__(parent) - self._empty_pixmap = QtGui.QPixmap(0, 0) - self._source_pixmap = pixmap - - def set_source_pixmap(self, pixmap): - """Change source image.""" - self._source_pixmap = pixmap - self._set_resized_pix() - - def _get_pix_size(self): - size = self.fontMetrics().height() * 3 - return size, size - - def _set_resized_pix(self): - if self._source_pixmap is None: - self.setPixmap(self._empty_pixmap) - return - width, height = self._get_pix_size() - self.setPixmap( - self._source_pixmap.scaled( - width, - height, - QtCore.Qt.KeepAspectRatio, - QtCore.Qt.SmoothTransformation - ) - ) - - def resizeEvent(self, event): - self._set_resized_pix() - super(PixmapLabel, self).resizeEvent(event) +from .dialogs import ( + UpdateDialog, +) class TrayManager: @@ -91,9 +59,13 @@ class TrayManager: self.errors = [] - self.main_thread_timer = None + self._bundle_check_timer = None + self._outdated_dialog = None + + self._main_thread_timer = None self._main_thread_callbacks = collections.deque() self._execution_in_progress = None + self._closing = False @property def doubleclick_callback(self): @@ -107,29 +79,25 @@ class TrayManager: if callback: self.execute_in_main_thread(callback) - def _restart_and_install(self): - self.restart(use_expected_version=True) + def show_tray_message(self, title, message, icon=None, msecs=None): + """Show tray message. - def execute_in_main_thread(self, callback, *args, **kwargs): - if isinstance(callback, WrappedCallbackItem): - item = callback - else: - item = WrappedCallbackItem(callback, *args, **kwargs) + Args: + title (str): Title of message. + message (str): Content of message. + icon (QSystemTrayIcon.MessageIcon): Message's icon. Default is + Information icon, may differ by Qt version. + msecs (int): Duration of message visibility in milliseconds. + Default is 10000 msecs, may differ by Qt version. + """ + args = [title, message] + kwargs = {} + if icon: + kwargs["icon"] = icon + if msecs: + kwargs["msecs"] = msecs - self._main_thread_callbacks.append(item) - - return item - - def _main_thread_execution(self): - if self._execution_in_progress: - return - self._execution_in_progress = True - for _ in range(len(self._main_thread_callbacks)): - if self._main_thread_callbacks: - item = self._main_thread_callbacks.popleft() - item.execute() - - self._execution_in_progress = False + self.tray_widget.showMessage(*args, **kwargs) def initialize_addons(self): """Add addons to tray.""" @@ -140,7 +108,9 @@ class TrayManager: self.tray_widget.menu.addMenu(admin_submenu) # Add services if they are - services_submenu = ITrayService.services_submenu(self.tray_widget.menu) + services_submenu = ITrayService.services_submenu( + self.tray_widget.menu + ) self.tray_widget.menu.addMenu(services_submenu) # Add separator @@ -165,39 +135,175 @@ class TrayManager: main_thread_timer.timeout.connect(self._main_thread_execution) main_thread_timer.start() - self.main_thread_timer = main_thread_timer + self._main_thread_timer = main_thread_timer + + version_check_timer = QtCore.QTimer() + if self._version_check_interval > 0: + version_check_timer.timeout.connect(self._on_bundle_check_timer) + version_check_timer.setInterval(self._version_check_interval) + version_check_timer.start() + self._bundle_check_timer = version_check_timer + + # For storing missing settings dialog + self._settings_validation_dialog = None self.execute_in_main_thread(self._startup_validations) + def restart(self): + """Restart Tray tool. + + First creates new process with same argument and close current tray. + """ + + self._closing = True + + args = get_ayon_launcher_args() + + # Create a copy of sys.argv + additional_args = list(sys.argv) + # Remove first argument from 'sys.argv' + # - when running from code the first argument is 'start.py' + # - when running from build the first argument is executable + additional_args.pop(0) + additional_args = [ + arg + for arg in additional_args + if arg not in {"--use-staging", "--use-dev"} + ] + + if is_dev_mode_enabled(): + additional_args.append("--use-dev") + elif is_staging_enabled(): + additional_args.append("--use-staging") + + args.extend(additional_args) + + envs = dict(os.environ.items()) + for key in { + "AYON_BUNDLE_NAME", + }: + envs.pop(key, None) + + run_detached_process(args, env=envs) + self.exit() + + def exit(self): + self._closing = True + self.tray_widget.exit() + + def on_exit(self): + self._addons_manager.on_exit() + + def execute_in_main_thread(self, callback, *args, **kwargs): + if isinstance(callback, WrappedCallbackItem): + item = callback + else: + item = WrappedCallbackItem(callback, *args, **kwargs) + + self._main_thread_callbacks.append(item) + + return item + + def _on_bundle_check_timer(self): + try: + bundles = ayon_api.get_bundles() + user = ayon_api.get_user() + # This is a workaround for bug in ayon-python-api + if user.get("code") == 401: + raise Exception("Unauthorized") + except Exception: + self._revalidate_ayon_auth() + if self._closing: + return + + try: + bundles = ayon_api.get_bundles() + except Exception: + return + + if is_dev_mode_enabled(): + return + + bundle_type = ( + "stagingBundle" + if is_staging_enabled() + else "productionBundle" + ) + + expected_bundle = bundles.get(bundle_type) + current_bundle = os.environ.get("AYON_BUNDLE_NAME") + is_expected = expected_bundle == current_bundle + if is_expected or expected_bundle is None: + self._restart_action.setVisible(False) + if ( + self._outdated_dialog is not None + and self._outdated_dialog.isVisible() + ): + self._outdated_dialog.close_silently() + return + + self._restart_action.setVisible(True) + + if self._outdated_dialog is None: + self._outdated_dialog = UpdateDialog() + self._outdated_dialog.restart_requested.connect( + self._restart_and_install + ) + self._outdated_dialog.ignore_requested.connect( + self._outdated_bundle_ignored + ) + + self._outdated_dialog.show() + self._outdated_dialog.raise_() + self._outdated_dialog.activateWindow() + + def _revalidate_ayon_auth(self): + result = self._show_ayon_login(restart_on_token_change=False) + if self._closing: + return False + + if not result.new_token: + self.exit() + return False + return True + + def _restart_and_install(self): + self.restart() + + def _outdated_bundle_ignored(self): + self.show_tray_message( + "AYON update ignored", + ( + "Please restart AYON launcher as soon as possible" + " to propagate updates." + ) + ) + + def _main_thread_execution(self): + if self._execution_in_progress: + return + self._execution_in_progress = True + for _ in range(len(self._main_thread_callbacks)): + if self._main_thread_callbacks: + item = self._main_thread_callbacks.popleft() + try: + item.execute() + except BaseException: + self.log.erorr( + "Main thread execution failed", exc_info=True + ) + + self._execution_in_progress = False + def _startup_validations(self): """Run possible startup validations.""" - pass - - def show_tray_message(self, title, message, icon=None, msecs=None): - """Show tray message. - - Args: - title (str): Title of message. - message (str): Content of message. - icon (QSystemTrayIcon.MessageIcon): Message's icon. Default is - Information icon, may differ by Qt version. - msecs (int): Duration of message visibility in milliseconds. - Default is 10000 msecs, may differ by Qt version. - """ - args = [title, message] - kwargs = {} - if icon: - kwargs["icon"] = icon - if msecs: - kwargs["msecs"] = msecs - - self.tray_widget.showMessage(*args, **kwargs) + # Trigger bundle validation on start + self._bundle_check_timer.timeout.emit() def _add_version_item(self): login_action = QtWidgets.QAction("Login", self.tray_widget) login_action.triggered.connect(self._on_ayon_login) self.tray_widget.menu.addAction(login_action) - version_string = os.getenv("AYON_VERSION", "AYON Info") version_action = QtWidgets.QAction(version_string, self.tray_widget) @@ -216,16 +322,24 @@ class TrayManager: self._restart_action = restart_action def _on_ayon_login(self): - self.execute_in_main_thread(self._show_ayon_login) + self.execute_in_main_thread( + self._show_ayon_login, + restart_on_token_change=True + ) - def _show_ayon_login(self): + def _show_ayon_login(self, restart_on_token_change): from ayon_common.connection.credentials import change_user_ui result = change_user_ui() if result.shutdown: self.exit() + return result - elif result.restart or result.token_changed: + restart = result.restart + if restart_on_token_change and result.token_changed: + restart = True + + if restart: # Remove environment variables from current connection # - keep develop, staging, headless values for key in { @@ -235,23 +349,13 @@ class TrayManager: }: os.environ.pop(key, None) self.restart() + return result def _on_restart_action(self): - self.restart(use_expected_version=True) + self.restart() - def restart(self, use_expected_version=False, reset_version=False): - """Restart Tray tool. - - First creates new process with same argument and close current tray. - - Args: - use_expected_version(bool): OpenPype version is set to expected - version. - reset_version(bool): OpenPype version is cleaned up so igniters - logic will decide which version will be used. - """ + def _restart_ayon(self): args = get_ayon_launcher_args() - envs = dict(os.environ.items()) # Create a copy of sys.argv additional_args = list(sys.argv) @@ -259,35 +363,28 @@ class TrayManager: # - when running from code the first argument is 'start.py' # - when running from build the first argument is executable additional_args.pop(0) + additional_args = [ + arg + for arg in additional_args + if arg not in {"--use-staging", "--use-dev"} + ] - cleanup_additional_args = False - if use_expected_version: - cleanup_additional_args = True - reset_version = True - - # Pop OPENPYPE_VERSION - if reset_version: - cleanup_additional_args = True - envs.pop("OPENPYPE_VERSION", None) - - if cleanup_additional_args: - _additional_args = [] - for arg in additional_args: - if arg == "--use-staging" or arg.startswith("--use-version"): - continue - _additional_args.append(arg) - additional_args = _additional_args + if is_dev_mode_enabled(): + additional_args.append("--use-dev") + elif is_staging_enabled(): + additional_args.append("--use-staging") args.extend(additional_args) + + envs = dict(os.environ.items()) + for key in { + "AYON_BUNDLE_NAME", + }: + envs.pop(key, None) + run_detached_process(args, env=envs) self.exit() - def exit(self): - self.tray_widget.exit() - - def on_exit(self): - self._addons_manager.on_exit() - def _on_version_action(self): if self._info_widget is None: self._info_widget = InfoWidget() From 5edf44903cd214d156833af6630a417c19a7ec81 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 12 Feb 2024 16:13:17 +0100 Subject: [PATCH 062/573] use AYON prefix in context environment variables --- client/ayon_core/cli_commands.py | 14 +++++- .../hooks/pre_create_extra_workdir_folders.py | 2 +- client/ayon_core/host/host.py | 6 +-- client/ayon_core/host/interfaces.py | 2 +- .../hosts/aftereffects/api/launch_logic.py | 6 +-- .../ayon_core/hosts/blender/api/pipeline.py | 6 +-- client/ayon_core/hosts/blender/api/workio.py | 2 +- client/ayon_core/hosts/flame/api/workio.py | 2 +- .../hosts/flame/plugins/load/load_clip.py | 2 +- .../flame/plugins/load/load_clip_batch.py | 2 +- client/ayon_core/hosts/fusion/addon.py | 2 +- client/ayon_core/hosts/fusion/api/pipeline.py | 2 +- client/ayon_core/hosts/fusion/api/plugin.py | 2 +- .../fusion/hooks/pre_fusion_profile_hook.py | 2 +- .../hosts/fusion/hooks/pre_fusion_setup.py | 2 +- client/ayon_core/hosts/harmony/api/workio.py | 2 +- .../publish/validate_scene_settings.py | 2 +- client/ayon_core/hosts/hiero/api/workio.py | 2 +- .../hosts/houdini/hooks/set_paths.py | 2 +- client/ayon_core/hosts/max/hooks/set_paths.py | 2 +- client/ayon_core/hosts/maya/api/pipeline.py | 6 +-- .../maya/api/shader_definition_editor.py | 2 +- client/ayon_core/hosts/maya/api/workio.py | 2 +- .../hosts/maya/hooks/pre_copy_mel.py | 2 +- .../maya/plugins/publish/collect_render.py | 2 +- .../ayon_core/hosts/maya/startup/userSetup.py | 2 +- client/ayon_core/hosts/nuke/api/lib.py | 8 ++-- client/ayon_core/hosts/nuke/api/workio.py | 2 +- .../hosts/nuke/startup/custom_write_node.py | 2 +- .../ayon_core/hosts/photoshop/api/pipeline.py | 2 +- .../plugins/publish/collect_batch_data.py | 8 ++-- client/ayon_core/hosts/resolve/api/workio.py | 4 +- .../hosts/traypublisher/api/pipeline.py | 4 +- .../plugins/create/create_from_settings.py | 2 +- .../ayon_core/hosts/tvpaint/api/pipeline.py | 6 +-- .../plugins/publish/collect_workfile_data.py | 4 +- .../publish/validate_workfile_project_name.py | 2 +- .../ayon_core/hosts/unreal/api/rendering.py | 2 +- .../unreal/hooks/pre_workfile_preparation.py | 2 +- client/ayon_core/lib/applications.py | 14 +++--- client/ayon_core/lib/log.py | 2 +- .../launcher_actions/ClockifyStart.py | 8 ++-- .../clockify/launcher_actions/ClockifySync.py | 2 +- .../publish/submit_aftereffects_deadline.py | 10 ++-- .../publish/submit_blender_deadline.py | 10 ++-- .../plugins/publish/submit_fusion_deadline.py | 10 ++-- .../publish/submit_harmony_deadline.py | 10 ++-- .../publish/submit_houdini_cache_deadline.py | 10 ++-- .../publish/submit_houdini_render_deadline.py | 10 ++-- .../plugins/publish/submit_max_deadline.py | 10 ++-- .../plugins/publish/submit_maya_deadline.py | 10 ++-- .../submit_maya_remote_publish_deadline.py | 8 ++-- .../plugins/publish/submit_nuke_deadline.py | 8 ++-- .../publish/submit_publish_cache_job.py | 8 ++-- .../plugins/publish/submit_publish_job.py | 8 ++-- .../custom/plugins/GlobalJobPreLoad.py | 21 +++++--- client/ayon_core/modules/royalrender/lib.py | 4 +- .../publish/create_publish_royalrender_job.py | 8 ++-- .../perjob/m50__openpype_publish_render.py | 24 ++++++---- client/ayon_core/pipeline/actions.py | 2 +- client/ayon_core/pipeline/anatomy.py | 2 +- client/ayon_core/pipeline/context_tools.py | 48 +++++++++---------- client/ayon_core/pipeline/create/context.py | 2 +- .../pipeline/create/legacy_create.py | 2 +- .../ayon_core/pipeline/create/subset_name.py | 4 +- .../pipeline/farm/pyblish_functions.py | 6 +-- client/ayon_core/pipeline/load/plugins.py | 2 +- client/ayon_core/pipeline/publish/lib.py | 2 +- .../pipeline/workfile/build_workfile.py | 4 +- .../pipeline/workfile/path_resolving.py | 2 +- .../workfile/workfile_template_builder.py | 10 ++-- .../plugins/actions/open_file_explorer.py | 8 ++-- .../plugins/load/delete_old_versions.py | 2 +- .../publish/collect_anatomy_context_data.py | 2 +- .../publish/collect_context_entities.py | 1 - .../publish/collect_from_create_context.py | 6 +-- .../plugins/publish/collect_host_name.py | 8 ++-- .../plugins/publish/collect_rendered_files.py | 6 +-- .../scripts/non_python_host_launch.py | 4 +- client/ayon_core/settings/lib.py | 6 +-- .../tools/experimental_tools/tools_def.py | 2 +- .../tools/launcher/models/actions.py | 18 ++++--- client/ayon_core/tools/publisher/control.py | 4 +- client/ayon_core/tools/texture_copy/app.py | 4 +- .../tools/workfile_template_build/window.py | 2 +- 85 files changed, 257 insertions(+), 225 deletions(-) diff --git a/client/ayon_core/cli_commands.py b/client/ayon_core/cli_commands.py index c07b72afdf..31429c488b 100644 --- a/client/ayon_core/cli_commands.py +++ b/client/ayon_core/cli_commands.py @@ -73,6 +73,18 @@ class Commands: import pyblish.api import pyblish.util + # Fix older jobs + for src_key, dst_key in ( + ("AVALON_PROJECT", "AYON_PROJECT_NAME"), + ("AVALON_ASSET", "AYON_FOLDER_PATH"), + ("AVALON_TASK", "AYON_TASK_NAME"), + ("AVALON_WORKDIR", "AYON_WORKDIR"), + ("AVALON_APP_NAME", "AYON_APP_NAME"), + ("AVALON_APP", "AYON_HOST_NAME"), + ): + if src_key in os.environ and dst_key not in os.environ: + os.environ[dst_key] = os.environ[src_key] + log = Logger.get_logger("CLI-publish") install_ayon_plugins() @@ -87,7 +99,7 @@ class Commands: if not any(paths): raise RuntimeError("No publish paths specified") - app_full_name = os.getenv("AVALON_APP_NAME") + app_full_name = os.getenv("AYON_APP_NAME") if app_full_name: context = get_global_context() env = get_app_environments_for_context( diff --git a/client/ayon_core/hooks/pre_create_extra_workdir_folders.py b/client/ayon_core/hooks/pre_create_extra_workdir_folders.py index 6116d5fbd3..72c6bf2f68 100644 --- a/client/ayon_core/hooks/pre_create_extra_workdir_folders.py +++ b/client/ayon_core/hooks/pre_create_extra_workdir_folders.py @@ -21,7 +21,7 @@ class CreateWorkdirExtraFolders(PreLaunchHook): return env = self.data.get("env") or {} - workdir = env.get("AVALON_WORKDIR") + workdir = env.get("AYON_WORKDIR") if not workdir or not os.path.exists(workdir): return diff --git a/client/ayon_core/host/host.py b/client/ayon_core/host/host.py index 2dd98c8126..51dff9d558 100644 --- a/client/ayon_core/host/host.py +++ b/client/ayon_core/host/host.py @@ -106,7 +106,7 @@ class HostBase(object): Union[str, None]: Current project name. """ - return os.environ.get("AVALON_PROJECT") + return os.environ.get("AYON_PROJECT_NAME") def get_current_asset_name(self): """ @@ -114,7 +114,7 @@ class HostBase(object): Union[str, None]: Current asset name. """ - return os.environ.get("AVALON_ASSET") + return os.environ.get("AYON_FOLDER_PATH") def get_current_task_name(self): """ @@ -122,7 +122,7 @@ class HostBase(object): Union[str, None]: Current task name. """ - return os.environ.get("AVALON_TASK") + return os.environ.get("AYON_TASK_NAME") def get_current_context(self): """Get current context information. diff --git a/client/ayon_core/host/interfaces.py b/client/ayon_core/host/interfaces.py index 7c6057acf0..7157ad6f7e 100644 --- a/client/ayon_core/host/interfaces.py +++ b/client/ayon_core/host/interfaces.py @@ -234,7 +234,7 @@ class IWorkfileHost: str: Path to new workdir. """ - return session["AVALON_WORKDIR"] + return session["AYON_WORKDIR"] # --- Deprecated method names --- def file_extensions(self): diff --git a/client/ayon_core/hosts/aftereffects/api/launch_logic.py b/client/ayon_core/hosts/aftereffects/api/launch_logic.py index 3d09f4d53c..8c54908fe5 100644 --- a/client/ayon_core/hosts/aftereffects/api/launch_logic.py +++ b/client/ayon_core/hosts/aftereffects/api/launch_logic.py @@ -298,11 +298,11 @@ class AfterEffectsRoute(WebSocketRoute): log.info("Setting context change") log.info("project {} asset {} ".format(project, asset)) if project: - os.environ["AVALON_PROJECT"] = project + os.environ["AYON_PROJECT_NAME"] = project if asset: - os.environ["AVALON_ASSET"] = asset + os.environ["AYON_FOLDER_PATH"] = asset if task: - os.environ["AVALON_TASK"] = task + os.environ["AYON_TASK_NAME"] = task async def read(self): log.debug("aftereffects.read client calls server server calls " diff --git a/client/ayon_core/hosts/blender/api/pipeline.py b/client/ayon_core/hosts/blender/api/pipeline.py index 77731a0fd3..a49afeea6b 100644 --- a/client/ayon_core/hosts/blender/api/pipeline.py +++ b/client/ayon_core/hosts/blender/api/pipeline.py @@ -272,7 +272,7 @@ def set_resolution(data): def on_new(): - project = os.environ.get("AVALON_PROJECT") + project = os.environ.get("AYON_PROJECT_NAME") settings = get_project_settings(project).get("blender") set_resolution_startup = settings.get("set_resolution_startup") @@ -293,7 +293,7 @@ def on_new(): def on_open(): - project = os.environ.get("AVALON_PROJECT") + project = os.environ.get("AYON_PROJECT_NAME") settings = get_project_settings(project).get("blender") set_resolution_startup = settings.get("set_resolution_startup") @@ -379,7 +379,7 @@ def _on_task_changed(): # `directory` attribute, so it opens in that directory (does it?). # https://docs.blender.org/api/blender2.8/bpy.types.Operator.html#calling-a-file-selector # https://docs.blender.org/api/blender2.8/bpy.types.WindowManager.html#bpy.types.WindowManager.fileselect_add - workdir = os.getenv("AVALON_WORKDIR") + workdir = os.getenv("AYON_WORKDIR") log.debug("New working directory: %s", workdir) diff --git a/client/ayon_core/hosts/blender/api/workio.py b/client/ayon_core/hosts/blender/api/workio.py index a8f6193abc..e0f333843a 100644 --- a/client/ayon_core/hosts/blender/api/workio.py +++ b/client/ayon_core/hosts/blender/api/workio.py @@ -82,7 +82,7 @@ def file_extensions() -> List[str]: def work_root(session: dict) -> str: """Return the default root to browse for work files.""" - work_dir = session["AVALON_WORKDIR"] + work_dir = session["AYON_WORKDIR"] scene_dir = session.get("AVALON_SCENEDIR") if scene_dir: return str(Path(work_dir, scene_dir)) diff --git a/client/ayon_core/hosts/flame/api/workio.py b/client/ayon_core/hosts/flame/api/workio.py index 0e3cb7f5fd..eef10a4847 100644 --- a/client/ayon_core/hosts/flame/api/workio.py +++ b/client/ayon_core/hosts/flame/api/workio.py @@ -34,4 +34,4 @@ def current_file(): def work_root(session): - return os.path.normpath(session["AVALON_WORKDIR"]).replace("\\", "/") + return os.path.normpath(session["AYON_WORKDIR"]).replace("\\", "/") diff --git a/client/ayon_core/hosts/flame/plugins/load/load_clip.py b/client/ayon_core/hosts/flame/plugins/load/load_clip.py index 6f35196932..47d0331255 100644 --- a/client/ayon_core/hosts/flame/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/flame/plugins/load/load_clip.py @@ -70,7 +70,7 @@ class LoadClip(opfapi.ClipLoader): self.log.info("Loading with colorspace: `{}`".format(colorspace)) # create workfile path - workfile_dir = os.environ["AVALON_WORKDIR"] + workfile_dir = os.environ["AYON_WORKDIR"] openclip_dir = os.path.join( workfile_dir, clip_name ) diff --git a/client/ayon_core/hosts/flame/plugins/load/load_clip_batch.py b/client/ayon_core/hosts/flame/plugins/load/load_clip_batch.py index a66bf53622..46ed9ef58b 100644 --- a/client/ayon_core/hosts/flame/plugins/load/load_clip_batch.py +++ b/client/ayon_core/hosts/flame/plugins/load/load_clip_batch.py @@ -69,7 +69,7 @@ class LoadClipBatch(opfapi.ClipLoader): self.log.info("Loading with colorspace: `{}`".format(colorspace)) # create workfile path - workfile_dir = options.get("workdir") or os.environ["AVALON_WORKDIR"] + workfile_dir = options.get("workdir") or os.environ["AYON_WORKDIR"] openclip_dir = os.path.join( workfile_dir, clip_name ) diff --git a/client/ayon_core/hosts/fusion/addon.py b/client/ayon_core/hosts/fusion/addon.py index 391ee770c4..54e48ea7bf 100644 --- a/client/ayon_core/hosts/fusion/addon.py +++ b/client/ayon_core/hosts/fusion/addon.py @@ -22,7 +22,7 @@ def get_fusion_version(app_name): The function is triggered by the prelaunch hooks to get the fusion version. `app_name` is obtained by prelaunch hooks from the - `launch_context.env.get("AVALON_APP_NAME")`. + `launch_context.env.get("AYON_APP_NAME")`. To get a correct Fusion version, a version number should be present in the `applications/fusion/variants` key diff --git a/client/ayon_core/hosts/fusion/api/pipeline.py b/client/ayon_core/hosts/fusion/api/pipeline.py index 7c480704a5..0e9e0724c7 100644 --- a/client/ayon_core/hosts/fusion/api/pipeline.py +++ b/client/ayon_core/hosts/fusion/api/pipeline.py @@ -135,7 +135,7 @@ class FusionHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): return current_filepath def work_root(self, session): - work_dir = session["AVALON_WORKDIR"] + work_dir = session["AYON_WORKDIR"] scene_dir = session.get("AVALON_SCENEDIR") if scene_dir: return os.path.join(work_dir, scene_dir) diff --git a/client/ayon_core/hosts/fusion/api/plugin.py b/client/ayon_core/hosts/fusion/api/plugin.py index 80e194ea68..0d9409b4f5 100644 --- a/client/ayon_core/hosts/fusion/api/plugin.py +++ b/client/ayon_core/hosts/fusion/api/plugin.py @@ -135,7 +135,7 @@ class GenericCreateSaver(Creator): ext = data["creator_attributes"]["image_format"] # Subset change detected - workdir = os.path.normpath(os.getenv("AVALON_WORKDIR")) + workdir = os.path.normpath(os.getenv("AYON_WORKDIR")) formatting_data.update({ "workdir": workdir, "frame": "0" * frame_padding, diff --git a/client/ayon_core/hosts/fusion/hooks/pre_fusion_profile_hook.py b/client/ayon_core/hosts/fusion/hooks/pre_fusion_profile_hook.py index f63aaa1eb4..5aa2783129 100644 --- a/client/ayon_core/hosts/fusion/hooks/pre_fusion_profile_hook.py +++ b/client/ayon_core/hosts/fusion/hooks/pre_fusion_profile_hook.py @@ -131,7 +131,7 @@ class FusionCopyPrefsPrelaunch(PreLaunchHook): ) = self.get_copy_fusion_prefs_settings() # Get launched application context and return correct app version - app_name = self.launch_context.env.get("AVALON_APP_NAME") + app_name = self.launch_context.env.get("AYON_APP_NAME") app_version = get_fusion_version(app_name) if app_version is None: version_names = ", ".join(str(x) for x in FUSION_VERSIONS_DICT) diff --git a/client/ayon_core/hosts/fusion/hooks/pre_fusion_setup.py b/client/ayon_core/hosts/fusion/hooks/pre_fusion_setup.py index 7cfa9d0a26..7eaf2ddc02 100644 --- a/client/ayon_core/hosts/fusion/hooks/pre_fusion_setup.py +++ b/client/ayon_core/hosts/fusion/hooks/pre_fusion_setup.py @@ -28,7 +28,7 @@ class FusionPrelaunch(PreLaunchHook): def execute(self): # making sure python 3 is installed at provided path # Py 3.3-3.10 for Fusion 18+ or Py 3.6 for Fu 16-17 - app_data = self.launch_context.env.get("AVALON_APP_NAME") + app_data = self.launch_context.env.get("AYON_APP_NAME") app_version = get_fusion_version(app_data) if not app_version: raise ApplicationLaunchFailed( diff --git a/client/ayon_core/hosts/harmony/api/workio.py b/client/ayon_core/hosts/harmony/api/workio.py index 8df5ede917..1f95148e75 100644 --- a/client/ayon_core/hosts/harmony/api/workio.py +++ b/client/ayon_core/hosts/harmony/api/workio.py @@ -74,4 +74,4 @@ def current_file(): def work_root(session): - return os.path.normpath(session["AVALON_WORKDIR"]).replace("\\", "/") + return os.path.normpath(session["AYON_WORKDIR"]).replace("\\", "/") diff --git a/client/ayon_core/hosts/harmony/plugins/publish/validate_scene_settings.py b/client/ayon_core/hosts/harmony/plugins/publish/validate_scene_settings.py index 0cf96e70b0..6d46fbcd33 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/validate_scene_settings.py +++ b/client/ayon_core/hosts/harmony/plugins/publish/validate_scene_settings.py @@ -77,7 +77,7 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin): expected_settings.pop("resolutionWidth") expected_settings.pop("resolutionHeight") - if (any(re.search(pattern, os.getenv('AVALON_TASK')) + if (any(re.search(pattern, os.getenv('AYON_TASK_NAME')) for pattern in self.skip_timelines_check)): self.log.info("Skipping frames check because of " "task name and pattern {}".format( diff --git a/client/ayon_core/hosts/hiero/api/workio.py b/client/ayon_core/hosts/hiero/api/workio.py index 14d9439344..4c2416ca38 100644 --- a/client/ayon_core/hosts/hiero/api/workio.py +++ b/client/ayon_core/hosts/hiero/api/workio.py @@ -70,4 +70,4 @@ def current_file(): def work_root(session): - return os.path.normpath(session["AVALON_WORKDIR"]).replace("\\", "/") + return os.path.normpath(session["AYON_WORKDIR"]).replace("\\", "/") diff --git a/client/ayon_core/hosts/houdini/hooks/set_paths.py b/client/ayon_core/hosts/houdini/hooks/set_paths.py index 1f24a8dd7d..7eb346cc74 100644 --- a/client/ayon_core/hosts/houdini/hooks/set_paths.py +++ b/client/ayon_core/hosts/houdini/hooks/set_paths.py @@ -10,7 +10,7 @@ class SetPath(PreLaunchHook): launch_types = {LaunchTypes.local} def execute(self): - workdir = self.launch_context.env.get("AVALON_WORKDIR", "") + workdir = self.launch_context.env.get("AYON_WORKDIR", "") if not workdir: self.log.warning("BUG: Workdir is not filled.") return diff --git a/client/ayon_core/hosts/max/hooks/set_paths.py b/client/ayon_core/hosts/max/hooks/set_paths.py index c18fd29295..0ee1b0dab7 100644 --- a/client/ayon_core/hosts/max/hooks/set_paths.py +++ b/client/ayon_core/hosts/max/hooks/set_paths.py @@ -10,7 +10,7 @@ class SetPath(PreLaunchHook): launch_types = {LaunchTypes.local} def execute(self): - workdir = self.launch_context.env.get("AVALON_WORKDIR", "") + workdir = self.launch_context.env.get("AYON_WORKDIR", "") if not workdir: self.log.warning("BUG: Workdir is not filled.") return diff --git a/client/ayon_core/hosts/maya/api/pipeline.py b/client/ayon_core/hosts/maya/api/pipeline.py index dc6353618a..e58316030e 100644 --- a/client/ayon_core/hosts/maya/api/pipeline.py +++ b/client/ayon_core/hosts/maya/api/pipeline.py @@ -246,7 +246,7 @@ def _set_project(): None """ - workdir = os.getenv("AVALON_WORKDIR") + workdir = os.getenv("AYON_WORKDIR") try: os.makedirs(workdir) @@ -628,7 +628,7 @@ def on_task_changed(): # Run menu.update_menu_task_label() - workdir = os.getenv("AVALON_WORKDIR") + workdir = os.getenv("AYON_WORKDIR") if os.path.exists(workdir): log.info("Updating Maya workspace for task change to %s", workdir) _set_project() @@ -677,7 +677,7 @@ def workfile_save_before_xgen(event): import xgenm - current_work_dir = os.getenv("AVALON_WORKDIR").replace("\\", "/") + current_work_dir = os.getenv("AYON_WORKDIR").replace("\\", "/") expected_work_dir = event.data["workdir_path"].replace("\\", "/") if current_work_dir == expected_work_dir: return diff --git a/client/ayon_core/hosts/maya/api/shader_definition_editor.py b/client/ayon_core/hosts/maya/api/shader_definition_editor.py index 04e8dded6f..bfa531eb87 100644 --- a/client/ayon_core/hosts/maya/api/shader_definition_editor.py +++ b/client/ayon_core/hosts/maya/api/shader_definition_editor.py @@ -12,7 +12,7 @@ import gridfs DEFINITION_FILENAME = "{}/maya/shader_definition.txt".format( - os.getenv("AVALON_PROJECT")) + os.getenv("AYON_PROJECT_NAME")) class ShaderDefinitionsEditor(QtWidgets.QWidget): diff --git a/client/ayon_core/hosts/maya/api/workio.py b/client/ayon_core/hosts/maya/api/workio.py index 8c31974c73..ff6c11eb4f 100644 --- a/client/ayon_core/hosts/maya/api/workio.py +++ b/client/ayon_core/hosts/maya/api/workio.py @@ -35,7 +35,7 @@ def current_file(): def work_root(session): - work_dir = session["AVALON_WORKDIR"] + work_dir = session["AYON_WORKDIR"] scene_dir = None # Query scene file rule from workspace.mel if it exists in WORKDIR diff --git a/client/ayon_core/hosts/maya/hooks/pre_copy_mel.py b/client/ayon_core/hosts/maya/hooks/pre_copy_mel.py index 7198f98131..03ca8661bd 100644 --- a/client/ayon_core/hosts/maya/hooks/pre_copy_mel.py +++ b/client/ayon_core/hosts/maya/hooks/pre_copy_mel.py @@ -12,7 +12,7 @@ class PreCopyMel(PreLaunchHook): def execute(self): project_doc = self.data["project_doc"] - workdir = self.launch_context.env.get("AVALON_WORKDIR") + workdir = self.launch_context.env.get("AYON_WORKDIR") if not workdir: self.log.warning("BUG: Workdir is not filled.") return diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_render.py b/client/ayon_core/hosts/maya/plugins/publish/collect_render.py index e4221a091c..7daab19f4a 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/collect_render.py +++ b/client/ayon_core/hosts/maya/plugins/publish/collect_render.py @@ -13,7 +13,7 @@ Requires: context -> workspaceDir context -> user - session -> AVALON_ASSET + session -> AYON_FOLDER_PATH Optional: diff --git a/client/ayon_core/hosts/maya/startup/userSetup.py b/client/ayon_core/hosts/maya/startup/userSetup.py index 882f2df27c..22b776915c 100644 --- a/client/ayon_core/hosts/maya/startup/userSetup.py +++ b/client/ayon_core/hosts/maya/startup/userSetup.py @@ -38,7 +38,7 @@ if explicit_plugins_loading["enabled"]: key = "AYON_OPEN_WORKFILE_POST_INITIALIZATION" if bool(int(os.environ.get(key, "0"))): def _log_and_open(): - path = os.environ["AVALON_LAST_WORKFILE"] + path = os.environ["AYON_LAST_WORKFILE"] print("Opening \"{}\"".format(path)) cmds.file(path, open=True, force=True) cmds.evalDeferred( diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index 2ac33de68e..c320df9361 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -120,7 +120,7 @@ def deprecated(new_destination): class Context: main_window = None context_action_item = None - project_name = os.getenv("AVALON_PROJECT") + project_name = os.getenv("AYON_PROJECT_NAME") # Workfile related code workfiles_launched = False workfiles_tool_timer = None @@ -2605,7 +2605,7 @@ Reopening Nuke should synchronize these paths and resolve any discrepancies. def set_favorites(self): from .utils import set_context_favorites - work_dir = os.getenv("AVALON_WORKDIR") + work_dir = os.getenv("AYON_WORKDIR") asset = get_current_asset_name() favorite_items = OrderedDict() @@ -2953,7 +2953,7 @@ def process_workfile_builder(): create_fv_on = workfile_builder.get("create_first_version") or None builder_on = workfile_builder.get("builder_on_start") or None - last_workfile_path = os.environ.get("AVALON_LAST_WORKFILE") + last_workfile_path = os.environ.get("AYON_LAST_WORKFILE") # generate first version in file not existing and feature is enabled if create_fv_on and not os.path.exists(last_workfile_path): @@ -3203,7 +3203,7 @@ class DirmapCache: @classmethod def project_name(cls): if cls._project_name is None: - cls._project_name = os.getenv("AVALON_PROJECT") + cls._project_name = os.getenv("AYON_PROJECT_NAME") return cls._project_name @classmethod diff --git a/client/ayon_core/hosts/nuke/api/workio.py b/client/ayon_core/hosts/nuke/api/workio.py index 98e59eff71..b2445fd3d2 100644 --- a/client/ayon_core/hosts/nuke/api/workio.py +++ b/client/ayon_core/hosts/nuke/api/workio.py @@ -68,7 +68,7 @@ def current_file(): def work_root(session): - work_dir = session["AVALON_WORKDIR"] + work_dir = session["AYON_WORKDIR"] scene_dir = session.get("AVALON_SCENEDIR") if scene_dir: path = os.path.join(work_dir, scene_dir) diff --git a/client/ayon_core/hosts/nuke/startup/custom_write_node.py b/client/ayon_core/hosts/nuke/startup/custom_write_node.py index 01e255d0c0..89dfde297c 100644 --- a/client/ayon_core/hosts/nuke/startup/custom_write_node.py +++ b/client/ayon_core/hosts/nuke/startup/custom_write_node.py @@ -112,7 +112,7 @@ class WriteNodeKnobSettingPanel(nukescripts.PythonPanel): for write_node in write_selected_nodes: # data for mapping the path data = { - "work": os.getenv("AVALON_WORKDIR"), + "work": os.getenv("AYON_WORKDIR"), "subset": write_node["name"].value(), "frame": "#" * frame_padding, "ext": ext diff --git a/client/ayon_core/hosts/photoshop/api/pipeline.py b/client/ayon_core/hosts/photoshop/api/pipeline.py index 046ec8e6ee..ebde175053 100644 --- a/client/ayon_core/hosts/photoshop/api/pipeline.py +++ b/client/ayon_core/hosts/photoshop/api/pipeline.py @@ -62,7 +62,7 @@ class PhotoshopHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): return None def work_root(self, session): - return os.path.normpath(session["AVALON_WORKDIR"]).replace("\\", "/") + return os.path.normpath(session["AYON_WORKDIR"]).replace("\\", "/") def open_workfile(self, filepath): lib.stub().open(filepath) diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_batch_data.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_batch_data.py index 6639040bd7..0ed891c8cf 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_batch_data.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_batch_data.py @@ -52,10 +52,10 @@ class CollectBatchData(pyblish.api.ContextPlugin): assert os.path.exists(batch_dir), \ "Folder {} doesn't exist".format(batch_dir) - project_name = os.environ.get("AVALON_PROJECT") + project_name = os.environ.get("AYON_PROJECT_NAME") if project_name is None: raise AssertionError( - "Environment `AVALON_PROJECT` was not found." + "Environment `AYON_PROJECT_NAME` was not found." "Could not set project `root` which may cause issues." ) @@ -68,8 +68,8 @@ class CollectBatchData(pyblish.api.ContextPlugin): batch_data["context"] ) - os.environ["AVALON_ASSET"] = asset_name - os.environ["AVALON_TASK"] = task_name + os.environ["AYON_FOLDER_PATH"] = asset_name + os.environ["AYON_TASK_NAME"] = task_name context.data["asset"] = asset_name context.data["task"] = task_name diff --git a/client/ayon_core/hosts/resolve/api/workio.py b/client/ayon_core/hosts/resolve/api/workio.py index 5e4865ddc5..b6c2f63432 100644 --- a/client/ayon_core/hosts/resolve/api/workio.py +++ b/client/ayon_core/hosts/resolve/api/workio.py @@ -79,7 +79,7 @@ def open_file(filepath): def current_file(): pm = get_project_manager() file_ext = file_extensions()[0] - workdir_path = os.getenv("AVALON_WORKDIR") + workdir_path = os.getenv("AYON_WORKDIR") project = pm.GetCurrentProject() project_name = project.GetName() file_name = project_name + file_ext @@ -93,4 +93,4 @@ def current_file(): def work_root(session): - return os.path.normpath(session["AVALON_WORKDIR"]).replace("\\", "/") + return os.path.normpath(session["AYON_WORKDIR"]).replace("\\", "/") diff --git a/client/ayon_core/hosts/traypublisher/api/pipeline.py b/client/ayon_core/hosts/traypublisher/api/pipeline.py index 88fa3239a5..f4526ddf4b 100644 --- a/client/ayon_core/hosts/traypublisher/api/pipeline.py +++ b/client/ayon_core/hosts/traypublisher/api/pipeline.py @@ -22,7 +22,7 @@ class TrayPublisherHost(HostBase, IPublishHost): name = "traypublisher" def install(self): - os.environ["AVALON_APP"] = self.name + os.environ["AYON_HOST_NAME"] = self.name pyblish.api.register_host("traypublisher") pyblish.api.register_plugin_path(PUBLISH_PATH) @@ -40,7 +40,7 @@ class TrayPublisherHost(HostBase, IPublishHost): def set_project_name(self, project_name): # TODO Deregister project specific plugins and register new project # plugins - os.environ["AVALON_PROJECT"] = project_name + os.environ["AYON_PROJECT_NAME"] = project_name HostContext.set_project_name(project_name) diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_from_settings.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_from_settings.py index 20f8dd792a..cc1429901d 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_from_settings.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_from_settings.py @@ -8,7 +8,7 @@ log = Logger.get_logger(__name__) def initialize(): from ayon_core.hosts.traypublisher.api.plugin import SettingsCreator - project_name = os.environ["AVALON_PROJECT"] + project_name = os.environ["AYON_PROJECT_NAME"] project_settings = get_project_settings(project_name) simple_creators = project_settings["traypublisher"]["simple_creators"] diff --git a/client/ayon_core/hosts/tvpaint/api/pipeline.py b/client/ayon_core/hosts/tvpaint/api/pipeline.py index d636e68cfa..78a0c5270d 100644 --- a/client/ayon_core/hosts/tvpaint/api/pipeline.py +++ b/client/ayon_core/hosts/tvpaint/api/pipeline.py @@ -68,7 +68,7 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): log.info("AYON - Installing TVPaint integration") # Create workdir folder if does not exist yet - workdir = os.getenv("AVALON_WORKDIR") + workdir = os.getenv("AYON_WORKDIR") if not os.path.exists(workdir): os.makedirs(workdir) @@ -155,7 +155,7 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): return execute_george(george_script) def work_root(self, session): - return session["AVALON_WORKDIR"] + return session["AYON_WORKDIR"] def get_current_workfile(self): return execute_george("tv_GetProjectName") @@ -174,7 +174,7 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): # Setup project settings if its the template that's launched. # TODO also check for template creation when it's possible to define # templates - last_workfile = os.environ.get("AVALON_LAST_WORKFILE") + last_workfile = os.environ.get("AYON_LAST_WORKFILE") if not last_workfile or os.path.exists(last_workfile): return diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile_data.py b/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile_data.py index 05ceb143e9..a6b6f05dc9 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile_data.py +++ b/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile_data.py @@ -85,8 +85,8 @@ class CollectWorkfileData(pyblish.api.ContextPlugin): if workfile_context: # Change current context with context from workfile key_map = ( - ("AVALON_ASSET", "asset_name"), - ("AVALON_TASK", "task_name") + ("AYON_FOLDER_PATH", "asset_name"), + ("AYON_TASK_NAME", "task_name") ) for env_key, key in key_map: os.environ[env_key] = workfile_context[key] diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py b/client/ayon_core/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py index be3259bfd8..5b42842717 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py +++ b/client/ayon_core/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py @@ -6,7 +6,7 @@ class ValidateWorkfileProjectName(pyblish.api.ContextPlugin): """Validate project name stored in workfile metadata. It is not possible to publish from different project than is set in - environment variable "AVALON_PROJECT". + environment variable "AYON_PROJECT_NAME". """ label = "Validate Workfile Project Name" diff --git a/client/ayon_core/hosts/unreal/api/rendering.py b/client/ayon_core/hosts/unreal/api/rendering.py index 8717788732..4a15ceb89a 100644 --- a/client/ayon_core/hosts/unreal/api/rendering.py +++ b/client/ayon_core/hosts/unreal/api/rendering.py @@ -60,7 +60,7 @@ def start_rendering(): inst_data.append(data) try: - project = os.environ.get("AVALON_PROJECT") + project = os.environ.get("AYON_PROJECT_NAME") anatomy = Anatomy(project) root = anatomy.roots['renders'] except Exception as e: diff --git a/client/ayon_core/hosts/unreal/hooks/pre_workfile_preparation.py b/client/ayon_core/hosts/unreal/hooks/pre_workfile_preparation.py index 4317844ca5..0eaa1adb84 100644 --- a/client/ayon_core/hosts/unreal/hooks/pre_workfile_preparation.py +++ b/client/ayon_core/hosts/unreal/hooks/pre_workfile_preparation.py @@ -146,7 +146,7 @@ class UnrealPrelaunchHook(PreLaunchHook): def execute(self): """Hook entry method.""" - workdir = self.launch_context.env["AVALON_WORKDIR"] + workdir = self.launch_context.env["AYON_WORKDIR"] executable = str(self.launch_context.executable) engine_version = self.app_name.split("/")[-1].replace("-", ".") try: diff --git a/client/ayon_core/lib/applications.py b/client/ayon_core/lib/applications.py index febdaacdd1..38a29d7991 100644 --- a/client/ayon_core/lib/applications.py +++ b/client/ayon_core/lib/applications.py @@ -1740,15 +1740,15 @@ def prepare_context_environments(data, env_group=None, addons_manager=None): app = data["app"] context_env = { - "AVALON_PROJECT": project_doc["name"], - "AVALON_APP_NAME": app.full_name + "AYON_PROJECT_NAME": project_doc["name"], + "AYON_APP_NAME": app.full_name } if asset_doc: asset_name = get_asset_name_identifier(asset_doc) - context_env["AVALON_ASSET"] = asset_name + context_env["AYON_FOLDER_PATH"] = asset_name if task_name: - context_env["AVALON_TASK"] = task_name + context_env["AYON_TASK_NAME"] = task_name log.debug( "Context environments set:\n{}".format( @@ -1766,7 +1766,7 @@ def prepare_context_environments(data, env_group=None, addons_manager=None): if not app.is_host: return - data["env"]["AVALON_APP"] = app.host_name + data["env"]["AYON_HOST_NAME"] = app.host_name if not asset_doc or not task_name: # QUESTION replace with log.info and skip workfile discovery? @@ -1812,7 +1812,7 @@ def prepare_context_environments(data, env_group=None, addons_manager=None): "Couldn't create workdir because: {}".format(str(exc)) ) - data["env"]["AVALON_WORKDIR"] = workdir + data["env"]["AYON_WORKDIR"] = workdir _prepare_last_workfile(data, workdir, addons_manager) @@ -1929,7 +1929,7 @@ def _prepare_last_workfile(data, workdir, addons_manager): "Setting last workfile path: {}".format(last_workfile_path) ) - data["env"]["AVALON_LAST_WORKFILE"] = last_workfile_path + data["env"]["AYON_LAST_WORKFILE"] = last_workfile_path data["last_workfile_path"] = last_workfile_path diff --git a/client/ayon_core/lib/log.py b/client/ayon_core/lib/log.py index cbb1e41bae..36c39f9d84 100644 --- a/client/ayon_core/lib/log.py +++ b/client/ayon_core/lib/log.py @@ -257,7 +257,7 @@ class Logger: return cls._process_name # Get process name - process_name = os.environ.get("AVALON_APP_NAME") + process_name = os.environ.get("AYON_APP_NAME") if not process_name: try: import psutil diff --git a/client/ayon_core/modules/clockify/launcher_actions/ClockifyStart.py b/client/ayon_core/modules/clockify/launcher_actions/ClockifyStart.py index 19aa2ef195..f7dd1772b0 100644 --- a/client/ayon_core/modules/clockify/launcher_actions/ClockifyStart.py +++ b/client/ayon_core/modules/clockify/launcher_actions/ClockifyStart.py @@ -12,7 +12,7 @@ class ClockifyStart(LauncherAction): def is_compatible(self, session): """Return whether the action is compatible with the session""" - if "AVALON_TASK" in session: + if "AYON_TASK_NAME" in session: return True return False @@ -20,9 +20,9 @@ class ClockifyStart(LauncherAction): self.clockify_api.set_api() user_id = self.clockify_api.user_id workspace_id = self.clockify_api.workspace_id - project_name = session["AVALON_PROJECT"] - asset_name = session["AVALON_ASSET"] - task_name = session["AVALON_TASK"] + project_name = session["AYON_PROJECT_NAME"] + asset_name = session["AYON_FOLDER_PATH"] + task_name = session["AYON_TASK_NAME"] description = asset_name # fetch asset docs diff --git a/client/ayon_core/modules/clockify/launcher_actions/ClockifySync.py b/client/ayon_core/modules/clockify/launcher_actions/ClockifySync.py index 30f5ae698f..5ef9033ffe 100644 --- a/client/ayon_core/modules/clockify/launcher_actions/ClockifySync.py +++ b/client/ayon_core/modules/clockify/launcher_actions/ClockifySync.py @@ -36,7 +36,7 @@ class ClockifySync(LauncherAction): raise ClockifyPermissionsCheckFailed( "Current CLockify user is missing permissions for this action!" ) - project_name = session.get("AVALON_PROJECT") or "" + project_name = session.get("AYON_PROJECT_NAME") or "" projects_to_sync = [] if project_name.strip(): diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_aftereffects_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_aftereffects_deadline.py index 618b71bbaf..a4eb5b673c 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_aftereffects_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_aftereffects_deadline.py @@ -80,11 +80,11 @@ class AfterEffectsSubmitDeadline( "FTRACK_API_KEY", "FTRACK_API_USER", "FTRACK_SERVER", - "AVALON_PROJECT", - "AVALON_ASSET", - "AVALON_TASK", - "AVALON_WORKDIR", - "AVALON_APP_NAME", + "AYON_PROJECT_NAME", + "AYON_FOLDER_PATH", + "AYON_TASK_NAME", + "AYON_WORKDIR", + "AYON_APP_NAME", "AYON_LOG_NO_COLORS", "IS_TEST" ] diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_blender_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_blender_deadline.py index af864ace5b..e9e6ef083d 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_blender_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_blender_deadline.py @@ -102,11 +102,11 @@ class BlenderSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline, "FTRACK_API_USER", "FTRACK_SERVER", "OPENPYPE_SG_USER", - "AVALON_PROJECT", - "AVALON_ASSET", - "AVALON_TASK", - "AVALON_WORKDIR", - "AVALON_APP_NAME", + "AYON_PROJECT_NAME", + "AYON_FOLDER_PATH", + "AYON_TASK_NAME", + "AYON_WORKDIR", + "AYON_APP_NAME", "IS_TEST" ] diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_fusion_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_fusion_deadline.py index 7aa8546bb6..9c125db174 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_fusion_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_fusion_deadline.py @@ -220,11 +220,11 @@ class FusionSubmitDeadline( "FTRACK_API_KEY", "FTRACK_API_USER", "FTRACK_SERVER", - "AVALON_PROJECT", - "AVALON_ASSET", - "AVALON_TASK", - "AVALON_WORKDIR", - "AVALON_APP_NAME", + "AYON_PROJECT_NAME", + "AYON_FOLDER_PATH", + "AYON_TASK_NAME", + "AYON_WORKDIR", + "AYON_APP_NAME", "AYON_LOG_NO_COLORS", "IS_TEST", "AYON_BUNDLE_NAME", diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_harmony_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_harmony_deadline.py index 4d375299fa..d3915ed16d 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_harmony_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_harmony_deadline.py @@ -273,11 +273,11 @@ class HarmonySubmitDeadline( "FTRACK_API_KEY", "FTRACK_API_USER", "FTRACK_SERVER", - "AVALON_PROJECT", - "AVALON_ASSET", - "AVALON_TASK", - "AVALON_WORKDIR", - "AVALON_APP_NAME", + "AYON_PROJECT_NAME", + "AYON_FOLDER_PATH", + "AYON_TASK_NAME", + "AYON_WORKDIR", + "AYON_APP_NAME", "AYON_LOG_NO_COLORS" "IS_TEST" ] diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py index 96ee80c4d7..f6676da3d6 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py @@ -98,11 +98,11 @@ class HoudiniCacheSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline "FTRACK_API_USER", "FTRACK_SERVER", "OPENPYPE_SG_USER", - "AVALON_PROJECT", - "AVALON_ASSET", - "AVALON_TASK", - "AVALON_WORKDIR", - "AVALON_APP_NAME", + "AYON_PROJECT_NAME", + "AYON_FOLDER_PATH", + "AYON_TASK_NAME", + "AYON_WORKDIR", + "AYON_APP_NAME", "AYON_LOG_NO_COLORS", ] diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py index d7a062c9a6..436f0d70ad 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py @@ -204,11 +204,11 @@ class HoudiniSubmitDeadline( "FTRACK_API_USER", "FTRACK_SERVER", "OPENPYPE_SG_USER", - "AVALON_PROJECT", - "AVALON_ASSET", - "AVALON_TASK", - "AVALON_WORKDIR", - "AVALON_APP_NAME", + "AYON_PROJECT_NAME", + "AYON_FOLDER_PATH", + "AYON_TASK_NAME", + "AYON_WORKDIR", + "AYON_APP_NAME", "AYON_LOG_NO_COLORS", ] diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_max_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_max_deadline.py index 8908283164..4eaaedac34 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_max_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_max_deadline.py @@ -106,11 +106,11 @@ class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline, "FTRACK_API_USER", "FTRACK_SERVER", "OPENPYPE_SG_USER", - "AVALON_PROJECT", - "AVALON_ASSET", - "AVALON_TASK", - "AVALON_WORKDIR", - "AVALON_APP_NAME", + "AYON_PROJECT_NAME", + "AYON_FOLDER_PATH", + "AYON_TASK_NAME", + "AYON_WORKDIR", + "AYON_APP_NAME", "IS_TEST" ] diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_maya_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_maya_deadline.py index 2b7a7cf698..1b86d74987 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -199,11 +199,11 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline, "FTRACK_API_USER", "FTRACK_SERVER", "OPENPYPE_SG_USER", - "AVALON_PROJECT", - "AVALON_ASSET", - "AVALON_TASK", - "AVALON_WORKDIR", - "AVALON_APP_NAME", + "AYON_PROJECT_NAME", + "AYON_FOLDER_PATH", + "AYON_TASK_NAME", + "AYON_WORKDIR", + "AYON_APP_NAME", "IS_TEST" ] diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py index ed360d84ae..08ddbcf661 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py @@ -104,10 +104,10 @@ class MayaSubmitRemotePublishDeadline( if key in os.environ } - environment["AVALON_PROJECT"] = project_name - environment["AVALON_ASSET"] = instance.context.data["asset"] - environment["AVALON_TASK"] = instance.context.data["task"] - environment["AVALON_APP_NAME"] = os.environ.get("AVALON_APP_NAME") + environment["AYON_PROJECT_NAME"] = project_name + environment["AYON_FOLDER_PATH"] = instance.context.data["asset"] + environment["AYON_TASK_NAME"] = instance.context.data["task"] + environment["AYON_APP_NAME"] = os.environ.get("AYON_APP_NAME") environment["OPENPYPE_PUBLISH_SUBSET"] = instance.data["subset"] environment["AYON_LOG_NO_COLORS"] = "1" environment["AYON_USERNAME"] = instance.context.data["user"] diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_nuke_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_nuke_deadline.py index 61a334e184..f48f9ba9b7 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_nuke_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_nuke_deadline.py @@ -373,10 +373,10 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin, keys = [ "PYTHONPATH", "PATH", - "AVALON_PROJECT", - "AVALON_ASSET", - "AVALON_TASK", - "AVALON_APP_NAME", + "AYON_PROJECT_NAME", + "AYON_FOLDER_PATH", + "AYON_TASK_NAME", + "AYON_APP_NAME", "FTRACK_API_KEY", "FTRACK_API_USER", "FTRACK_SERVER", diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py index 38ea31ab7b..522f19604b 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py @@ -68,7 +68,7 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin, "FTRACK_API_USER", "FTRACK_API_KEY", "FTRACK_SERVER", - "AVALON_APP_NAME", + "AYON_APP_NAME", "AYON_USERNAME", "OPENPYPE_SG_USER", "KITSU_LOGIN", @@ -126,9 +126,9 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin, create_metadata_path(instance, anatomy) environment = { - "AVALON_PROJECT": instance.context.data["projectName"], - "AVALON_ASSET": instance.context.data["asset"], - "AVALON_TASK": instance.context.data["task"], + "AYON_PROJECT_NAME": instance.context.data["projectName"], + "AYON_FOLDER_PATH": instance.context.data["asset"], + "AYON_TASK_NAME": instance.context.data["task"], "AYON_USERNAME": instance.context.data["user"], "AYON_LOG_NO_COLORS": "1", "IS_TEST": str(int(is_in_tests())), diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py index c4ab6a2932..35b54d40ca 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py @@ -110,7 +110,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, "FTRACK_API_USER", "FTRACK_API_KEY", "FTRACK_SERVER", - "AVALON_APP_NAME", + "AYON_APP_NAME", "AYON_USERNAME", "OPENPYPE_SG_USER", "KITSU_LOGIN", @@ -182,9 +182,9 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, create_metadata_path(instance, anatomy) environment = { - "AVALON_PROJECT": instance.context.data["projectName"], - "AVALON_ASSET": instance.context.data["asset"], - "AVALON_TASK": instance.context.data["task"], + "AYON_PROJECT_NAME": instance.context.data["projectName"], + "AYON_FOLDER_PATH": instance.context.data["asset"], + "AYON_TASK_NAME": instance.context.data["task"], "AYON_USERNAME": instance.context.data["user"], "AYON_LOG_NO_COLORS": "1", "IS_TEST": str(int(is_in_tests())), diff --git a/client/ayon_core/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py b/client/ayon_core/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py index 81aab00b93..8b539931fb 100644 --- a/client/ayon_core/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py +++ b/client/ayon_core/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py @@ -471,12 +471,21 @@ def inject_ayon_environment(deadlinePlugin): ] add_kwargs = { - "project": job.GetJobEnvironmentKeyValue("AVALON_PROJECT"), - "asset": job.GetJobEnvironmentKeyValue("AVALON_ASSET"), - "task": job.GetJobEnvironmentKeyValue("AVALON_TASK"), - "app": job.GetJobEnvironmentKeyValue("AVALON_APP_NAME"), "envgroup": "farm", } + # Support backwards compatible keys + for key, env_keys in { + "project": ["AYON_PROJECT_NAME", "AVALON_PROJECT"], + "asset": ["AYON_FOLDER_PATH", "AVALON_ASSET"], + "task": ["AYON_TASK_NAME", "AVALON_TASK"], + "app": ["AYON_APP_NAME", "AVALON_APP_NAME"], + }: + value = "" + for env_key in env_keys: + value = job.GetJobEnvironmentKeyValue(env_key) + if value: + break + add_kwargs[key] = value if job.GetJobEnvironmentKeyValue("IS_TEST"): args.append("--automatic-tests") @@ -486,8 +495,8 @@ def inject_ayon_environment(deadlinePlugin): args.extend(["--{}".format(key), value]) else: raise RuntimeError(( - "Missing required env vars: AVALON_PROJECT, AVALON_ASSET," - " AVALON_TASK, AVALON_APP_NAME" + "Missing required env vars: AYON_PROJECT_NAME," + " AYON_FOLDER_PATH, AYON_TASK_NAME, AYON_APP_NAME" )) environment = { diff --git a/client/ayon_core/modules/royalrender/lib.py b/client/ayon_core/modules/royalrender/lib.py index d985a39d24..2b26ec82e4 100644 --- a/client/ayon_core/modules/royalrender/lib.py +++ b/client/ayon_core/modules/royalrender/lib.py @@ -357,8 +357,8 @@ class BaseCreateRoyalRenderJob(pyblish.api.InstancePlugin, if not all(add_kwargs.values()): raise RuntimeError(( - "Missing required env vars: AVALON_PROJECT, AVALON_ASSET," - " AVALON_TASK, AVALON_APP_NAME" + "Missing required env vars: AYON_PROJECT_NAME, AYON_FOLDER_PATH," + " AYON_TASK_NAME, AYON_APP_NAME" )) for key, value in add_kwargs.items(): diff --git a/client/ayon_core/modules/royalrender/plugins/publish/create_publish_royalrender_job.py b/client/ayon_core/modules/royalrender/plugins/publish/create_publish_royalrender_job.py index abc8d7dccd..910abfcb15 100644 --- a/client/ayon_core/modules/royalrender/plugins/publish/create_publish_royalrender_job.py +++ b/client/ayon_core/modules/royalrender/plugins/publish/create_publish_royalrender_job.py @@ -63,7 +63,7 @@ class CreatePublishRoyalRenderJob(pyblish.api.InstancePlugin, "FTRACK_API_USER", "FTRACK_API_KEY", "FTRACK_SERVER", - "AVALON_APP_NAME", + "AYON_APP_NAME", "AYON_USERNAME", "OPENPYPE_SG_USER", ] @@ -179,9 +179,9 @@ class CreatePublishRoyalRenderJob(pyblish.api.InstancePlugin, anatomy_data = instance.context.data["anatomyData"] environment = RREnvList({ - "AVALON_PROJECT": anatomy_data["project"]["name"], - "AVALON_ASSET": instance.context.data["asset"], - "AVALON_TASK": anatomy_data["task"]["name"], + "AYON_PROJECT_NAME": anatomy_data["project"]["name"], + "AYON_FOLDER_PATH": instance.context.data["asset"], + "AYON_TASK_NAME": anatomy_data["task"]["name"], "AYON_USERNAME": anatomy_data["user"] }) diff --git a/client/ayon_core/modules/royalrender/rr_root/plugins/control_job/perjob/m50__openpype_publish_render.py b/client/ayon_core/modules/royalrender/rr_root/plugins/control_job/perjob/m50__openpype_publish_render.py index 7118c5ebef..cfec2622d9 100644 --- a/client/ayon_core/modules/royalrender/rr_root/plugins/control_job/perjob/m50__openpype_publish_render.py +++ b/client/ayon_core/modules/royalrender/rr_root/plugins/control_job/perjob/m50__openpype_publish_render.py @@ -136,10 +136,10 @@ class OpenPypeContextSelector: def run_publish(self): """Run publish process.""" - env = {"AVALON_PROJECT": str(self.context.get("project")), - "AVALON_ASSET": str(self.context.get("asset")), - "AVALON_TASK": str(self.context.get("task")), - # "AVALON_APP_NAME": str(self.context.get("app_name")) + env = {"AYON_PROJECT_NAME": str(self.context.get("project")), + "AYON_FOLDER_PATH": str(self.context.get("asset")), + "AYON_TASK_NAME": str(self.context.get("task")), + # "AYON_APP_NAME": str(self.context.get("app_name")) } print(">>> setting environment:") @@ -182,10 +182,18 @@ print("running selector") selector = OpenPypeContextSelector() # try to set context from environment -selector.context["project"] = os.getenv("AVALON_PROJECT") -selector.context["asset"] = os.getenv("AVALON_ASSET") -selector.context["task"] = os.getenv("AVALON_TASK") -# selector.context["app_name"] = os.getenv("AVALON_APP_NAME") +for key, env_keys in { + "project": ["AYON_PROJECT_NAME", "AVALON_PROJECT"], + "asset": ["AYON_FOLDER_PATH", "AVALON_ASSET"], + "task": ["AYON_TASK_NAME", "AVALON_TASK"], + # "app_name": ["AYON_APP_NAME", "AVALON_APP_NAME"] +}: + value = "" + for env_key in env_keys: + value = os.getenv(env_key) + if value: + break + selector.context[key] = value # if anything inside is None, scratch the whole thing and # ask user for context. diff --git a/client/ayon_core/pipeline/actions.py b/client/ayon_core/pipeline/actions.py index 1701498d10..8e0ce7e583 100644 --- a/client/ayon_core/pipeline/actions.py +++ b/client/ayon_core/pipeline/actions.py @@ -26,7 +26,7 @@ class LauncherAction(object): Args: session (dict[str, Union[str, None]]): Session data with - AVALON_PROJECT, AVALON_ASSET and AVALON_TASK. + AYON_PROJECT_NAME, AYON_FOLDER_PATH and AYON_TASK_NAME. """ return True diff --git a/client/ayon_core/pipeline/anatomy.py b/client/ayon_core/pipeline/anatomy.py index 86b7d92309..4864822aa1 100644 --- a/client/ayon_core/pipeline/anatomy.py +++ b/client/ayon_core/pipeline/anatomy.py @@ -423,7 +423,7 @@ class Anatomy(BaseAnatomy): def __init__(self, project_name=None, site_name=None): if not project_name: - project_name = os.environ.get("AVALON_PROJECT") + project_name = os.environ.get("AYON_PROJECT_NAME") if not project_name: raise ProjectNotSet(( diff --git a/client/ayon_core/pipeline/context_tools.py b/client/ayon_core/pipeline/context_tools.py index 339ef9187f..5716793354 100644 --- a/client/ayon_core/pipeline/context_tools.py +++ b/client/ayon_core/pipeline/context_tools.py @@ -117,12 +117,12 @@ def install_host(host): addons_manager = _get_addons_manager() - project_name = os.getenv("AVALON_PROJECT") + project_name = os.getenv("AYON_PROJECT_NAME") # WARNING: This might be an issue # - commented out because 'traypublisher' does not have set project # if not project_name: # raise ValueError( - # "AVALON_PROJECT is missing in environment variables." + # "AYON_PROJECT_NAME is missing in environment variables." # ) log.info("Activating {}..".format(project_name)) @@ -152,7 +152,7 @@ def install_host(host): print("Registering pyblish target: automated") pyblish.api.register_target("automated") - host_name = os.environ.get("AVALON_APP") + host_name = os.environ.get("AYON_HOST_NAME") # Give option to handle host installation for addon in addons_manager.get_enabled_addons(): @@ -172,7 +172,7 @@ def install_ayon_plugins(project_name=None, host_name=None): register_inventory_action_path(INVENTORY_PATH) if host_name is None: - host_name = os.environ.get("AVALON_APP") + host_name = os.environ.get("AYON_HOST_NAME") addons_manager = _get_addons_manager() publish_plugin_dirs = addons_manager.collect_publish_plugin_paths( @@ -196,7 +196,7 @@ def install_ayon_plugins(project_name=None, host_name=None): register_inventory_action_path(path) if project_name is None: - project_name = os.environ.get("AVALON_PROJECT") + project_name = os.environ.get("AYON_PROJECT_NAME") # Register studio specific plugins if project_name: @@ -331,7 +331,7 @@ def get_current_host_name(): """Current host name. Function is based on currently registered host integration or environment - variable 'AVALON_APP'. + variable 'AYON_HOST_NAME'. Returns: Union[str, None]: Name of host integration in current process or None. @@ -340,7 +340,7 @@ def get_current_host_name(): host = registered_host() if isinstance(host, HostBase): return host.name - return os.environ.get("AVALON_APP") + return os.environ.get("AYON_HOST_NAME") def get_global_context(): @@ -365,9 +365,9 @@ def get_global_context(): """ return { - "project_name": os.environ.get("AVALON_PROJECT"), - "asset_name": os.environ.get("AVALON_ASSET"), - "task_name": os.environ.get("AVALON_TASK"), + "project_name": os.environ.get("AYON_PROJECT_NAME"), + "asset_name": os.environ.get("AYON_FOLDER_PATH"), + "task_name": os.environ.get("AYON_TASK_NAME"), } @@ -474,10 +474,10 @@ def get_template_data_from_session(session=None, system_settings=None): """ if session is not None: - project_name = session["AVALON_PROJECT"] - asset_name = session["AVALON_ASSET"] - task_name = session["AVALON_TASK"] - host_name = session["AVALON_APP"] + project_name = session["AYON_PROJECT_NAME"] + asset_name = session["AYON_FOLDER_PATH"] + task_name = session["AYON_TASK_NAME"] + host_name = session["AYON_HOST_NAME"] else: context = get_current_context() project_name = context["project_name"] @@ -525,8 +525,8 @@ def get_workdir_from_session(session=None, template_key=None): """ if session is not None: - project_name = session["AVALON_PROJECT"] - host_name = session["AVALON_APP"] + project_name = session["AYON_PROJECT_NAME"] + host_name = session["AYON_HOST_NAME"] else: project_name = get_current_project_name() host_name = get_current_host_name() @@ -566,10 +566,10 @@ def get_custom_workfile_template_from_session( """ if session is not None: - project_name = session["AVALON_PROJECT"] - asset_name = session["AVALON_ASSET"] - task_name = session["AVALON_TASK"] - host_name = session["AVALON_APP"] + project_name = session["AYON_PROJECT_NAME"] + asset_name = session["AYON_FOLDER_PATH"] + task_name = session["AYON_TASK_NAME"] + host_name = session["AYON_HOST_NAME"] else: context = get_current_context() project_name = context["project_name"] @@ -616,10 +616,10 @@ def change_current_context(asset_doc, task_name, template_key=None): folder_path = get_asset_name_identifier(asset_doc) envs = { - "AVALON_PROJECT": project_name, - "AVALON_ASSET": folder_path, - "AVALON_TASK": task_name, - "AVALON_WORKDIR": workdir, + "AYON_PROJECT_NAME": project_name, + "AYON_FOLDER_PATH": folder_path, + "AYON_TASK_NAME": task_name, + "AYON_WORKDIR": workdir, } # Update the Session and environments. Pop from environments all keys with diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index be685ea2cc..b973c45097 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -1536,7 +1536,7 @@ class CreateContext: def host_name(self): if hasattr(self.host, "name"): return self.host.name - return os.environ["AVALON_APP"] + return os.environ["AYON_HOST_NAME"] def get_current_project_name(self): """Project name which was used as current context on context reset. diff --git a/client/ayon_core/pipeline/create/legacy_create.py b/client/ayon_core/pipeline/create/legacy_create.py index 08be32eed4..aab6b67e6f 100644 --- a/client/ayon_core/pipeline/create/legacy_create.py +++ b/client/ayon_core/pipeline/create/legacy_create.py @@ -45,7 +45,7 @@ class LegacyCreator(object): def apply_settings(cls, project_settings, system_settings): """Apply OpenPype settings to a plugin class.""" - host_name = os.environ.get("AVALON_APP") + host_name = os.environ.get("AYON_HOST_NAME") plugin_type = "create" plugin_type_settings = ( project_settings diff --git a/client/ayon_core/pipeline/create/subset_name.py b/client/ayon_core/pipeline/create/subset_name.py index 2973b1e54e..93a61b8b8b 100644 --- a/client/ayon_core/pipeline/create/subset_name.py +++ b/client/ayon_core/pipeline/create/subset_name.py @@ -128,13 +128,13 @@ def get_subset_name( return "" if not host_name: - host_name = os.environ.get("AVALON_APP") + host_name = os.environ.get("AYON_HOST_NAME") # Use only last part of class family value split by dot (`.`) family = family.rsplit(".", 1)[-1] if project_name is None: - project_name = os.environ.get("AVALON_PROJECT") + project_name = os.environ.get("AYON_PROJECT_NAME") asset_tasks = asset_doc.get("data", {}).get("tasks") or {} task_info = asset_tasks.get(task_name) or {} diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 9423d8501c..389d3d27ed 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -321,7 +321,7 @@ def prepare_representations(skeleton_data, exp_files, anatomy, aov_filter, """ representations = [] - host_name = os.environ.get("AVALON_APP", "") + host_name = os.environ.get("AYON_HOST_NAME", "") collections, remainders = clique.assemble(exp_files) log = Logger.get_logger("farm_publishing") @@ -541,7 +541,7 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, """ # TODO: this needs to be taking the task from context or instance - task = os.environ["AVALON_TASK"] + task = os.environ["AYON_TASK_NAME"] anatomy = instance.context.data["anatomy"] subset = skeleton["subset"] @@ -611,7 +611,7 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, log.info("Creating data for: {}".format(subset_name)) - app = os.environ.get("AVALON_APP", "") + app = os.environ.get("AYON_HOST_NAME", "") if isinstance(col, list): render_file_name = os.path.basename(col[0]) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index fc64edf2ae..1d4627689f 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -38,7 +38,7 @@ class LoaderPlugin(list): @classmethod def apply_settings(cls, project_settings, system_settings): - host_name = os.environ.get("AVALON_APP") + host_name = os.environ.get("AYON_HOST_NAME") plugin_type = "load" plugin_type_settings = ( project_settings diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 47f4be9e69..a62c2d9c5b 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -437,7 +437,7 @@ def filter_pyblish_plugins(plugins): # - kept becau on farm is probably used host 'shell' which propably # affect how settings are applied there host_name = pyblish.api.current_host() - project_name = os.environ.get("AVALON_PROJECT") + project_name = os.environ.get("AYON_PROJECT_NAME") project_settings = get_project_settings(project_name) system_settings = get_system_settings() diff --git a/client/ayon_core/pipeline/workfile/build_workfile.py b/client/ayon_core/pipeline/workfile/build_workfile.py index c62facaaa9..6b13eeeed6 100644 --- a/client/ayon_core/pipeline/workfile/build_workfile.py +++ b/client/ayon_core/pipeline/workfile/build_workfile.py @@ -229,8 +229,8 @@ class BuildWorkfile: def get_build_presets(self, task_name, asset_doc): """ Returns presets to build workfile for task name. - Presets are loaded for current project set in - io.Session["AVALON_PROJECT"], filtered by registered host + Presets are loaded for current project received by + 'get_current_project_name', filtered by registered host and entered task name. Args: diff --git a/client/ayon_core/pipeline/workfile/path_resolving.py b/client/ayon_core/pipeline/workfile/path_resolving.py index 95a0a03c60..2062705d3c 100644 --- a/client/ayon_core/pipeline/workfile/path_resolving.py +++ b/client/ayon_core/pipeline/workfile/path_resolving.py @@ -157,7 +157,7 @@ def get_workdir( task_name (str): Task name for which are workdir data preapred. host_name (str): Host which is used to workdir. This is required because workdir template may contain `{app}` key. In `Session` - is stored under `AVALON_APP` key. + is stored under `AYON_HOST_NAME` key. anatomy (Anatomy): Optional argument. Anatomy object is created using project name from `project_doc`. It is preferred to pass this argument as initialization of a new Anatomy object may be time diff --git a/client/ayon_core/pipeline/workfile/workfile_template_builder.py b/client/ayon_core/pipeline/workfile/workfile_template_builder.py index 1afe26813f..9778e60d1b 100644 --- a/client/ayon_core/pipeline/workfile/workfile_template_builder.py +++ b/client/ayon_core/pipeline/workfile/workfile_template_builder.py @@ -103,7 +103,7 @@ class AbstractTemplateBuilder(object): if isinstance(host, HostBase): host_name = host.name else: - host_name = os.environ.get("AVALON_APP") + host_name = os.environ.get("AYON_HOST_NAME") self._host = host self._host_name = host_name @@ -129,19 +129,19 @@ class AbstractTemplateBuilder(object): def project_name(self): if isinstance(self._host, HostBase): return self._host.get_current_project_name() - return os.getenv("AVALON_PROJECT") + return os.getenv("AYON_PROJECT_NAME") @property def current_asset_name(self): if isinstance(self._host, HostBase): return self._host.get_current_asset_name() - return os.getenv("AVALON_ASSET") + return os.getenv("AYON_FOLDER_PATH") @property def current_task_name(self): if isinstance(self._host, HostBase): return self._host.get_current_task_name() - return os.getenv("AVALON_TASK") + return os.getenv("AYON_TASK_NAME") def get_current_context(self): if isinstance(self._host, HostBase): @@ -579,7 +579,7 @@ class AbstractTemplateBuilder(object): template_path (str): Fullpath for current task and host's template file. """ - last_workfile_path = os.environ.get("AVALON_LAST_WORKFILE") + last_workfile_path = os.environ.get("AYON_LAST_WORKFILE") self.log.info("__ last_workfile_path: {}".format(last_workfile_path)) if os.path.exists(last_workfile_path): # ignore in case workfile existence diff --git a/client/ayon_core/plugins/actions/open_file_explorer.py b/client/ayon_core/plugins/actions/open_file_explorer.py index b29ed30258..fba3c231a5 100644 --- a/client/ayon_core/plugins/actions/open_file_explorer.py +++ b/client/ayon_core/plugins/actions/open_file_explorer.py @@ -22,14 +22,14 @@ class OpenTaskPath(LauncherAction): def is_compatible(self, session): """Return whether the action is compatible with the session""" - return bool(session.get("AVALON_ASSET")) + return bool(session.get("AYON_FOLDER_PATH")) def process(self, session, **kwargs): from qtpy import QtCore, QtWidgets - project_name = session["AVALON_PROJECT"] - asset_name = session["AVALON_ASSET"] - task_name = session.get("AVALON_TASK", None) + project_name = session["AYON_PROJECT_NAME"] + asset_name = session["AYON_FOLDER_PATH"] + task_name = session.get("AYON_TASK_NAME", None) path = self._get_workdir(project_name, asset_name, task_name) if not path: diff --git a/client/ayon_core/plugins/load/delete_old_versions.py b/client/ayon_core/plugins/load/delete_old_versions.py index 6b3263e2b6..956b232e2b 100644 --- a/client/ayon_core/plugins/load/delete_old_versions.py +++ b/client/ayon_core/plugins/load/delete_old_versions.py @@ -359,7 +359,7 @@ # # if mongo_changes_bulk: # dbcon = AvalonMongoDB() -# dbcon.Session["AVALON_PROJECT"] = project_name +# dbcon.Session["AYON_PROJECT_NAME"] = project_name # dbcon.install() # dbcon.bulk_write(mongo_changes_bulk) # dbcon.uninstall() diff --git a/client/ayon_core/plugins/publish/collect_anatomy_context_data.py b/client/ayon_core/plugins/publish/collect_anatomy_context_data.py index 978ae5e1e1..fbdf26e76c 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_context_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_context_data.py @@ -4,9 +4,9 @@ Requires: context -> anatomy context -> projectEntity context -> assetEntity + context -> task context -> username context -> datetimeData - session -> AVALON_TASK Provides: context -> anatomyData diff --git a/client/ayon_core/plugins/publish/collect_context_entities.py b/client/ayon_core/plugins/publish/collect_context_entities.py index 8480435e21..30bb184ef5 100644 --- a/client/ayon_core/plugins/publish/collect_context_entities.py +++ b/client/ayon_core/plugins/publish/collect_context_entities.py @@ -1,7 +1,6 @@ """Collect Anatomy and global anatomy data. Requires: - session -> AVALON_ASSET context -> projectName context -> asset context -> task diff --git a/client/ayon_core/plugins/publish/collect_from_create_context.py b/client/ayon_core/plugins/publish/collect_from_create_context.py index d8e803a43c..edb5cc1654 100644 --- a/client/ayon_core/plugins/publish/collect_from_create_context.py +++ b/client/ayon_core/plugins/publish/collect_from_create_context.py @@ -57,9 +57,9 @@ class CollectFromCreateContext(pyblish.api.ContextPlugin): asset_name = create_context.get_current_asset_name() task_name = create_context.get_current_task_name() for key, value in ( - ("AVALON_PROJECT", project_name), - ("AVALON_ASSET", asset_name), - ("AVALON_TASK", task_name) + ("AYON_PROJECT_NAME", project_name), + ("AYON_FOLDER_PATH", asset_name), + ("AYON_TASK_NAME", task_name) ): os.environ[key] = value diff --git a/client/ayon_core/plugins/publish/collect_host_name.py b/client/ayon_core/plugins/publish/collect_host_name.py index 89e4e03c1a..e76579bbd2 100644 --- a/client/ayon_core/plugins/publish/collect_host_name.py +++ b/client/ayon_core/plugins/publish/collect_host_name.py @@ -24,13 +24,13 @@ class CollectHostName(pyblish.api.ContextPlugin): if host_name and app_name and app_label: return - # Use AVALON_APP to get host name if available + # Use AYON_HOST_NAME to get host name if available if not host_name: - host_name = os.environ.get("AVALON_APP") + host_name = os.environ.get("AYON_HOST_NAME") - # Use AVALON_APP_NAME to get full app name + # Use AYON_APP_NAME to get full app name if not app_name: - app_name = os.environ.get("AVALON_APP_NAME") + app_name = os.environ.get("AYON_APP_NAME") # Fill missing values based on app full name if (not host_name or not app_label) and app_name: diff --git a/client/ayon_core/plugins/publish/collect_rendered_files.py b/client/ayon_core/plugins/publish/collect_rendered_files.py index a7b6064b7a..9a316b69a4 100644 --- a/client/ayon_core/plugins/publish/collect_rendered_files.py +++ b/client/ayon_core/plugins/publish/collect_rendered_files.py @@ -179,14 +179,14 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin): ) # Remap workdir if it's set - workdir = os.getenv("AVALON_WORKDIR") + workdir = os.getenv("AYON_WORKDIR") remapped_workdir = None if workdir: remapped_workdir = anatomy.roots_obj.path_remapper( - os.getenv("AVALON_WORKDIR") + os.getenv("AYON_WORKDIR") ) if remapped_workdir: - os.environ["AVALON_WORKDIR"] = remapped_workdir + os.environ["AYON_WORKDIR"] = remapped_workdir except Exception as e: self.log.error(e, exc_info=True) raise Exception("Error") from e diff --git a/client/ayon_core/scripts/non_python_host_launch.py b/client/ayon_core/scripts/non_python_host_launch.py index 97632e98ad..4c18fd0ccc 100644 --- a/client/ayon_core/scripts/non_python_host_launch.py +++ b/client/ayon_core/scripts/non_python_host_launch.py @@ -79,7 +79,7 @@ def main(argv): if after_script_idx is not None: launch_args = sys_args[after_script_idx:] - host_name = os.environ["AVALON_APP"].lower() + host_name = os.environ["AYON_HOST_NAME"].lower() if host_name == "photoshop": # TODO refactor launch logic according to AE from ayon_core.hosts.photoshop.api.lib import main @@ -90,7 +90,7 @@ def main(argv): else: title = "Unknown host name" message = ( - "BUG: Environment variable AVALON_APP contains unknown" + "BUG: Environment variable AYON_HOST_NAME contains unknown" " host name \"{}\"" ).format(host_name) show_error_messagebox(title, message) diff --git a/client/ayon_core/settings/lib.py b/client/ayon_core/settings/lib.py index beae376b7c..717d3fba3a 100644 --- a/client/ayon_core/settings/lib.py +++ b/client/ayon_core/settings/lib.py @@ -240,15 +240,15 @@ def get_site_local_overrides(project_name, site_name, local_settings=None): def get_current_project_settings(): """Project settings for current context project. - Project name should be stored in environment variable `AVALON_PROJECT`. + Project name should be stored in environment variable `AYON_PROJECT_NAME`. This function should be used only in host context where environment variable must be set and should not happen that any part of process will change the value of the enviornment variable. """ - project_name = os.environ.get("AVALON_PROJECT") + project_name = os.environ.get("AYON_PROJECT_NAME") if not project_name: raise ValueError( - "Missing context project in environemt variable `AVALON_PROJECT`." + "Missing context project in environemt variable `AYON_PROJECT_NAME`." ) return get_project_settings(project_name) diff --git a/client/ayon_core/tools/experimental_tools/tools_def.py b/client/ayon_core/tools/experimental_tools/tools_def.py index 568c7032d0..be6762b239 100644 --- a/client/ayon_core/tools/experimental_tools/tools_def.py +++ b/client/ayon_core/tools/experimental_tools/tools_def.py @@ -139,7 +139,7 @@ class ExperimentalTools: def get_tools_for_host(self, host_name=None): if not host_name: - host_name = os.environ.get("AVALON_APP") + host_name = os.environ.get("AYON_HOST_NAME") tools = [] for tool in self.tools: if ( diff --git a/client/ayon_core/tools/launcher/models/actions.py b/client/ayon_core/tools/launcher/models/actions.py index 37024b5810..53d2b78dc3 100644 --- a/client/ayon_core/tools/launcher/models/actions.py +++ b/client/ayon_core/tools/launcher/models/actions.py @@ -69,9 +69,9 @@ class ApplicationAction(LauncherAction): _log = None required_session_keys = ( - "AVALON_PROJECT", - "AVALON_ASSET", - "AVALON_TASK" + "AYON_PROJECT_NAME", + "AYON_FOLDER_PATH", + "AYON_TASK_NAME" ) @property @@ -85,7 +85,7 @@ class ApplicationAction(LauncherAction): if not session.get(key): return False - project_name = session["AVALON_PROJECT"] + project_name = session["AYON_PROJECT_NAME"] project_entity = self.project_entities[project_name] apps = project_entity["attrib"].get("applications") if not apps or self.application.full_name not in apps: @@ -119,9 +119,9 @@ class ApplicationAction(LauncherAction): ApplicationLaunchFailed, ) - project_name = session["AVALON_PROJECT"] - asset_name = session["AVALON_ASSET"] - task_name = session["AVALON_TASK"] + project_name = session["AYON_PROJECT_NAME"] + asset_name = session["AYON_FOLDER_PATH"] + task_name = session["AYON_TASK_NAME"] try: self.application.launch( project_name=project_name, @@ -416,6 +416,10 @@ class ActionsModel: task_name = task["name"] return { + "AYON_PROJECT_NAME": project_name, + "AYON_FOLDER_PATH": folder_path, + "AYON_TASK_NAME": task_name, + # Deprecated - kept for backwards compatibility "AVALON_PROJECT": project_name, "AVALON_ASSET": folder_path, "AVALON_TASK": task_name, diff --git a/client/ayon_core/tools/publisher/control.py b/client/ayon_core/tools/publisher/control.py index 988362fee4..5ccd428551 100644 --- a/client/ayon_core/tools/publisher/control.py +++ b/client/ayon_core/tools/publisher/control.py @@ -1807,9 +1807,9 @@ class PublisherController(BasePublisherController): context_title = self._host.get_context_title() if context_title is None: - context_title = os.environ.get("AVALON_APP_NAME") + context_title = os.environ.get("AYON_APP_NAME") if context_title is None: - context_title = os.environ.get("AVALON_APP") + context_title = os.environ.get("AYON_HOST_NAME") return context_title diff --git a/client/ayon_core/tools/texture_copy/app.py b/client/ayon_core/tools/texture_copy/app.py index 9b4406d8e7..eef648eaf9 100644 --- a/client/ayon_core/tools/texture_copy/app.py +++ b/client/ayon_core/tools/texture_copy/app.py @@ -132,8 +132,8 @@ class TextureCopy: def texture_copy(asset, project, path): t.echo("*** Running Texture tool ***") t.echo(">>> Initializing avalon session ...") - os.environ["AVALON_PROJECT"] = project - os.environ["AVALON_ASSET"] = asset + os.environ["AYON_PROJECT_NAME"] = project + os.environ["AYON_FOLDER_PATH"] = asset TextureCopy().process(asset, project, path) diff --git a/client/ayon_core/tools/workfile_template_build/window.py b/client/ayon_core/tools/workfile_template_build/window.py index ae4946d41d..feb11c5e75 100644 --- a/client/ayon_core/tools/workfile_template_build/window.py +++ b/client/ayon_core/tools/workfile_template_build/window.py @@ -27,7 +27,7 @@ class WorkfileBuildPlaceholderDialog(QtWidgets.QDialog): host_name = getattr(self._host, "name", None) if not host_name: - host_name = os.getenv("AVALON_APP") or "NA" + host_name = os.getenv("AYON_HOST_NAME") or "NA" self._host_name = host_name plugins_combo = QtWidgets.QComboBox(self) From 31460538f9b557921bfc18dcfd02f5322ca7a7b2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 12 Feb 2024 16:17:51 +0100 Subject: [PATCH 063/573] fix session requirement --- client/ayon_core/hosts/maya/plugins/publish/collect_render.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_render.py b/client/ayon_core/hosts/maya/plugins/publish/collect_render.py index 7daab19f4a..4ea91ccb0d 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/collect_render.py +++ b/client/ayon_core/hosts/maya/plugins/publish/collect_render.py @@ -8,13 +8,12 @@ publishing on farm. Requires: instance -> families instance -> setMembers + instance -> asset context -> currentFile context -> workspaceDir context -> user - session -> AYON_FOLDER_PATH - Optional: Provides: From 27512a06a121f76f8c7d0705ee317b11a062032a Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Mon, 12 Feb 2024 15:34:21 +0000 Subject: [PATCH 064/573] Removed references to `AYON_SERVER_ENABLED` --- client/ayon_core/hosts/blender/api/render_lib.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/blender/api/render_lib.py b/client/ayon_core/hosts/blender/api/render_lib.py index 9b43c14568..e1d0e2ae67 100644 --- a/client/ayon_core/hosts/blender/api/render_lib.py +++ b/client/ayon_core/hosts/blender/api/render_lib.py @@ -2,7 +2,6 @@ from pathlib import Path import bpy -from ayon_core import AYON_SERVER_ENABLED from ayon_core.settings import get_project_settings from ayon_core.pipeline import get_current_project_name @@ -166,14 +165,13 @@ def set_render_passes(settings, renderer): aovs_names = [aov.name for aov in vl.aovs] for cp in custom_passes: - cp_name = cp["attribute"] if AYON_SERVER_ENABLED else cp[0] + cp_name = cp["attribute"] if cp_name not in aovs_names: aov = vl.aovs.add() aov.name = cp_name else: aov = vl.aovs[cp_name] - aov.type = (cp["value"] - if AYON_SERVER_ENABLED else cp[1].get("type", "VALUE")) + aov.type = cp["value"] return aov_list, custom_passes From a0b18c91cd06ba18adb102f211ed4e7e30e00042 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Mon, 12 Feb 2024 15:39:40 +0000 Subject: [PATCH 065/573] Cast aov_list to set to improve performance --- client/ayon_core/hosts/blender/api/render_lib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/blender/api/render_lib.py b/client/ayon_core/hosts/blender/api/render_lib.py index e1d0e2ae67..73d327b1a8 100644 --- a/client/ayon_core/hosts/blender/api/render_lib.py +++ b/client/ayon_core/hosts/blender/api/render_lib.py @@ -100,7 +100,7 @@ def set_render_format(ext, multilayer): def set_render_passes(settings, renderer): - aov_list = settings["blender"]["RenderSettings"]["aov_list"] + aov_list = set(settings["blender"]["RenderSettings"]["aov_list"]) custom_passes = settings["blender"]["RenderSettings"]["custom_passes"] # Common passes for both renderers @@ -173,7 +173,7 @@ def set_render_passes(settings, renderer): aov = vl.aovs[cp_name] aov.type = cp["value"] - return aov_list, custom_passes + return list(aov_list), custom_passes def _create_aov_slot(name, aov_sep, slots, rpass_name, multi_exr, output_path): From 6e30f4a6f54c840d4105aedc9b1d3a50ad6f6026 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 12 Feb 2024 22:35:18 +0100 Subject: [PATCH 066/573] fix env auto fix --- .../repository/custom/plugins/GlobalJobPreLoad.py | 12 ++++++------ .../perjob/m50__openpype_publish_render.py | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/client/ayon_core/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py b/client/ayon_core/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py index 8b539931fb..330bf5b0c9 100644 --- a/client/ayon_core/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py +++ b/client/ayon_core/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py @@ -474,12 +474,12 @@ def inject_ayon_environment(deadlinePlugin): "envgroup": "farm", } # Support backwards compatible keys - for key, env_keys in { - "project": ["AYON_PROJECT_NAME", "AVALON_PROJECT"], - "asset": ["AYON_FOLDER_PATH", "AVALON_ASSET"], - "task": ["AYON_TASK_NAME", "AVALON_TASK"], - "app": ["AYON_APP_NAME", "AVALON_APP_NAME"], - }: + for key, env_keys in ( + ("project", ["AYON_PROJECT_NAME", "AVALON_PROJECT"]), + ("asset", ["AYON_FOLDER_PATH", "AVALON_ASSET"], + ("task", ["AYON_TASK_NAME", "AVALON_TASK"]), + ("app", ["AYON_APP_NAME", "AVALON_APP_NAME"]), + ): value = "" for env_key in env_keys: value = job.GetJobEnvironmentKeyValue(env_key) diff --git a/client/ayon_core/modules/royalrender/rr_root/plugins/control_job/perjob/m50__openpype_publish_render.py b/client/ayon_core/modules/royalrender/rr_root/plugins/control_job/perjob/m50__openpype_publish_render.py index cfec2622d9..778052778f 100644 --- a/client/ayon_core/modules/royalrender/rr_root/plugins/control_job/perjob/m50__openpype_publish_render.py +++ b/client/ayon_core/modules/royalrender/rr_root/plugins/control_job/perjob/m50__openpype_publish_render.py @@ -182,12 +182,12 @@ print("running selector") selector = OpenPypeContextSelector() # try to set context from environment -for key, env_keys in { - "project": ["AYON_PROJECT_NAME", "AVALON_PROJECT"], - "asset": ["AYON_FOLDER_PATH", "AVALON_ASSET"], - "task": ["AYON_TASK_NAME", "AVALON_TASK"], - # "app_name": ["AYON_APP_NAME", "AVALON_APP_NAME"] -}: +for key, env_keys in ( + ("project", ["AYON_PROJECT_NAME", "AVALON_PROJECT"]), + ("asset", ["AYON_FOLDER_PATH", "AVALON_ASSET"]), + ("task", ["AYON_TASK_NAME", "AVALON_TASK"]), + # ("app_name", ["AYON_APP_NAME", "AVALON_APP_NAME"]) +): value = "" for env_key in env_keys: value = os.getenv(env_key) From 97e4a3f93edd1af0f612c5f17b408c9abb2e6006 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 13 Feb 2024 19:21:25 +0800 Subject: [PATCH 067/573] clean up the unneccessary logs and code in collect members and collect workfiles --- .../max/plugins/create/create_workfile.py | 18 +++++------------ .../max/plugins/publish/collect_members.py | 3 +-- .../max/plugins/publish/collect_workfile.py | 20 ++++++------------- 3 files changed, 12 insertions(+), 29 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/create/create_workfile.py b/client/ayon_core/hosts/max/plugins/create/create_workfile.py index 5b51391749..e5fec5f437 100644 --- a/client/ayon_core/hosts/max/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/max/plugins/create/create_workfile.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- """Creator plugin for creating workfiles.""" -from ayon_core import AYON_SERVER_ENABLED from ayon_core.pipeline import CreatedInstance, AutoCreator from ayon_core.client import get_asset_by_name, get_asset_name_identifier from ayon_core.hosts.max.api import plugin @@ -31,10 +30,8 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): if current_instance is None: current_instance_asset = None - elif AYON_SERVER_ENABLED: - current_instance_asset = current_instance["folderPath"] - else: - current_instance_asset = current_instance["asset"] + + current_instance_asset = current_instance["folderPath"] if current_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) @@ -45,10 +42,8 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): "task": task_name, "variant": variant } - if AYON_SERVER_ENABLED: - data["folderPath"] = asset_name - else: - data["asset"] = asset_name + + data["folderPath"] = asset_name data.update( self.get_dynamic_data( @@ -74,10 +69,7 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): ) asset_name = get_asset_name_identifier(asset_doc) - if AYON_SERVER_ENABLED: - current_instance["folderPath"] = asset_name - else: - current_instance["asset"] = asset_name + current_instance["folderPath"] = asset_name current_instance["task"] = task_name current_instance["subset"] = subset_name diff --git a/client/ayon_core/hosts/max/plugins/publish/collect_members.py b/client/ayon_core/hosts/max/plugins/publish/collect_members.py index 7cd646e0e7..f3fde00fe0 100644 --- a/client/ayon_core/hosts/max/plugins/publish/collect_members.py +++ b/client/ayon_core/hosts/max/plugins/publish/collect_members.py @@ -13,8 +13,7 @@ class CollectMembers(pyblish.api.InstancePlugin): def process(self, instance): if instance.data["family"] == "workfile": - self.log.debug("Skipping Actions for workfile family.") - self.log.debug("{}".format(instance.data["subset"])) + self.log.debug("Skipping Collecting Members for workfile family.") return if instance.data.get("instance_node"): container = rt.GetNodeByName(instance.data["instance_node"]) diff --git a/client/ayon_core/hosts/max/plugins/publish/collect_workfile.py b/client/ayon_core/hosts/max/plugins/publish/collect_workfile.py index 446175c0ed..4dbaf28bf7 100644 --- a/client/ayon_core/hosts/max/plugins/publish/collect_workfile.py +++ b/client/ayon_core/hosts/max/plugins/publish/collect_workfile.py @@ -29,15 +29,8 @@ class CollectWorkfile(pyblish.api.InstancePlugin): data = {} # create instance - subset = instance.data["subset"] data.update({ - "subset": subset, - "asset": context.data["asset"], - "label": subset, - "publish": True, - "family": 'workfile', - "families": ['workfile'], "setMembers": [current_file], "frameStart": context.data['frameStart'], "frameEnd": context.data['frameEnd'], @@ -46,15 +39,14 @@ class CollectWorkfile(pyblish.api.InstancePlugin): }) data['representations'] = [{ - 'name': ext.lstrip("."), - 'ext': ext.lstrip("."), + 'name': ext, + 'ext': ext, 'files': file, "stagingDir": folder, }] instance.data.update(data) - self.log.info('Collected data: {}'.format(data)) - self.log.info('Collected instance: {}'.format(file)) - self.log.info('Scene path: {}'.format(current_file)) - self.log.info('staging Dir: {}'.format(folder)) - self.log.info('subset: {}'.format(subset)) + self.log.debug('Collected data: {}'.format(data)) + self.log.debug('Collected instance: {}'.format(file)) + self.log.debug('Scene path: {}'.format(current_file)) + self.log.debug('staging Dir: {}'.format(folder)) From c932042168d2a87691da6a5f6a0146e4f0116394 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 13 Feb 2024 12:53:59 +0100 Subject: [PATCH 068/573] make sure old keys are removed --- client/ayon_core/cli_commands.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/ayon_core/cli_commands.py b/client/ayon_core/cli_commands.py index 31429c488b..7e652950eb 100644 --- a/client/ayon_core/cli_commands.py +++ b/client/ayon_core/cli_commands.py @@ -84,6 +84,8 @@ class Commands: ): if src_key in os.environ and dst_key not in os.environ: os.environ[dst_key] = os.environ[src_key] + # Remove old keys, so we're sure they're not used + os.environ.pop(src_key, None) log = Logger.get_logger("CLI-publish") From 0ca94aa3c03acf63d177dde49099d1c99e08ed2d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 13 Feb 2024 13:11:03 +0100 Subject: [PATCH 069/573] bump version of global pre load plugin --- .../deadline/repository/custom/plugins/GlobalJobPreLoad.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py b/client/ayon_core/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py index 0b44c8dce3..818424b5fc 100644 --- a/client/ayon_core/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py +++ b/client/ayon_core/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py @@ -14,7 +14,7 @@ from Deadline.Scripting import ( DirectoryUtils, ProcessUtils, ) -__version__ = "1.0.0" +__version__ = "1.0.1" VERSION_REGEX = re.compile( r"(?P0|[1-9]\d*)" r"\.(?P0|[1-9]\d*)" From 5a185f4a2ad376c013aa2a5cd8ab8748f9f888bd Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 13 Feb 2024 15:03:42 +0100 Subject: [PATCH 070/573] move asset icon functions to assets widget --- .../tools/publisher/widgets/assets_widget.py | 2 +- client/ayon_core/tools/utils/__init__.py | 8 --- client/ayon_core/tools/utils/assets_widget.py | 59 ++++++++++++++++++- client/ayon_core/tools/utils/lib.py | 56 ------------------ 4 files changed, 59 insertions(+), 66 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/assets_widget.py b/client/ayon_core/tools/publisher/widgets/assets_widget.py index faad37104d..1c5016de99 100644 --- a/client/ayon_core/tools/publisher/widgets/assets_widget.py +++ b/client/ayon_core/tools/publisher/widgets/assets_widget.py @@ -5,13 +5,13 @@ from qtpy import QtWidgets, QtCore, QtGui from ayon_core.tools.utils import ( PlaceholderLineEdit, RecursiveSortFilterProxyModel, - get_asset_icon, ) from ayon_core.tools.utils.assets_widget import ( SingleSelectAssetsWidget, ASSET_ID_ROLE, ASSET_NAME_ROLE, ASSET_PATH_ROLE, + get_asset_icon, ) diff --git a/client/ayon_core/tools/utils/__init__.py b/client/ayon_core/tools/utils/__init__.py index 7be0ea5e9f..445b4d9b97 100644 --- a/client/ayon_core/tools/utils/__init__.py +++ b/client/ayon_core/tools/utils/__init__.py @@ -37,10 +37,6 @@ from .lib import ( get_qt_app, get_ayon_qt_app, get_openpype_qt_app, - get_asset_icon, - get_asset_icon_by_name, - get_asset_icon_name_from_doc, - get_asset_icon_color_from_doc, ) from .models import ( @@ -100,10 +96,6 @@ __all__ = ( "get_qt_app", "get_ayon_qt_app", "get_openpype_qt_app", - "get_asset_icon", - "get_asset_icon_by_name", - "get_asset_icon_name_from_doc", - "get_asset_icon_color_from_doc", "RecursiveSortFilterProxyModel", diff --git a/client/ayon_core/tools/utils/assets_widget.py b/client/ayon_core/tools/utils/assets_widget.py index 7bacf3291d..a97a289737 100644 --- a/client/ayon_core/tools/utils/assets_widget.py +++ b/client/ayon_core/tools/utils/assets_widget.py @@ -10,6 +10,7 @@ from ayon_core.client import ( ) from ayon_core.style import ( get_default_tools_icon_color, + get_default_entity_icon_color, ) from ayon_core.tools.flickcharm import FlickCharm @@ -21,7 +22,7 @@ from .widgets import PlaceholderLineEdit from .models import RecursiveSortFilterProxyModel from .lib import ( DynamicQThread, - get_asset_icon + get_qta_icon_by_name_and_color ) ASSET_ID_ROLE = QtCore.Qt.UserRole + 1 @@ -31,6 +32,62 @@ ASSET_UNDERLINE_COLORS_ROLE = QtCore.Qt.UserRole + 4 ASSET_PATH_ROLE = QtCore.Qt.UserRole + 5 +def get_default_asset_icon_name(has_children): + if has_children: + return "fa.folder" + return "fa.folder-o" + + +def get_asset_icon_color_from_doc(asset_doc): + if asset_doc: + return asset_doc["data"].get("color") + return None + + +def get_asset_icon_name_from_doc(asset_doc): + if asset_doc: + return asset_doc["data"].get("icon") + return None + + +def get_asset_icon_color(asset_doc): + icon_color = get_asset_icon_color_from_doc(asset_doc) + if icon_color: + return icon_color + return get_default_entity_icon_color() + + +def get_asset_icon_by_name(icon_name, icon_color, has_children=False): + if not icon_name: + icon_name = get_default_asset_icon_name(has_children) + + if icon_color: + icon_color = QtGui.QColor(icon_color) + else: + icon_color = get_default_entity_icon_color() + icon = get_qta_icon_by_name_and_color(icon_name, icon_color) + if icon is not None: + return icon + return get_qta_icon_by_name_and_color( + get_default_asset_icon_name(has_children), + icon_color + ) + + +def get_asset_icon_name(asset_doc, has_children=True): + icon_name = get_asset_icon_name_from_doc(asset_doc) + if icon_name: + return icon_name + return get_default_asset_icon_name(has_children) + + +def get_asset_icon(asset_doc, has_children=False): + 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) + + class _AssetsView(TreeViewSpinner, DeselectableTreeView): """Asset items view. diff --git a/client/ayon_core/tools/utils/lib.py b/client/ayon_core/tools/utils/lib.py index b7edd6be71..e785cec390 100644 --- a/client/ayon_core/tools/utils/lib.py +++ b/client/ayon_core/tools/utils/lib.py @@ -234,62 +234,6 @@ def get_qta_icon_by_name_and_color(icon_name, icon_color): return icon -def get_asset_icon_name(asset_doc, has_children=True): - icon_name = get_asset_icon_name_from_doc(asset_doc) - if icon_name: - return icon_name - return get_default_asset_icon_name(has_children) - - -def get_asset_icon_color(asset_doc): - icon_color = get_asset_icon_color_from_doc(asset_doc) - if icon_color: - return icon_color - return get_default_entity_icon_color() - - -def get_default_asset_icon_name(has_children): - if has_children: - return "fa.folder" - return "fa.folder-o" - - -def get_asset_icon_name_from_doc(asset_doc): - if asset_doc: - return asset_doc["data"].get("icon") - return None - - -def get_asset_icon_color_from_doc(asset_doc): - if asset_doc: - return asset_doc["data"].get("color") - return None - - -def get_asset_icon_by_name(icon_name, icon_color, has_children=False): - if not icon_name: - icon_name = get_default_asset_icon_name(has_children) - - if icon_color: - icon_color = QtGui.QColor(icon_color) - else: - icon_color = get_default_entity_icon_color() - icon = get_qta_icon_by_name_and_color(icon_name, icon_color) - if icon is not None: - return icon - return get_qta_icon_by_name_and_color( - get_default_asset_icon_name(has_children), - icon_color - ) - - -def get_asset_icon(asset_doc, has_children=False): - 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) - - def get_default_task_icon(color=None): if color is None: color = get_default_entity_icon_color() From 9d7dd0d75a4220f4677403e7e7da9ec75fec1c78 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 13 Feb 2024 16:41:42 +0100 Subject: [PATCH 071/573] remove settings conversion for timers manager --- client/ayon_core/settings/ayon_settings.py | 33 ++++++---------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index cea0158159..0fe4095a92 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -96,26 +96,6 @@ def _convert_kitsu_system_settings( output["modules"]["kitsu"] = kitsu_settings -def _convert_timers_manager_system_settings( - ayon_settings, output, addon_versions, default_settings -): - enabled = addon_versions.get("timers_manager") is not None - manager_settings = default_settings["modules"]["timers_manager"] - manager_settings["enabled"] = enabled - if enabled: - ayon_manager = ayon_settings["timers_manager"] - manager_settings.update({ - key: ayon_manager[key] - for key in { - "auto_stop", - "full_time", - "message_time", - "disregard_publishing" - } - }) - output["modules"]["timers_manager"] = manager_settings - - def _convert_clockify_system_settings( ayon_settings, output, addon_versions, default_settings ): @@ -167,21 +147,24 @@ def _convert_modules_system( # TODO add 'enabled' values for func in ( _convert_kitsu_system_settings, - _convert_timers_manager_system_settings, _convert_clockify_system_settings, _convert_deadline_system_settings, _convert_royalrender_system_settings, ): func(ayon_settings, output, addon_versions, default_settings) + for key in { + "timers_manager", + }: + if addon_versions.get(key): + output[key] = ayon_settings + else: + output.pop(key, None) + modules_settings = output["modules"] for module_name in ( "sync_server", - "log_viewer", - "standalonepublish_tool", - "project_manager", "job_queue", - "avalon", "addon_paths", ): settings = default_settings["modules"][module_name] From 221cd01ae2b6759bb6312819b1cd68ff52e02be1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 13 Feb 2024 16:42:01 +0100 Subject: [PATCH 072/573] use 'AYONAddon' for times manager addon --- .../modules/timers_manager/timers_manager.py | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/client/ayon_core/modules/timers_manager/timers_manager.py b/client/ayon_core/modules/timers_manager/timers_manager.py index daba0cead9..f8f0e4a526 100644 --- a/client/ayon_core/modules/timers_manager/timers_manager.py +++ b/client/ayon_core/modules/timers_manager/timers_manager.py @@ -3,8 +3,8 @@ import platform from ayon_core.client import get_asset_by_name -from ayon_core.modules import ( - OpenPypeModule, +from ayon_core.addon import ( + AYONAddon, ITrayService, IPluginPaths ) @@ -76,7 +76,7 @@ class ExampleTimersManagerConnector: class TimersManager( - OpenPypeModule, + AYONAddon, ITrayService, IPluginPaths ): @@ -99,23 +99,27 @@ class TimersManager( "start_timer" ) - def initialize(self, modules_settings): - timers_settings = modules_settings[self.name] + def initialize(self, studio_settings): + timers_settings = studio_settings.get(self.name) + enabled = timers_settings is not None - self.enabled = timers_settings["enabled"] + auto_stop = False + full_time = 0 + message_time = 0 + if enabled: + # When timer will stop if idle manager is running (minutes) + full_time = int(timers_settings["full_time"] * 60) + # How many minutes before the timer is stopped will popup the message + message_time = int(timers_settings["message_time"] * 60) - # When timer will stop if idle manager is running (minutes) - full_time = int(timers_settings["full_time"] * 60) - # How many minutes before the timer is stopped will popup the message - message_time = int(timers_settings["message_time"] * 60) - - auto_stop = timers_settings["auto_stop"] - platform_name = platform.system().lower() - # Turn of auto stop on MacOs because pynput requires root permissions - # and on linux can cause thread locks on application close - if full_time <= 0 or platform_name in ("darwin", "linux"): - auto_stop = False + auto_stop = timers_settings["auto_stop"] + platform_name = platform.system().lower() + # Turn of auto stop on MacOs because pynput requires root permissions + # and on linux can cause thread locks on application close + if full_time <= 0 or platform_name in ("darwin", "linux"): + auto_stop = False + self.enabled = enabled self.auto_stop = auto_stop self.time_show_message = full_time - message_time self.time_stop_timer = full_time From 40dc41a7a98572d0e3b2ba3dcdb90df85f4ea78c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 13 Feb 2024 16:52:51 +0100 Subject: [PATCH 073/573] remove conversion of traypublisher settings --- client/ayon_core/settings/ayon_settings.py | 52 ---------------------- 1 file changed, 52 deletions(-) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index cea0158159..fe54714f75 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -551,58 +551,6 @@ def _convert_traypublisher_project_settings(ayon_settings, output): _convert_host_imageio(ayon_traypublisher) - ayon_editorial_simple = ( - ayon_traypublisher["editorial_creators"]["editorial_simple"] - ) - # Subset -> Product type conversion - if "product_type_presets" in ayon_editorial_simple: - family_presets = ayon_editorial_simple.pop("product_type_presets") - for item in family_presets: - item["family"] = item.pop("product_type") - ayon_editorial_simple["family_presets"] = family_presets - - if "shot_metadata_creator" in ayon_editorial_simple: - shot_metadata_creator = ayon_editorial_simple.pop( - "shot_metadata_creator" - ) - if isinstance(shot_metadata_creator["clip_name_tokenizer"], dict): - shot_metadata_creator["clip_name_tokenizer"] = [ - {"name": "_sequence_", "regex": "(sc\\d{3})"}, - {"name": "_shot_", "regex": "(sh\\d{3})"}, - ] - ayon_editorial_simple.update(shot_metadata_creator) - - ayon_editorial_simple["clip_name_tokenizer"] = { - item["name"]: item["regex"] - for item in ayon_editorial_simple["clip_name_tokenizer"] - } - - if "shot_subset_creator" in ayon_editorial_simple: - ayon_editorial_simple.update( - ayon_editorial_simple.pop("shot_subset_creator")) - for item in ayon_editorial_simple["shot_hierarchy"]["parents"]: - item["type"] = item.pop("parent_type") - - # Simple creators - ayon_simple_creators = ayon_traypublisher["simple_creators"] - for item in ayon_simple_creators: - if "product_type" not in item: - break - item["family"] = item.pop("product_type") - - shot_add_tasks = ayon_editorial_simple["shot_add_tasks"] - - # TODO: backward compatibility and remove in future - if isinstance(shot_add_tasks, dict): - shot_add_tasks = [] - - # aggregate shot_add_tasks items - new_shot_add_tasks = { - item["name"]: {"type": item["task_type"]} - for item in shot_add_tasks - } - ayon_editorial_simple["shot_add_tasks"] = new_shot_add_tasks - output["traypublisher"] = ayon_traypublisher From 6c158157cc533f386d1255ebb035cee05612ff04 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 13 Feb 2024 16:53:14 +0100 Subject: [PATCH 074/573] modify SettingsCreator to match new settings --- client/ayon_core/hosts/traypublisher/api/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/traypublisher/api/plugin.py b/client/ayon_core/hosts/traypublisher/api/plugin.py index 77a8f23d2e..d3d7f80e89 100644 --- a/client/ayon_core/hosts/traypublisher/api/plugin.py +++ b/client/ayon_core/hosts/traypublisher/api/plugin.py @@ -311,7 +311,7 @@ class SettingsCreator(TrayPublishCreator): @classmethod def from_settings(cls, item_data): identifier = item_data["identifier"] - family = item_data["family"] + family = item_data["product_type"] if not identifier: identifier = "settings_{}".format(family) return type( From 197d3d6786a320289e9765c3ac4152bc36648325 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 13 Feb 2024 16:55:45 +0100 Subject: [PATCH 075/573] change how setting are applied in editorial creator --- .../hosts/traypublisher/api/editorial.py | 24 +++++++----- .../plugins/create/create_editorial.py | 39 ++++++++----------- 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/client/ayon_core/hosts/traypublisher/api/editorial.py b/client/ayon_core/hosts/traypublisher/api/editorial.py index d84a7200c8..2c77a66ac7 100644 --- a/client/ayon_core/hosts/traypublisher/api/editorial.py +++ b/client/ayon_core/hosts/traypublisher/api/editorial.py @@ -16,25 +16,31 @@ class ShotMetadataSolver: NO_DECOR_PATERN = re.compile(r"\{([a-z]*?)\}") - # presets - clip_name_tokenizer = None - shot_rename = True - shot_hierarchy = None - shot_add_tasks = None + def __init__(self, logger): + self.clip_name_tokenizer = [] + self.shot_rename = { + "enabled": False, + "shot_rename_template": "", + } + self.shot_hierarchy = { + "enabled": False, + "parents": [], + "parents_path": "", + } + self.shot_add_tasks = [] + self.log = logger - def __init__( + def update_data( self, clip_name_tokenizer, shot_rename, shot_hierarchy, - shot_add_tasks, - logger + shot_add_tasks ): self.clip_name_tokenizer = clip_name_tokenizer self.shot_rename = shot_rename self.shot_hierarchy = shot_hierarchy self.shot_add_tasks = shot_add_tasks - self.log = logger def _rename_template(self, data): """Shot renaming function diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py index 51a67a871e..791d989070 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py @@ -174,35 +174,28 @@ Supporting publishing new shots to project or updating already created. Publishing will create OTIO file. """ icon = "fa.file" + product_type_presets = [] - def __init__( - self, project_settings, *args, **kwargs - ): - super(EditorialSimpleCreator, self).__init__( - project_settings, *args, **kwargs - ) + def __init__(self, *args, **kwargs): + self._shot_metadata_solver = ShotMetadataSolver(self.log) + super(EditorialSimpleCreator, self).__init__(*args, **kwargs) + + def apply_settings(self, project_settings): editorial_creators = deepcopy( project_settings["traypublisher"]["editorial_creators"] ) - # get this creator settings by identifier - self._creator_settings = editorial_creators.get(self.identifier) + creator_settings = editorial_creators.get(self.identifier) - clip_name_tokenizer = self._creator_settings["clip_name_tokenizer"] - shot_rename = self._creator_settings["shot_rename"] - shot_hierarchy = self._creator_settings["shot_hierarchy"] - shot_add_tasks = self._creator_settings["shot_add_tasks"] - - self._shot_metadata_solver = ShotMetadataSolver( - clip_name_tokenizer, - shot_rename, - shot_hierarchy, - shot_add_tasks, - self.log + self._shot_metadata_solver.update_data( + creator_settings["clip_name_tokenizer"], + creator_settings["shot_rename"], + creator_settings["shot_hierarchy"], + creator_settings["shot_add_tasks"] ) - - # try to set main attributes from settings - if self._creator_settings.get("default_variants"): - self.default_variants = self._creator_settings["default_variants"] + self.product_type_presets = creator_settings["product_type_presets"] + default_variants = creator_settings.get("default_variants") + if default_variants: + self.default_variants = default_variants def create(self, subset_name, instance_data, pre_create_data): allowed_family_presets = self._get_allowed_family_presets( From 0669ef30c8560a6d19a03376cf8f443e3d33db92 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 13 Feb 2024 16:56:03 +0100 Subject: [PATCH 076/573] use new settings structure in ShotMetadataSolver --- .../hosts/traypublisher/api/editorial.py | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/client/ayon_core/hosts/traypublisher/api/editorial.py b/client/ayon_core/hosts/traypublisher/api/editorial.py index 2c77a66ac7..6153bc5752 100644 --- a/client/ayon_core/hosts/traypublisher/api/editorial.py +++ b/client/ayon_core/hosts/traypublisher/api/editorial.py @@ -92,7 +92,9 @@ class ShotMetadataSolver: search_text = parent_name + clip_name - for token_key, pattern in self.clip_name_tokenizer.items(): + for clip_name_item in self.clip_name_tokenizer: + token_key = clip_name_item["name"] + pattern = clip_name_item["regex"] p = re.compile(pattern) match = p.findall(search_text) if not match: @@ -143,11 +145,11 @@ class ShotMetadataSolver: )) _parent_tokens_type = { - parent_token["name"]: parent_token["type"] + parent_token["name"]: parent_token["parent_type"] for parent_token in hierarchy_parents } for _index, _parent in enumerate( - shot_hierarchy["parents_path"].split("/") + shot_hierarchy["parents_path"].split("/") ): # format parent token with value which is formatted try: @@ -268,22 +270,22 @@ class ShotMetadataSolver: """ tasks_to_add = {} - project_tasks = project_doc["config"]["tasks"] - for task_name, task_data in self.shot_add_tasks.items(): - _task_data = deepcopy(task_data) + project_task_types = project_doc["config"]["tasks"] + for task_item in self.shot_add_tasks: + task_name = task_item["name"] + task_type = task_item["task_type"] # check if task type in project task types - if _task_data["type"] in project_tasks.keys(): - tasks_to_add[task_name] = _task_data - else: + if task_type not in project_task_types.keys(): raise KeyError( "Missing task type `{}` for `{}` is not" " existing in `{}``".format( - _task_data["type"], + task_type, task_name, - list(project_tasks.keys()) + list(project_task_types.keys()) ) ) + tasks_to_add[task_name] = {"type": task_type} return tasks_to_add From 551184e3c23662b2b9197b0ebd8f8da528675c12 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 13 Feb 2024 16:56:18 +0100 Subject: [PATCH 077/573] change variables used during create editorial --- .../plugins/create/create_editorial.py | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py index 791d989070..d6501e65a2 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py @@ -198,15 +198,18 @@ or updating already created. Publishing will create OTIO file. self.default_variants = default_variants def create(self, subset_name, instance_data, pre_create_data): - allowed_family_presets = self._get_allowed_family_presets( + allowed_product_type_presets = self._get_allowed_product_type_presets( pre_create_data) + product_types = { + item["product_type"] + for item in self.product_type_presets + } clip_instance_properties = { - k: v for k, v in pre_create_data.items() + k: v + for k, v in pre_create_data.items() if k != "sequence_filepath_data" - if k not in [ - i["family"] for i in self._creator_settings["family_presets"] - ] + if k not in product_types } asset_name = instance_data["folderPath"] @@ -248,7 +251,7 @@ or updating already created. Publishing will create OTIO file. otio_timeline, media_path, clip_instance_properties, - allowed_family_presets, + allowed_product_type_presets, os.path.basename(seq_path), first_otio_timeline ) @@ -348,7 +351,7 @@ or updating already created. Publishing will create OTIO file. otio_timeline, media_path, instance_data, - family_presets, + product_type_presets, sequence_file_name, first_otio_timeline=None ): @@ -358,7 +361,7 @@ or updating already created. Publishing will create OTIO file. otio_timeline (otio.Timeline): otio timeline object media_path (str): media file path string instance_data (dict): clip instance data - family_presets (list): list of dict settings subset presets + product_type_presets (list): list of dict settings subset presets """ tracks = [ @@ -404,17 +407,17 @@ or updating already created. Publishing will create OTIO file. "instance_id": None } - for _fpreset in family_presets: + for product_type_preset in product_type_presets: # exclude audio family if no audio stream if ( - _fpreset["family"] == "audio" + product_type_preset["product_type"] == "audio" and not media_data.get("audio") ): continue instance = self._make_subset_instance( otio_clip, - _fpreset, + product_type_preset, deepcopy(base_instance_data), parenting_data ) @@ -526,7 +529,7 @@ or updating already created. Publishing will create OTIO file. def _make_subset_instance( self, otio_clip, - preset, + product_type_preset, instance_data, parenting_data ): @@ -534,16 +537,16 @@ or updating already created. Publishing will create OTIO file. Args: otio_clip (otio.Clip): otio clip object - preset (dict): single family preset + product_type_preset (dict): single family preset instance_data (dict): instance data parenting_data (dict): shot instance parent data Returns: CreatedInstance: creator instance object """ - family = preset["family"] + family = product_type_preset["product_type"] label = self._make_subset_naming( - preset, + product_type_preset, instance_data ) instance_data["label"] = label @@ -562,11 +565,11 @@ or updating already created. Publishing will create OTIO file. else: # add review family if defined instance_data.update({ - "outputFileType": preset["output_file_type"], + "outputFileType": product_type_preset["output_file_type"], "parent_instance_id": parenting_data["instance_id"], "creator_attributes": { "parent_instance": parenting_data["instance_label"], - "add_review_family": preset.get("review") + "add_review_family": product_type_preset.get("review") } }) @@ -578,15 +581,11 @@ or updating already created. Publishing will create OTIO file. return c_instance - def _make_subset_naming( - self, - preset, - instance_data - ): + def _make_subset_naming(self, product_type_preset, instance_data): """ Subset name maker Args: - preset (dict): single preset item + product_type_preset (dict): single preset item instance_data (dict): instance data Returns: @@ -595,10 +594,10 @@ or updating already created. Publishing will create OTIO file. asset_name = instance_data["creator_attributes"]["folderPath"] variant_name = instance_data["variant"] - family = preset["family"] + family = product_type_preset["product_type"] # get variant name from preset or from inheritance - _variant_name = preset.get("variant") or variant_name + _variant_name = product_type_preset.get("variant") or variant_name # subset name subset_name = "{}{}".format( @@ -756,7 +755,7 @@ or updating already created. Publishing will create OTIO file. "sourceOut": int(source_out) } - def _get_allowed_family_presets(self, pre_create_data): + def _get_allowed_product_type_presets(self, pre_create_data): """ Filter out allowed family presets. Args: @@ -766,10 +765,11 @@ or updating already created. Publishing will create OTIO file. list: lit of dict with preset items """ return [ - {"family": "shot"}, + {"product_type": "shot"}, *[ - preset for preset in self._creator_settings["family_presets"] - if pre_create_data[preset["family"]] + preset + for preset in self.product_type_presets + if pre_create_data[preset["product_type"]] ] ] @@ -846,8 +846,8 @@ or updating already created. Publishing will create OTIO file. ] # add variants swithers attr_defs.extend( - BoolDef(_var["family"], label=_var["family"]) - for _var in self._creator_settings["family_presets"] + BoolDef(item["product_type"], label=item["product_type"]) + for item in self.product_type_presets ) attr_defs.append(UISeparatorDef()) From 8bb4db39d0eed4dac6e56b2281d5bcb2b611376c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 13 Feb 2024 17:11:02 +0100 Subject: [PATCH 078/573] fix missing bracket Co-authored-by: Mustafa Taher --- .../deadline/repository/custom/plugins/GlobalJobPreLoad.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py b/client/ayon_core/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py index 818424b5fc..1565b2c496 100644 --- a/client/ayon_core/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py +++ b/client/ayon_core/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py @@ -476,7 +476,7 @@ def inject_ayon_environment(deadlinePlugin): # Support backwards compatible keys for key, env_keys in ( ("project", ["AYON_PROJECT_NAME", "AVALON_PROJECT"]), - ("asset", ["AYON_FOLDER_PATH", "AVALON_ASSET"], + ("asset", ["AYON_FOLDER_PATH", "AVALON_ASSET"]), ("task", ["AYON_TASK_NAME", "AVALON_TASK"]), ("app", ["AYON_APP_NAME", "AVALON_APP_NAME"]), ): From de643a13d837de57d88e8598dcaccd48f23aff74 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 13 Feb 2024 17:21:41 +0100 Subject: [PATCH 079/573] removed outdated shotgrid conversion --- client/ayon_core/settings/ayon_settings.py | 28 ---------------------- 1 file changed, 28 deletions(-) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index fe54714f75..dd079bec84 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -600,33 +600,6 @@ def _convert_kitsu_project_settings(ayon_settings, output): output["kitsu"] = ayon_kitsu_settings -def _convert_shotgrid_project_settings(ayon_settings, output): - if "shotgrid" not in ayon_settings: - return - - ayon_shotgrid = ayon_settings["shotgrid"] - # This means that a different variant of addon is used - if "leecher_backend_url" not in ayon_shotgrid: - return - - for key in { - "leecher_backend_url", - "filter_projects_by_login", - "shotgrid_settings", - "leecher_manager_url", - }: - ayon_shotgrid.pop(key) - - asset_field = ayon_shotgrid["fields"]["asset"] - asset_field["type"] = asset_field.pop("asset_type") - - task_field = ayon_shotgrid["fields"]["task"] - if "task" in task_field: - task_field["step"] = task_field.pop("task") - - output["shotgrid"] = ayon_settings["shotgrid"] - - def _convert_slack_project_settings(ayon_settings, output): if "slack" not in ayon_settings: return @@ -848,7 +821,6 @@ def convert_project_settings(ayon_settings, default_settings): _convert_royalrender_project_settings(ayon_settings, output) _convert_kitsu_project_settings(ayon_settings, output) - _convert_shotgrid_project_settings(ayon_settings, output) _convert_slack_project_settings(ayon_settings, output) _convert_global_project_settings(ayon_settings, output, default_settings) From 4e2ee7aaac2c9f00b328204e4463a7d10671b4b8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 13 Feb 2024 17:30:39 +0100 Subject: [PATCH 080/573] removed webpublisher settings conversion --- client/ayon_core/settings/ayon_settings.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index dd079bec84..ff97d9726b 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -560,15 +560,6 @@ def _convert_webpublisher_project_settings(ayon_settings, output): ayon_webpublisher = ayon_settings["webpublisher"] _convert_host_imageio(ayon_webpublisher) - - ayon_publish = ayon_webpublisher["publish"] - - ayon_collect_files = ayon_publish["CollectPublishedFiles"] - ayon_collect_files["task_type_to_family"] = { - item["name"]: item["value"] - for item in ayon_collect_files["task_type_to_family"] - } - output["webpublisher"] = ayon_webpublisher From 342c84784a275b3b2d98a235f5c17811a6e6ec7f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 13 Feb 2024 17:33:39 +0100 Subject: [PATCH 081/573] removed kitsu settings conversion --- client/ayon_core/settings/ayon_settings.py | 32 ---------------------- 1 file changed, 32 deletions(-) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index ff97d9726b..2978202b51 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -82,20 +82,6 @@ def _convert_general(ayon_settings, output, default_settings): } -def _convert_kitsu_system_settings( - ayon_settings, output, addon_versions, default_settings -): - if "kitsu" in ayon_settings: - output["kitsu"] = ayon_settings["kitsu"] - - enabled = addon_versions.get("kitsu") is not None - kitsu_settings = default_settings["modules"]["kitsu"] - kitsu_settings["enabled"] = enabled - if enabled: - kitsu_settings["server"] = ayon_settings["kitsu"]["server"] - output["modules"]["kitsu"] = kitsu_settings - - def _convert_timers_manager_system_settings( ayon_settings, output, addon_versions, default_settings ): @@ -166,7 +152,6 @@ def _convert_modules_system( # TODO add all modules # TODO add 'enabled' values for func in ( - _convert_kitsu_system_settings, _convert_timers_manager_system_settings, _convert_clockify_system_settings, _convert_deadline_system_settings, @@ -575,22 +560,6 @@ def _convert_royalrender_project_settings(ayon_settings, output): } -def _convert_kitsu_project_settings(ayon_settings, output): - if "kitsu" not in ayon_settings: - return - - ayon_kitsu_settings = ayon_settings["kitsu"] - ayon_kitsu_settings.pop("server") - - integrate_note = ayon_kitsu_settings["publish"]["IntegrateKitsuNote"] - status_change_conditions = integrate_note["status_change_conditions"] - if "product_type_requirements" in status_change_conditions: - status_change_conditions["family_requirements"] = ( - status_change_conditions.pop("product_type_requirements")) - - output["kitsu"] = ayon_kitsu_settings - - def _convert_slack_project_settings(ayon_settings, output): if "slack" not in ayon_settings: return @@ -811,7 +780,6 @@ def convert_project_settings(ayon_settings, default_settings): _convert_webpublisher_project_settings(ayon_settings, output) _convert_royalrender_project_settings(ayon_settings, output) - _convert_kitsu_project_settings(ayon_settings, output) _convert_slack_project_settings(ayon_settings, output) _convert_global_project_settings(ayon_settings, output, default_settings) From a1a325002804e4fa6a131bcae3e202c3ed9ec868 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 13 Feb 2024 17:34:07 +0100 Subject: [PATCH 082/573] removed slack settings conversion --- client/ayon_core/settings/ayon_settings.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index 2978202b51..19a32520b8 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -560,19 +560,6 @@ def _convert_royalrender_project_settings(ayon_settings, output): } -def _convert_slack_project_settings(ayon_settings, output): - if "slack" not in ayon_settings: - return - - ayon_slack = ayon_settings["slack"] - ayon_slack.pop("enabled", None) - for profile in ayon_slack["publish"]["CollectSlackFamilies"]["profiles"]: - profile["tasks"] = profile.pop("task_names") - profile["subsets"] = profile.pop("subset_names") - - output["slack"] = ayon_slack - - def _convert_global_project_settings(ayon_settings, output, default_settings): if "core" not in ayon_settings: return @@ -780,7 +767,6 @@ def convert_project_settings(ayon_settings, default_settings): _convert_webpublisher_project_settings(ayon_settings, output) _convert_royalrender_project_settings(ayon_settings, output) - _convert_slack_project_settings(ayon_settings, output) _convert_global_project_settings(ayon_settings, output, default_settings) From ad74bafc4d32476ca38ffe79fb6d8c287fe9197d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 13 Feb 2024 17:56:19 +0100 Subject: [PATCH 083/573] removed '_convert_host_imageio' --- client/ayon_core/settings/ayon_settings.py | 38 ---------------------- 1 file changed, 38 deletions(-) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index fe54714f75..27f38fbb5e 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -52,26 +52,6 @@ def _convert_color(color_value): return color_value -def _convert_host_imageio(host_settings): - if "imageio" not in host_settings: - return - - # --- imageio --- - ayon_imageio = host_settings["imageio"] - # TODO remove when fixed on server - if "ocio_config" in ayon_imageio["ocio_config"]: - ayon_imageio["ocio_config"]["filepath"] = ( - ayon_imageio["ocio_config"].pop("ocio_config") - ) - # Convert file rules - imageio_file_rules = ayon_imageio["file_rules"] - new_rules = {} - for rule in imageio_file_rules["rules"]: - name = rule.pop("name") - new_rules[name] = rule - imageio_file_rules["rules"] = new_rules - - def _convert_general(ayon_settings, output, default_settings): output["core"] = ayon_settings["core"] version_check_interval = ( @@ -242,7 +222,6 @@ def _convert_blender_project_settings(ayon_settings, output): if "blender" not in ayon_settings: return ayon_blender = ayon_settings["blender"] - _convert_host_imageio(ayon_blender) output["blender"] = ayon_blender @@ -252,7 +231,6 @@ def _convert_celaction_project_settings(ayon_settings, output): return ayon_celaction = ayon_settings["celaction"] - _convert_host_imageio(ayon_celaction) output["celaction"] = ayon_celaction @@ -263,7 +241,6 @@ def _convert_flame_project_settings(ayon_settings, output): ayon_flame = ayon_settings["flame"] - _convert_host_imageio(ayon_flame) output["flame"] = ayon_flame @@ -272,7 +249,6 @@ def _convert_fusion_project_settings(ayon_settings, output): return ayon_fusion = ayon_settings["fusion"] - _convert_host_imageio(ayon_fusion) output["fusion"] = ayon_fusion @@ -283,8 +259,6 @@ def _convert_maya_project_settings(ayon_settings, output): ayon_maya = ayon_settings["maya"] - _convert_host_imageio(ayon_maya) - output["maya"] = ayon_maya @@ -294,8 +268,6 @@ def _convert_3dsmax_project_settings(ayon_settings, output): ayon_max = ayon_settings["max"] - _convert_host_imageio(ayon_max) - output["max"] = ayon_max @@ -442,7 +414,6 @@ def _convert_nuke_project_settings(ayon_settings, output): # --- ImageIO --- # NOTE 'monitorOutLut' is maybe not yet in v3 (ut should be) - _convert_host_imageio(ayon_nuke) ayon_imageio = ayon_nuke["imageio"] # workfile @@ -491,7 +462,6 @@ def _convert_hiero_project_settings(ayon_settings, output): return ayon_hiero = ayon_settings["hiero"] - _convert_host_imageio(ayon_hiero) new_gui_filters = {} for item in ayon_hiero.pop("filters", []): @@ -521,7 +491,6 @@ def _convert_photoshop_project_settings(ayon_settings, output): return ayon_photoshop = ayon_settings["photoshop"] - _convert_host_imageio(ayon_photoshop) output["photoshop"] = ayon_photoshop @@ -530,7 +499,6 @@ def _convert_substancepainter_project_settings(ayon_settings, output): return ayon_substance_painter = ayon_settings["substancepainter"] - _convert_host_imageio(ayon_substance_painter) output["substancepainter"] = ayon_substance_painter @@ -539,7 +507,6 @@ def _convert_tvpaint_project_settings(ayon_settings, output): return ayon_tvpaint = ayon_settings["tvpaint"] - _convert_host_imageio(ayon_tvpaint) output["tvpaint"] = ayon_tvpaint @@ -549,8 +516,6 @@ def _convert_traypublisher_project_settings(ayon_settings, output): ayon_traypublisher = ayon_settings["traypublisher"] - _convert_host_imageio(ayon_traypublisher) - output["traypublisher"] = ayon_traypublisher @@ -559,7 +524,6 @@ def _convert_webpublisher_project_settings(ayon_settings, output): return ayon_webpublisher = ayon_settings["webpublisher"] - _convert_host_imageio(ayon_webpublisher) ayon_publish = ayon_webpublisher["publish"] @@ -646,7 +610,6 @@ def _convert_global_project_settings(ayon_settings, output, default_settings): ayon_core = ayon_settings["core"] - _convert_host_imageio(ayon_core) # Publish conversion ayon_publish = ayon_core["publish"] @@ -830,7 +793,6 @@ def convert_project_settings(ayon_settings, default_settings): for key in exact_match: if key in ayon_settings: output[key] = ayon_settings[key] - _convert_host_imageio(output[key]) _convert_blender_project_settings(ayon_settings, output) _convert_celaction_project_settings(ayon_settings, output) From 62a6e6c3afbba1f9953052550b3633cd94922148 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 13 Feb 2024 17:59:18 +0100 Subject: [PATCH 084/573] modified code to use new imageio structure --- client/ayon_core/pipeline/colorspace.py | 45 +++++++++++++------------ 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index d77f301498..70b7678d0a 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -254,7 +254,7 @@ def get_imageio_file_rules_colorspace_from_filepath( # match file rule from path colorspace_name = None - for file_rule in file_rules.values(): + for file_rule in file_rules: pattern = file_rule["pattern"] extension = file_rule["ext"] ext_match = re.match( @@ -281,7 +281,7 @@ def get_config_file_rules_colorspace_from_filepath(config_path, filepath): filepath (str): path leading to a file Returns: - Any[str, None]: matching colorspace name + Union[str, None]: matching colorspace name """ if not compatibility_check(): # python environment is not compatible with PyOpenColorIO @@ -837,13 +837,13 @@ def get_imageio_config( if override_global_config: config_data = _get_config_data( - host_ocio_config["filepath"], formatting_data, env + host_ocio_config["ocio_config"], formatting_data, env ) else: # get config path from global config_global = imageio_global["ocio_config"] config_data = _get_config_data( - config_global["filepath"], formatting_data, env + config_global["ocio_config"], formatting_data, env ) if not config_data: @@ -918,28 +918,13 @@ def get_imageio_file_rules(project_name, host_name, project_settings=None): Defaults to None. Returns: - dict: file rules data + list[dict[str, Any]]: file rules data """ project_settings = project_settings or get_project_settings(project_name) imageio_global, imageio_host = _get_imageio_settings( project_settings, host_name) - # get file rules from global and host_name - frules_global = imageio_global["file_rules"] - activate_global_rules = ( - frules_global.get("activate_global_file_rules", False) - # TODO: remove this in future - backward compatibility - or frules_global.get("enabled") - ) - global_rules = frules_global["rules"] - - if not activate_global_rules: - log.info( - "Colorspace global file rules are disabled." - ) - global_rules = {} - # host is optional, some might not have any settings frules_host = imageio_host.get("file_rules", {}) @@ -949,8 +934,24 @@ def get_imageio_file_rules(project_name, host_name, project_settings=None): # TODO: remove this in future - backward compatibility activate_host_rules = frules_host.get("enabled", False) - # return host rules if activated or global rules - return frules_host["rules"] if activate_host_rules else global_rules + if activate_host_rules: + return frules_host["rules"] + + # get file rules from global and host_name + frules_global = imageio_global["file_rules"] + activate_global_rules = ( + frules_global.get("activate_global_file_rules", False) + # TODO: remove this in future - backward compatibility + or frules_global.get("enabled") + ) + + if not activate_global_rules: + log.info( + "Colorspace global file rules are disabled." + ) + return [] + + return frules_global["rules"] def get_remapped_colorspace_to_native( From 85f8392ca8137c4ad002d7963d6e35df53d44af8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 13 Feb 2024 18:08:10 +0100 Subject: [PATCH 085/573] fix key used to get ocio config path --- client/ayon_core/pipeline/colorspace.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index 70b7678d0a..1b795e1c39 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -837,13 +837,13 @@ def get_imageio_config( if override_global_config: config_data = _get_config_data( - host_ocio_config["ocio_config"], formatting_data, env + host_ocio_config["filepath"], formatting_data, env ) else: # get config path from global config_global = imageio_global["ocio_config"] config_data = _get_config_data( - config_global["ocio_config"], formatting_data, env + config_global["filepath"], formatting_data, env ) if not config_data: From 2f462dac7aa5475a8b0eb2802ebf4cfbafc5317d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 13 Feb 2024 18:42:10 +0100 Subject: [PATCH 086/573] keep only 'get_asset_icon' as public function --- client/ayon_core/tools/utils/assets_widget.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/client/ayon_core/tools/utils/assets_widget.py b/client/ayon_core/tools/utils/assets_widget.py index a97a289737..4af932a054 100644 --- a/client/ayon_core/tools/utils/assets_widget.py +++ b/client/ayon_core/tools/utils/assets_widget.py @@ -32,26 +32,26 @@ ASSET_UNDERLINE_COLORS_ROLE = QtCore.Qt.UserRole + 4 ASSET_PATH_ROLE = QtCore.Qt.UserRole + 5 -def get_default_asset_icon_name(has_children): +def _get_default_asset_icon_name(has_children): if has_children: return "fa.folder" return "fa.folder-o" -def get_asset_icon_color_from_doc(asset_doc): +def _get_asset_icon_color_from_doc(asset_doc): if asset_doc: return asset_doc["data"].get("color") return None -def get_asset_icon_name_from_doc(asset_doc): +def _get_asset_icon_name_from_doc(asset_doc): if asset_doc: return asset_doc["data"].get("icon") return None -def get_asset_icon_color(asset_doc): - icon_color = get_asset_icon_color_from_doc(asset_doc) +def _get_asset_icon_color(asset_doc): + icon_color = _get_asset_icon_color_from_doc(asset_doc) if icon_color: return icon_color return get_default_entity_icon_color() @@ -74,16 +74,16 @@ def get_asset_icon_by_name(icon_name, icon_color, has_children=False): ) -def get_asset_icon_name(asset_doc, has_children=True): - icon_name = get_asset_icon_name_from_doc(asset_doc) +def _get_asset_icon_name(asset_doc, has_children=True): + icon_name = _get_asset_icon_name_from_doc(asset_doc) if icon_name: return icon_name - return get_default_asset_icon_name(has_children) + return _get_default_asset_icon_name(has_children) def get_asset_icon(asset_doc, has_children=False): - icon_name = get_asset_icon_name(asset_doc, has_children) - icon_color = get_asset_icon_color(asset_doc) + 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 9156bd08bd5744a06f828425c44ee350903eaba3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 13 Feb 2024 18:42:20 +0100 Subject: [PATCH 087/573] removed unused 'get_asset_icon_by_name' --- client/ayon_core/tools/utils/assets_widget.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/client/ayon_core/tools/utils/assets_widget.py b/client/ayon_core/tools/utils/assets_widget.py index 4af932a054..694675d877 100644 --- a/client/ayon_core/tools/utils/assets_widget.py +++ b/client/ayon_core/tools/utils/assets_widget.py @@ -57,23 +57,6 @@ def _get_asset_icon_color(asset_doc): return get_default_entity_icon_color() -def get_asset_icon_by_name(icon_name, icon_color, has_children=False): - if not icon_name: - icon_name = get_default_asset_icon_name(has_children) - - if icon_color: - icon_color = QtGui.QColor(icon_color) - else: - icon_color = get_default_entity_icon_color() - icon = get_qta_icon_by_name_and_color(icon_name, icon_color) - if icon is not None: - return icon - return get_qta_icon_by_name_and_color( - get_default_asset_icon_name(has_children), - icon_color - ) - - def _get_asset_icon_name(asset_doc, has_children=True): icon_name = _get_asset_icon_name_from_doc(asset_doc) if icon_name: From e3b0bb98afd21c6ad69fdbd202128461b6949822 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 13 Feb 2024 18:42:29 +0100 Subject: [PATCH 088/573] added docstring to 'get_asset_icon' --- client/ayon_core/tools/utils/assets_widget.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/client/ayon_core/tools/utils/assets_widget.py b/client/ayon_core/tools/utils/assets_widget.py index 694675d877..7c3fd8d97c 100644 --- a/client/ayon_core/tools/utils/assets_widget.py +++ b/client/ayon_core/tools/utils/assets_widget.py @@ -65,6 +65,20 @@ def _get_asset_icon_name(asset_doc, has_children=True): def get_asset_icon(asset_doc, has_children=False): + """Get asset icon. + + Deprecated: + This function will be removed in future releases. Use on your own + risk. + + Args: + asset_doc (dict): Asset document. + has_children (Optional[bool]): Asset has children assets. + + Returns: + QIcon: Asset icon. + + """ icon_name = _get_asset_icon_name(asset_doc, has_children) icon_color = _get_asset_icon_color(asset_doc) From b9e01b254ddca1d402c8a0a4570be2834b43aeda Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 13 Feb 2024 18:58:25 +0100 Subject: [PATCH 089/573] addons can define their openpype alias name --- client/ayon_core/addon/base.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/client/ayon_core/addon/base.py b/client/ayon_core/addon/base.py index a3920c4acb..3b35476aed 100644 --- a/client/ayon_core/addon/base.py +++ b/client/ayon_core/addon/base.py @@ -788,6 +788,7 @@ class AddonsManager: addon_classes.append(modules_item) + aliased_names = [] for addon_cls in addon_classes: name = addon_cls.__name__ if issubclass(addon_cls, OpenPypeModule): @@ -807,6 +808,13 @@ class AddonsManager: self._addons.append(addon) self._addons_by_id[addon.id] = addon self._addons_by_name[addon.name] = addon + # NOTE This will be removed with release 1.0.0 of ayon-core + # please use carefully. + # Gives option to use alias name for addon for cases when + # name in OpenPype was not the same as in AYON. + name_alias = getattr(addon, "openpype_alias", None) + if name_alias: + aliased_names.append((name_alias, addon)) enabled_str = "X" if not addon.enabled: enabled_str = " " @@ -822,6 +830,17 @@ class AddonsManager: exc_info=True ) + for item in aliased_names: + name_alias, addon = item + if name_alias not in self._addons_by_name: + self._addons_by_name[name_alias] = addon + continue + self.log.warning( + "Alias name '{}' of addon '{}' is already assigned.".format( + name_alias, addon.name + ) + ) + if self._report is not None: report[self._report_total_key] = time.time() - time_start self._report["Initialization"] = report From 66cb5336d354b501f7c467854d74e37ec9faffcc Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Tue, 13 Feb 2024 18:03:44 +0000 Subject: [PATCH 090/573] Added an option to disable composite output --- .../ayon_core/hosts/blender/api/render_lib.py | 29 ++++++++++++++----- .../server/settings/render_settings.py | 4 +++ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/hosts/blender/api/render_lib.py b/client/ayon_core/hosts/blender/api/render_lib.py index 73d327b1a8..91913f7913 100644 --- a/client/ayon_core/hosts/blender/api/render_lib.py +++ b/client/ayon_core/hosts/blender/api/render_lib.py @@ -55,6 +55,14 @@ def get_renderer(settings): ["renderer"]) +def get_compositing(settings): + """Get compositing from blender settings.""" + + return (settings["blender"] + ["RenderSettings"] + ["compositing"]) + + def get_render_product(output_path, name, aov_sep): """ Generate the path to the render product. Blender interprets the `#` @@ -184,7 +192,9 @@ def _create_aov_slot(name, aov_sep, slots, rpass_name, multi_exr, output_path): return slot, filepath -def set_node_tree(output_path, render_product, name, aov_sep, ext, multilayer): +def set_node_tree( + output_path, render_product, name, aov_sep, ext, multilayer, compositing +): # Set the scene to use the compositor node tree to render bpy.context.scene.use_nodes = True @@ -250,11 +260,12 @@ def set_node_tree(output_path, render_product, name, aov_sep, ext, multilayer): name, aov_sep, slots, pass_name, multi_exr, output_path) tree.links.new(render_layer_node.outputs["Image"], slot) - # Create a new socket for the composite output - pass_name = "composite" - comp_socket, filepath = _create_aov_slot( - name, aov_sep, slots, pass_name, multi_exr, output_path) - aov_file_products.append(("Composite", filepath)) + if compositing: + # Create a new socket for the composite output + pass_name = "composite" + comp_socket, filepath = _create_aov_slot( + name, aov_sep, slots, pass_name, multi_exr, output_path) + aov_file_products.append(("Composite", filepath)) # For each active render pass, we add a new socket to the output node # and link it @@ -278,7 +289,7 @@ def set_node_tree(output_path, render_product, name, aov_sep, ext, multilayer): tree.links.remove(link) # If there's a composite node, we connect its input with the new output - if composite_node: + if compositing and composite_node: for link in tree.links: if link.to_node == composite_node: tree.links.new(link.from_socket, comp_socket) @@ -321,6 +332,7 @@ def prepare_rendering(asset_group): ext = get_image_format(settings) multilayer = get_multilayer(settings) renderer = get_renderer(settings) + compositing = get_compositing(settings) set_render_format(ext, multilayer) bpy.context.scene.render.engine = renderer @@ -330,7 +342,8 @@ def prepare_rendering(asset_group): render_product = get_render_product(output_path, name, aov_sep) aov_file_product = set_node_tree( - output_path, render_product, name, aov_sep, ext, multilayer) + output_path, render_product, name, aov_sep, + ext, multilayer, compositing) # Clear the render filepath, so that the output is handled only by the # output node in the compositor. diff --git a/server_addon/blender/server/settings/render_settings.py b/server_addon/blender/server/settings/render_settings.py index a1f6d3114a..f992ea6fcc 100644 --- a/server_addon/blender/server/settings/render_settings.py +++ b/server_addon/blender/server/settings/render_settings.py @@ -127,6 +127,9 @@ class RenderSettingsModel(BaseSettingsModel): title="Renderer", enum_resolver=renderers_enum ) + compositing: bool = SettingsField( + title="Enable Compositing" + ) aov_list: list[str] = SettingsField( default_factory=list, enum_resolver=aov_list_enum, @@ -149,6 +152,7 @@ DEFAULT_RENDER_SETTINGS = { "image_format": "exr", "multilayer_exr": True, "renderer": "CYCLES", + "compositing": True, "aov_list": ["combined"], "custom_passes": [] } From f0eaf0c128b78fd8977c3eae60921b6290d947bd Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 14 Feb 2024 15:19:56 +0800 Subject: [PATCH 091/573] code tweaks on workfile creators to make sure there is no error out --- .../ayon_core/hosts/max/plugins/create/create_workfile.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/create/create_workfile.py b/client/ayon_core/hosts/max/plugins/create/create_workfile.py index e5fec5f437..cb47a36678 100644 --- a/client/ayon_core/hosts/max/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/max/plugins/create/create_workfile.py @@ -30,8 +30,8 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): if current_instance is None: current_instance_asset = None - - current_instance_asset = current_instance["folderPath"] + else: + current_instance_asset = current_instance["folderPath"] if current_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) @@ -39,12 +39,11 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): variant, task_name, asset_doc, project_name, host_name ) data = { + "folderPath": asset_name, "task": task_name, "variant": variant } - data["folderPath"] = asset_name - data.update( self.get_dynamic_data( variant, task_name, asset_doc, From d80ba2bac486711a41d1f889d4e0be3386c463cf Mon Sep 17 00:00:00 2001 From: Sponge96 Date: Wed, 14 Feb 2024 08:50:08 +0000 Subject: [PATCH 092/573] fix: removed artifact syntax Co-authored-by: Kayla Man <64118225+moonyuet@users.noreply.github.com> --- client/ayon_core/hosts/max/plugins/publish/save_scene.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/save_scene.py b/client/ayon_core/hosts/max/plugins/publish/save_scene.py index 37ff38bc59..7961631b2a 100644 --- a/client/ayon_core/hosts/max/plugins/publish/save_scene.py +++ b/client/ayon_core/hosts/max/plugins/publish/save_scene.py @@ -20,4 +20,4 @@ class SaveCurrentScene(pyblish.api.ContextPlugin): self.log.info(f"Saving current file: {current_file}") host.save_workfile(current_file) else: - self.log.debug("No unsaved changes, skipping file save..")") \ No newline at end of file + self.log.debug("No unsaved changes, skipping file save..") \ No newline at end of file From e43e2344195d57e53a064d2422e547f47a730f67 Mon Sep 17 00:00:00 2001 From: Sponge96 Date: Wed, 14 Feb 2024 08:50:45 +0000 Subject: [PATCH 093/573] refactor: replaced OP imports with ayon-core Co-authored-by: Kayla Man <64118225+moonyuet@users.noreply.github.com> --- client/ayon_core/hosts/max/plugins/publish/save_scene.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/save_scene.py b/client/ayon_core/hosts/max/plugins/publish/save_scene.py index 7961631b2a..1c59335ceb 100644 --- a/client/ayon_core/hosts/max/plugins/publish/save_scene.py +++ b/client/ayon_core/hosts/max/plugins/publish/save_scene.py @@ -1,5 +1,5 @@ import pyblish.api -from openpype.pipeline import registered_host +from ayon_core.pipeline import registered_host class SaveCurrentScene(pyblish.api.ContextPlugin): From c9ddd6c9a270545d556fab4d391819c493c03666 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 14 Feb 2024 17:06:41 +0800 Subject: [PATCH 094/573] clean up the code for current_instance['folderPath'] --- .../ayon_core/hosts/maya/plugins/create/create_workfile.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/create/create_workfile.py b/client/ayon_core/hosts/maya/plugins/create/create_workfile.py index 396ad6ffbb..dee574018f 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_workfile.py @@ -29,11 +29,6 @@ class CreateWorkfile(plugin.MayaCreatorBase, AutoCreator): task_name = self.create_context.get_current_task_name() host_name = self.create_context.host_name - if current_instance is None: - current_instance_asset = None - else: - current_instance_asset = current_instance["folderPath"] - if current_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) subset_name = self.get_subset_name( @@ -55,7 +50,7 @@ class CreateWorkfile(plugin.MayaCreatorBase, AutoCreator): ) self._add_instance_to_context(current_instance) elif ( - current_instance_asset != asset_name + current_instance["folderPath"] != asset_name or current_instance["task"] != task_name ): # Update instance context if is not the same From 9615a02aace1ad2aa6433c4172d6b42bb1f75746 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 14 Feb 2024 17:10:27 +0800 Subject: [PATCH 095/573] clean up the code of current_instance['folderPath'] --- .../ayon_core/hosts/max/plugins/create/create_workfile.py | 7 +------ .../ayon_core/hosts/maya/plugins/create/create_workfile.py | 7 ++++++- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/create/create_workfile.py b/client/ayon_core/hosts/max/plugins/create/create_workfile.py index cb47a36678..ce4d8b976d 100644 --- a/client/ayon_core/hosts/max/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/max/plugins/create/create_workfile.py @@ -28,11 +28,6 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): task_name = self.create_context.get_current_task_name() host_name = self.create_context.host_name - if current_instance is None: - current_instance_asset = None - else: - current_instance_asset = current_instance["folderPath"] - if current_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) subset_name = self.get_subset_name( @@ -58,7 +53,7 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): self._add_instance_to_context(current_instance) imprint(instance_node.name, current_instance.data) elif ( - current_instance_asset != asset_name + current_instance["folderPath"] != asset_name or current_instance["task"] != task_name ): # Update instance context if is not the same diff --git a/client/ayon_core/hosts/maya/plugins/create/create_workfile.py b/client/ayon_core/hosts/maya/plugins/create/create_workfile.py index dee574018f..396ad6ffbb 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_workfile.py @@ -29,6 +29,11 @@ class CreateWorkfile(plugin.MayaCreatorBase, AutoCreator): task_name = self.create_context.get_current_task_name() host_name = self.create_context.host_name + if current_instance is None: + current_instance_asset = None + else: + current_instance_asset = current_instance["folderPath"] + if current_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) subset_name = self.get_subset_name( @@ -50,7 +55,7 @@ class CreateWorkfile(plugin.MayaCreatorBase, AutoCreator): ) self._add_instance_to_context(current_instance) elif ( - current_instance["folderPath"] != asset_name + current_instance_asset != asset_name or current_instance["task"] != task_name ): # Update instance context if is not the same From a36bd63da7645d2afe90d27c170de4d66fca574a Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 14 Feb 2024 17:15:05 +0800 Subject: [PATCH 096/573] some style tweaks on pipeline --- client/ayon_core/hosts/max/api/pipeline.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/max/api/pipeline.py b/client/ayon_core/hosts/max/api/pipeline.py index c9b74cea9e..c26e697429 100644 --- a/client/ayon_core/hosts/max/api/pipeline.py +++ b/client/ayon_core/hosts/max/api/pipeline.py @@ -59,9 +59,9 @@ class MaxHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): rt.callbacks.addScript(rt.Name('filePostOpen'), lib.check_colorspace) - + rt.callbacks.addScript(rt.Name('postWorkspaceChange'), - self._deferred_menu_creation) + self._deferred_menu_creation) def workfile_has_unsaved_changes(self): return rt.getSaveRequired() From ecd69f0a511890e6356769e092ca4143c4c862f6 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 14 Feb 2024 17:58:10 +0800 Subject: [PATCH 097/573] Separating collect current files from the collect workfile --- .../plugins/publish/collect_current_file.py | 22 ++++++++++++ .../max/plugins/publish/collect_workfile.py | 34 ++++++++----------- 2 files changed, 37 insertions(+), 19 deletions(-) create mode 100644 client/ayon_core/hosts/max/plugins/publish/collect_current_file.py diff --git a/client/ayon_core/hosts/max/plugins/publish/collect_current_file.py b/client/ayon_core/hosts/max/plugins/publish/collect_current_file.py new file mode 100644 index 0000000000..b10f3d51bc --- /dev/null +++ b/client/ayon_core/hosts/max/plugins/publish/collect_current_file.py @@ -0,0 +1,22 @@ +import os +import pyblish.api + +from pymxs import runtime as rt + + +class CollectCurrentFile(pyblish.api.ContextPlugin): + """Inject the current working file.""" + + order = pyblish.api.CollectorOrder - 0.4 + label = "Max Current File" + hosts = ['max'] + + def process(self, context): + """Inject the current working file""" + folder = rt.maxFilePath + file = rt.maxFileName + if not folder or not file: + self.log.error("Scene is not saved.") + current_file = os.path.join(folder, file) + + context.data["currentFile"] = current_file diff --git a/client/ayon_core/hosts/max/plugins/publish/collect_workfile.py b/client/ayon_core/hosts/max/plugins/publish/collect_workfile.py index 4dbaf28bf7..775e22ff0e 100644 --- a/client/ayon_core/hosts/max/plugins/publish/collect_workfile.py +++ b/client/ayon_core/hosts/max/plugins/publish/collect_workfile.py @@ -12,6 +12,7 @@ class CollectWorkfile(pyblish.api.InstancePlugin): order = pyblish.api.CollectorOrder - 0.01 label = "Collect 3dsmax Workfile" hosts = ['max'] + families = ["workfile"] def process(self, instance): """Inject the current working file.""" @@ -20,33 +21,28 @@ class CollectWorkfile(pyblish.api.InstancePlugin): file = rt.maxFileName if not folder or not file: self.log.error("Scene is not saved.") - current_file = os.path.join(folder, file) - - context.data['currentFile'] = current_file - ext = os.path.splitext(file)[-1].lstrip(".") data = {} - # create instance - data.update({ - "setMembers": [current_file], - "frameStart": context.data['frameStart'], - "frameEnd": context.data['frameEnd'], - "handleStart": context.data['handleStart'], - "handleEnd": context.data['handleEnd'] + "setMembers": context.data["currentFile"], + "frameStart": context.data["frameStart"], + "frameEnd": context.data["frameEnd"], + "handleStart": context.data["handleStart"], + "handleEnd": context.data["handleEnd"] }) - data['representations'] = [{ - 'name': ext, - 'ext': ext, - 'files': file, + data["representations"] = [{ + "name": ext, + "ext": ext, + "files": file, "stagingDir": folder, }] instance.data.update(data) - self.log.debug('Collected data: {}'.format(data)) - self.log.debug('Collected instance: {}'.format(file)) - self.log.debug('Scene path: {}'.format(current_file)) - self.log.debug('staging Dir: {}'.format(folder)) + self.log.debug("Collected data: {}".format(data)) + self.log.debug("Collected instance: {}".format(file)) + self.log.debug("Scene path: {}".format( + context.data["currentFile"])) + self.log.debug("staging Dir: {}".format(folder)) From e86555b2cc6eeb799118f9ea7f16fad5ad676c12 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 14 Feb 2024 11:43:23 +0100 Subject: [PATCH 098/573] use correct dirmap key in nuke --- client/ayon_core/hosts/nuke/api/pipeline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/nuke/api/pipeline.py b/client/ayon_core/hosts/nuke/api/pipeline.py index 7483d404ec..735426240e 100644 --- a/client/ayon_core/hosts/nuke/api/pipeline.py +++ b/client/ayon_core/hosts/nuke/api/pipeline.py @@ -179,7 +179,7 @@ def add_nuke_callbacks(): nuke.addOnScriptLoad(WorkfileSettings().set_context_settings) - if nuke_settings["nuke_dirmap"]["enabled"]: + if nuke_settings["dirmap"]["enabled"]: log.info("Added Nuke's dir-mapping callback ...") # Add dirmap for file paths. nuke.addFilenameFilter(dirmap_file_name_filter) From 4d947d7ac77300de276d2819876d423284607221 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 14 Feb 2024 12:12:29 +0100 Subject: [PATCH 099/573] removed clockify conversion --- client/ayon_core/settings/ayon_settings.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index 8795fd0650..2577a3af06 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -76,19 +76,6 @@ def _convert_kitsu_system_settings( output["modules"]["kitsu"] = kitsu_settings -def _convert_clockify_system_settings( - ayon_settings, output, addon_versions, default_settings -): - enabled = addon_versions.get("clockify") is not None - clockify_settings = default_settings["modules"]["clockify"] - clockify_settings["enabled"] = enabled - if enabled: - clockify_settings["workspace_name"] = ( - ayon_settings["clockify"]["workspace_name"] - ) - output["modules"]["clockify"] = clockify_settings - - def _convert_deadline_system_settings( ayon_settings, output, addon_versions, default_settings ): @@ -127,7 +114,6 @@ def _convert_modules_system( # TODO add 'enabled' values for func in ( _convert_kitsu_system_settings, - _convert_clockify_system_settings, _convert_deadline_system_settings, _convert_royalrender_system_settings, ): @@ -135,6 +121,7 @@ def _convert_modules_system( for key in { "timers_manager", + "clockify", }: if addon_versions.get(key): output[key] = ayon_settings From fa55d1556215fe00b5ed73366db148a1a2b18983 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 14 Feb 2024 12:13:03 +0100 Subject: [PATCH 100/573] modified clockify addon to use AYON settings --- .../modules/clockify/clockify_module.py | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/modules/clockify/clockify_module.py b/client/ayon_core/modules/clockify/clockify_module.py index adb7eb66af..58407bfe94 100644 --- a/client/ayon_core/modules/clockify/clockify_module.py +++ b/client/ayon_core/modules/clockify/clockify_module.py @@ -2,22 +2,27 @@ import os import threading import time -from ayon_core.modules import OpenPypeModule, ITrayModule, IPluginPaths +from ayon_core.modules import AYONAddon, ITrayModule, IPluginPaths from ayon_core.client import get_asset_by_name from .constants import CLOCKIFY_FTRACK_USER_PATH, CLOCKIFY_FTRACK_SERVER_PATH -class ClockifyModule(OpenPypeModule, ITrayModule, IPluginPaths): +class ClockifyModule(AYONAddon, ITrayModule, IPluginPaths): name = "clockify" - def initialize(self, modules_settings): - clockify_settings = modules_settings[self.name] - self.enabled = clockify_settings["enabled"] - self.workspace_name = clockify_settings["workspace_name"] + def initialize(self, studio_settings): + enabled = self.name in studio_settings + workspace_name = None + if enabled: + clockify_settings = studio_settings[self.name] + workspace_name = clockify_settings["workspace_name"] - if self.enabled and not self.workspace_name: - raise Exception("Clockify Workspace is not set in settings.") + if enabled and workspace_name: + self.log.warning("Clockify Workspace is not set in settings.") + enabled = False + self.enabled = enabled + self.workspace_name = workspace_name self.timer_manager = None self.MessageWidgetClass = None From 4be12c71efe3dcb65e4e8887eeaf1a775f73014d Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 14 Feb 2024 19:47:05 +0800 Subject: [PATCH 101/573] move debug msg of scene path into colelct current file --- .../ayon_core/hosts/max/plugins/publish/collect_current_file.py | 1 + client/ayon_core/hosts/max/plugins/publish/collect_workfile.py | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/collect_current_file.py b/client/ayon_core/hosts/max/plugins/publish/collect_current_file.py index b10f3d51bc..689a357c53 100644 --- a/client/ayon_core/hosts/max/plugins/publish/collect_current_file.py +++ b/client/ayon_core/hosts/max/plugins/publish/collect_current_file.py @@ -20,3 +20,4 @@ class CollectCurrentFile(pyblish.api.ContextPlugin): current_file = os.path.join(folder, file) context.data["currentFile"] = current_file + self.log.debug("Scene path: {}".format(current_file)) diff --git a/client/ayon_core/hosts/max/plugins/publish/collect_workfile.py b/client/ayon_core/hosts/max/plugins/publish/collect_workfile.py index 775e22ff0e..6eec0f7292 100644 --- a/client/ayon_core/hosts/max/plugins/publish/collect_workfile.py +++ b/client/ayon_core/hosts/max/plugins/publish/collect_workfile.py @@ -43,6 +43,4 @@ class CollectWorkfile(pyblish.api.InstancePlugin): instance.data.update(data) self.log.debug("Collected data: {}".format(data)) self.log.debug("Collected instance: {}".format(file)) - self.log.debug("Scene path: {}".format( - context.data["currentFile"])) self.log.debug("staging Dir: {}".format(folder)) From 32dde5f02e94fcd8091877eea7ddb003d3b807da Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 14 Feb 2024 13:30:06 +0100 Subject: [PATCH 102/573] 'get_ayon_settings' is in settings functions --- client/ayon_core/settings/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ayon_core/settings/__init__.py b/client/ayon_core/settings/__init__.py index ed3aaef7d4..f0e5c95efe 100644 --- a/client/ayon_core/settings/__init__.py +++ b/client/ayon_core/settings/__init__.py @@ -9,6 +9,7 @@ from .lib import ( get_current_project_settings, get_local_settings, ) +from .ayon_settings import get_ayon_settings __all__ = ( @@ -20,4 +21,6 @@ __all__ = ( "get_project_settings", "get_current_project_settings", "get_local_settings", + + "get_ayon_settings", ) From 731a4d7b29f97742ffa2c2647e753d9aa553b908 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 14 Feb 2024 13:43:04 +0100 Subject: [PATCH 103/573] simplify project settings conversion --- client/ayon_core/settings/ayon_settings.py | 110 --------------------- 1 file changed, 110 deletions(-) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index 2577a3af06..6fdeec574f 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -188,59 +188,6 @@ def convert_system_settings(ayon_settings, default_settings, addon_versions): # --------- Project settings --------- -def _convert_blender_project_settings(ayon_settings, output): - if "blender" not in ayon_settings: - return - ayon_blender = ayon_settings["blender"] - - output["blender"] = ayon_blender - - -def _convert_celaction_project_settings(ayon_settings, output): - if "celaction" not in ayon_settings: - return - - ayon_celaction = ayon_settings["celaction"] - - output["celaction"] = ayon_celaction - - -def _convert_flame_project_settings(ayon_settings, output): - if "flame" not in ayon_settings: - return - - ayon_flame = ayon_settings["flame"] - - output["flame"] = ayon_flame - - -def _convert_fusion_project_settings(ayon_settings, output): - if "fusion" not in ayon_settings: - return - - ayon_fusion = ayon_settings["fusion"] - - output["fusion"] = ayon_fusion - - -def _convert_maya_project_settings(ayon_settings, output): - if "maya" not in ayon_settings: - return - - ayon_maya = ayon_settings["maya"] - - output["maya"] = ayon_maya - - -def _convert_3dsmax_project_settings(ayon_settings, output): - if "max" not in ayon_settings: - return - - ayon_max = ayon_settings["max"] - - output["max"] = ayon_max - - def _convert_nuke_knobs(knobs): new_knobs = [] for knob in knobs: @@ -456,39 +403,6 @@ def _convert_hiero_project_settings(ayon_settings, output): output["hiero"] = ayon_hiero -def _convert_photoshop_project_settings(ayon_settings, output): - if "photoshop" not in ayon_settings: - return - - ayon_photoshop = ayon_settings["photoshop"] - output["photoshop"] = ayon_photoshop - - -def _convert_substancepainter_project_settings(ayon_settings, output): - if "substancepainter" not in ayon_settings: - return - - ayon_substance_painter = ayon_settings["substancepainter"] - output["substancepainter"] = ayon_substance_painter - - -def _convert_tvpaint_project_settings(ayon_settings, output): - if "tvpaint" not in ayon_settings: - return - - ayon_tvpaint = ayon_settings["tvpaint"] - output["tvpaint"] = ayon_tvpaint - - -def _convert_traypublisher_project_settings(ayon_settings, output): - if "traypublisher" not in ayon_settings: - return - - ayon_traypublisher = ayon_settings["traypublisher"] - - output["traypublisher"] = ayon_traypublisher - - def _convert_webpublisher_project_settings(ayon_settings, output): if "webpublisher" not in ayon_settings: return @@ -747,35 +661,11 @@ def _convert_global_project_settings(ayon_settings, output, default_settings): def convert_project_settings(ayon_settings, default_settings): - # Missing settings - # - standalonepublisher default_settings = copy.deepcopy(default_settings) output = {} - exact_match = { - "aftereffects", - "harmony", - "houdini", - "resolve", - "unreal", - "applications", - "deadline", - } - for key in exact_match: - if key in ayon_settings: - output[key] = ayon_settings[key] - _convert_blender_project_settings(ayon_settings, output) - _convert_celaction_project_settings(ayon_settings, output) - _convert_flame_project_settings(ayon_settings, output) - _convert_fusion_project_settings(ayon_settings, output) - _convert_maya_project_settings(ayon_settings, output) - _convert_3dsmax_project_settings(ayon_settings, output) _convert_nuke_project_settings(ayon_settings, output) _convert_hiero_project_settings(ayon_settings, output) - _convert_photoshop_project_settings(ayon_settings, output) - _convert_substancepainter_project_settings(ayon_settings, output) - _convert_tvpaint_project_settings(ayon_settings, output) - _convert_traypublisher_project_settings(ayon_settings, output) _convert_webpublisher_project_settings(ayon_settings, output) _convert_royalrender_project_settings(ayon_settings, output) From 6b1b4367bbb5f62b9e9d03e22d82f21675d104bf Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 14 Feb 2024 13:43:19 +0100 Subject: [PATCH 104/573] simplify system settings conversion --- client/ayon_core/settings/ayon_settings.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index 6fdeec574f..7e9fad314c 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -164,9 +164,6 @@ def convert_system_settings(ayon_settings, default_settings, addon_versions): output = { "modules": {} } - if "applications" in ayon_settings: - output["applications"] = ayon_settings["applications"] - if "core" in ayon_settings: _convert_general(ayon_settings, output, default_settings) From 8324b971a8a023dce4f25f9ce2d5196d35ad1f8c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 14 Feb 2024 14:01:47 +0100 Subject: [PATCH 105/573] handle additional applications correctly --- client/ayon_core/lib/applications.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/lib/applications.py b/client/ayon_core/lib/applications.py index 70aa9811c7..2b0b855c81 100644 --- a/client/ayon_core/lib/applications.py +++ b/client/ayon_core/lib/applications.py @@ -419,7 +419,14 @@ class ApplicationManager: # Prepare known applications app_defs = applications_addon_settings["applications"] additional_apps = app_defs.pop("additional_apps") - app_defs.update(additional_apps) + for additional_app in additional_apps: + app_name = additional_app.pop("name") + if app_name in app_defs: + self.log.warning(( + "Additional application '{}' is already" + " in built-in applications." + ).format(app_name)) + app_defs[app_name] = additional_app for group_name, variant_defs in app_defs.items(): group = ApplicationGroup(group_name, variant_defs, self) From 4113ff7bf0b4dc396aa829352161aca6fdef9eec Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Wed, 14 Feb 2024 14:52:18 +0000 Subject: [PATCH 106/573] Add `rez` packages generation for `server_addons` Allow to generate `rez` compatible packages for the `server_addons`, will create a `package.py` which then is also included in the `.zip`. --- server_addon/create_ayon_addons.py | 68 +++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/server_addon/create_ayon_addons.py b/server_addon/create_ayon_addons.py index 08443d6588..f9f31dd9b0 100644 --- a/server_addon/create_ayon_addons.py +++ b/server_addon/create_ayon_addons.py @@ -144,11 +144,18 @@ def create_addon_zip( output_dir: Path, addon_name: str, addon_version: str, - keep_source: bool + keep_source: bool, + rez_package: bool, ): zip_filepath = output_dir / f"{addon_name}-{addon_version}.zip" + + if rez_package: + zip_filepath = output_dir / f"{addon_name}-{addon_version}-rez.zip" + addon_output_dir = output_dir / addon_name / addon_version with ZipFileLongPaths(zip_filepath, "w", zipfile.ZIP_DEFLATED) as zipf: + # TODO: Decide if we want to keep this for first pass checking without + # executing any python. Otherwiseh we can be removed zipf.writestr( "manifest.json", json.dumps({ @@ -168,8 +175,14 @@ def create_addon_zip( src_path = os.path.join(root, filename) if rel_root: dst_path = os.path.join("addon", rel_root, filename) + if rez_package: + dst_path = os.path.join(rel_root, filename) else: dst_path = os.path.join("addon", filename) + + if rez_package: + dst_path = filename + zipf.write(src_path, dst_path) if not keep_source: @@ -180,7 +193,8 @@ def create_addon_package( addon_dir: Path, output_dir: Path, create_zip: bool, - keep_source: bool + keep_source: bool, + rez_package: bool, ): server_dir = addon_dir / "server" addon_version = get_addon_version(addon_dir) @@ -191,22 +205,37 @@ def create_addon_package( addon_output_dir.mkdir(parents=True) # Copy server content - src_root = os.path.normpath(str(server_dir.absolute())) - src_root_offset = len(src_root) + 1 - for root, _, filenames in os.walk(str(server_dir)): - dst_root = addon_output_dir - if root != src_root: - rel_root = root[src_root_offset:] - dst_root = dst_root / rel_root + if not rez_package: + src_root = os.path.normpath(str(server_dir.absolute())) + src_root_offset = len(src_root) + 1 + for root, _, filenames in os.walk(str(server_dir)): + dst_root = addon_output_dir + if root != src_root: + rel_root = root[src_root_offset:] + dst_root = dst_root / rel_root - dst_root.mkdir(parents=True, exist_ok=True) - for filename in filenames: - src_path = os.path.join(root, filename) - shutil.copy(src_path, str(dst_root)) + dst_root.mkdir(parents=True, exist_ok=True) + for filename in filenames: + src_path = os.path.join(root, filename) + shutil.copy(src_path, str(dst_root)) + else: + package_py = addon_output_dir / "package.py" + + with open(package_py, "w+") as pkg_py: + pkg_py.write( + f"""name = "{addon_dir.name}" +version = "{addon_version}" +plugin_for = ["ayon_server"] +build_command = "python {{root}}/rezbuild.py" +""" + ) + shutil.copytree( + server_dir, addon_output_dir / "server", dirs_exist_ok=True + ) if create_zip: create_addon_zip( - output_dir, addon_dir.name, addon_version, keep_source + output_dir, addon_dir.name, addon_version, keep_source, rez_package ) @@ -216,6 +245,7 @@ def main( keep_source=False, clear_output_dir=False, addons=None, + rez_package=False, ): current_dir = Path(os.path.dirname(os.path.abspath(__file__))) root_dir = current_dir.parent @@ -250,7 +280,7 @@ def main( continue create_addon_package( - addon_dir, output_dir, create_zip, keep_source + addon_dir, output_dir, create_zip, keep_source, rez_package ) print(f"- package '{addon_dir.name}' created") @@ -300,6 +330,13 @@ if __name__ == "__main__": action="append", help="Limit addon creation to given addon name", ) + parser.add_argument( + "-r", + "--rez", + dest="rez_package", + action="store_true", + help="Make it a Rez-compatible package", + ) args = parser.parse_args(sys.argv[1:]) main( @@ -308,4 +345,5 @@ if __name__ == "__main__": args.keep_sources, args.clear_output_dir, args.addons, + args.rez_package, ) From 0744c27c91d8eac2d9742827f6dd1a687f1189bf Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Wed, 14 Feb 2024 14:56:52 +0000 Subject: [PATCH 107/573] `package.py` Ensure we require the correct `ayon_server` version For more info on `rez` package requests syntax check: https://rez.readthedocs.io/en/latest/basic_concepts.html#package-requests-concept --- package.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.py b/package.py index d7266d852b..3b451e0078 100644 --- a/package.py +++ b/package.py @@ -5,4 +5,7 @@ version = "0.2.1-dev.1" client_dir = "ayon_core" plugin_for = ["ayon_server"] -ayon_version = ">=1.0.3,<2.0.0" +requires = [ + "~ayon_server-1.0.3+<2.0.0", +] + From 4c0651f593213838e2a2b21a34ccebba81bea732 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 14 Feb 2024 18:18:02 +0100 Subject: [PATCH 108/573] create only rez packages --- server_addon/create_ayon_addons.py | 77 ++++++++---------------------- 1 file changed, 19 insertions(+), 58 deletions(-) diff --git a/server_addon/create_ayon_addons.py b/server_addon/create_ayon_addons.py index f9f31dd9b0..5e06d8ee8a 100644 --- a/server_addon/create_ayon_addons.py +++ b/server_addon/create_ayon_addons.py @@ -40,6 +40,11 @@ IGNORED_HOSTS = [ IGNORED_MODULES = [] +PACKAGE_PY_TEMPLATE = """name = "{addon_name}" +version = "{addon_version}" +plugin_for = ["ayon_server"] +""" + class ZipFileLongPaths(zipfile.ZipFile): """Allows longer paths in zip files. @@ -145,24 +150,11 @@ def create_addon_zip( addon_name: str, addon_version: str, keep_source: bool, - rez_package: bool, ): zip_filepath = output_dir / f"{addon_name}-{addon_version}.zip" - if rez_package: - zip_filepath = output_dir / f"{addon_name}-{addon_version}-rez.zip" - addon_output_dir = output_dir / addon_name / addon_version with ZipFileLongPaths(zip_filepath, "w", zipfile.ZIP_DEFLATED) as zipf: - # TODO: Decide if we want to keep this for first pass checking without - # executing any python. Otherwiseh we can be removed - zipf.writestr( - "manifest.json", - json.dumps({ - "addon_name": addon_name, - "addon_version": addon_version - }) - ) # Add client code content to zip src_root = os.path.normpath(str(addon_output_dir.absolute())) src_root_offset = len(src_root) + 1 @@ -174,14 +166,9 @@ def create_addon_zip( for filename in filenames: src_path = os.path.join(root, filename) if rel_root: - dst_path = os.path.join("addon", rel_root, filename) - if rez_package: - dst_path = os.path.join(rel_root, filename) + dst_path = os.path.join(rel_root, filename) else: - dst_path = os.path.join("addon", filename) - - if rez_package: - dst_path = filename + dst_path = filename zipf.write(src_path, dst_path) @@ -194,9 +181,7 @@ def create_addon_package( output_dir: Path, create_zip: bool, keep_source: bool, - rez_package: bool, ): - server_dir = addon_dir / "server" addon_version = get_addon_version(addon_dir) addon_output_dir = output_dir / addon_dir.name / addon_version @@ -205,37 +190,22 @@ def create_addon_package( addon_output_dir.mkdir(parents=True) # Copy server content - if not rez_package: - src_root = os.path.normpath(str(server_dir.absolute())) - src_root_offset = len(src_root) + 1 - for root, _, filenames in os.walk(str(server_dir)): - dst_root = addon_output_dir - if root != src_root: - rel_root = root[src_root_offset:] - dst_root = dst_root / rel_root + package_py = addon_output_dir / "package.py" + package_py_content = PACKAGE_PY_TEMPLATE.format( + addon_name=addon_dir.name, addon_version=addon_version + ) - dst_root.mkdir(parents=True, exist_ok=True) - for filename in filenames: - src_path = os.path.join(root, filename) - shutil.copy(src_path, str(dst_root)) - else: - package_py = addon_output_dir / "package.py" + with open(package_py, "w+") as pkg_py: + pkg_py.write(package_py_content) - with open(package_py, "w+") as pkg_py: - pkg_py.write( - f"""name = "{addon_dir.name}" -version = "{addon_version}" -plugin_for = ["ayon_server"] -build_command = "python {{root}}/rezbuild.py" -""" - ) - shutil.copytree( - server_dir, addon_output_dir / "server", dirs_exist_ok=True - ) + server_dir = addon_dir / "server" + shutil.copytree( + server_dir, addon_output_dir / "server", dirs_exist_ok=True + ) if create_zip: create_addon_zip( - output_dir, addon_dir.name, addon_version, keep_source, rez_package + output_dir, addon_dir.name, addon_version, keep_source ) @@ -245,7 +215,6 @@ def main( keep_source=False, clear_output_dir=False, addons=None, - rez_package=False, ): current_dir = Path(os.path.dirname(os.path.abspath(__file__))) root_dir = current_dir.parent @@ -280,7 +249,7 @@ def main( continue create_addon_package( - addon_dir, output_dir, create_zip, keep_source, rez_package + addon_dir, output_dir, create_zip, keep_source ) print(f"- package '{addon_dir.name}' created") @@ -330,13 +299,6 @@ if __name__ == "__main__": action="append", help="Limit addon creation to given addon name", ) - parser.add_argument( - "-r", - "--rez", - dest="rez_package", - action="store_true", - help="Make it a Rez-compatible package", - ) args = parser.parse_args(sys.argv[1:]) main( @@ -345,5 +307,4 @@ if __name__ == "__main__": args.keep_sources, args.clear_output_dir, args.addons, - args.rez_package, ) From 40a9788536fd06a40d54f351e9a55f8c5d96a580 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 14 Feb 2024 18:32:09 +0100 Subject: [PATCH 109/573] fix applications settings --- server_addon/applications/server/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server_addon/applications/server/__init__.py b/server_addon/applications/server/__init__.py index e782e8a591..d5c2de3df3 100644 --- a/server_addon/applications/server/__init__.py +++ b/server_addon/applications/server/__init__.py @@ -92,8 +92,9 @@ class ApplicationsAddon(BaseServerAddon): settings_model = ApplicationsAddonSettings async def get_default_settings(self): - applications_path = os.path.join(self.addon_dir, "applications.json") - tools_path = os.path.join(self.addon_dir, "tools.json") + server_dir = os.path.join(self.addon_dir, "server") + applications_path = os.path.join(server_dir, "applications.json") + tools_path = os.path.join(server_dir, "tools.json") default_values = copy.deepcopy(DEFAULT_VALUES) with open(applications_path, "r") as stream: default_values.update(json.load(stream)) From 1d72cd102356bb5d0b4ae0f8555dec46433311cd Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 15 Feb 2024 16:47:05 +0800 Subject: [PATCH 110/573] bug fix remove items not working in scene inventory manager --- client/ayon_core/hosts/max/api/pipeline.py | 17 +++++++++++++++++ .../hosts/max/plugins/load/load_camera_fbx.py | 8 ++++---- .../hosts/max/plugins/load/load_max_scene.py | 9 +++++---- .../hosts/max/plugins/load/load_model.py | 8 +++++--- .../hosts/max/plugins/load/load_model_fbx.py | 9 +++++---- .../hosts/max/plugins/load/load_model_obj.py | 6 +++--- .../hosts/max/plugins/load/load_model_usd.py | 5 +++-- .../hosts/max/plugins/load/load_pointcache.py | 5 +++-- .../plugins/load/load_pointcache_ornatrix.py | 5 +++-- .../hosts/max/plugins/load/load_pointcloud.py | 6 +++--- .../max/plugins/load/load_redshift_proxy.py | 6 +++--- .../hosts/max/plugins/load/load_tycache.py | 9 ++++----- 12 files changed, 58 insertions(+), 35 deletions(-) diff --git a/client/ayon_core/hosts/max/api/pipeline.py b/client/ayon_core/hosts/max/api/pipeline.py index c26e697429..106c29fd26 100644 --- a/client/ayon_core/hosts/max/api/pipeline.py +++ b/client/ayon_core/hosts/max/api/pipeline.py @@ -242,3 +242,20 @@ def get_previous_loaded_object(container: str): if str(obj) in sel_list: node_list.append(obj) return node_list + + +def remove_container_data(container: str): + """Function to remove container data after updating, switching or deleting it. + + Args: + container (str): container + """ + if container.modifiers[0].name == "OP Data": + all_set_members_names = [ + member.node for member + in container.modifiers[0].openPypeData.all_handles] + for current_set_member in all_set_members_names: + rt.Delete(current_set_member) + rt.deleteModifier(container, container.modifiers[0]) + + rt.Delete(container) diff --git a/client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py b/client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py index 34b120c179..d7eebdbc3a 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py +++ b/client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py @@ -1,6 +1,6 @@ import os -from ayon_core.hosts.max.api import lib, maintained_selection +from ayon_core.hosts.max.api import lib from ayon_core.hosts.max.api.lib import ( unique_namespace, get_namespace, @@ -9,7 +9,8 @@ from ayon_core.hosts.max.api.lib import ( from ayon_core.hosts.max.api.pipeline import ( containerise, get_previous_loaded_object, - update_custom_attribute_data + update_custom_attribute_data, + remove_container_data ) from ayon_core.pipeline import get_representation_path, load @@ -94,6 +95,5 @@ class FbxLoader(load.LoaderPlugin): def remove(self, container): from pymxs import runtime as rt - node = rt.GetNodeByName(container["instance_node"]) - rt.Delete(node) + remove_container_data(node) diff --git a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py index 7267d7a59e..81f3af089f 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py +++ b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py @@ -7,8 +7,10 @@ from ayon_core.hosts.max.api.lib import ( object_transform_set ) from ayon_core.hosts.max.api.pipeline import ( - containerise, get_previous_loaded_object, - update_custom_attribute_data + containerise, + get_previous_loaded_object, + update_custom_attribute_data, + remove_container_data ) from ayon_core.pipeline import get_representation_path, load @@ -93,6 +95,5 @@ class MaxSceneLoader(load.LoaderPlugin): def remove(self, container): from pymxs import runtime as rt - node = rt.GetNodeByName(container["instance_node"]) - rt.Delete(node) + remove_container_data(node) diff --git a/client/ayon_core/hosts/max/plugins/load/load_model.py b/client/ayon_core/hosts/max/plugins/load/load_model.py index 796e1b80ad..28ec7be01f 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_model.py +++ b/client/ayon_core/hosts/max/plugins/load/load_model.py @@ -2,11 +2,13 @@ import os from ayon_core.pipeline import load, get_representation_path from ayon_core.hosts.max.api.pipeline import ( containerise, - get_previous_loaded_object + get_previous_loaded_object, + remove_container_data ) from ayon_core.hosts.max.api import lib from ayon_core.hosts.max.api.lib import ( - maintained_selection, unique_namespace + maintained_selection, + unique_namespace ) @@ -99,7 +101,7 @@ class ModelAbcLoader(load.LoaderPlugin): from pymxs import runtime as rt node = rt.GetNodeByName(container["instance_node"]) - rt.Delete(node) + remove_container_data(node) @staticmethod def get_container_children(parent, type_name): diff --git a/client/ayon_core/hosts/max/plugins/load/load_model_fbx.py b/client/ayon_core/hosts/max/plugins/load/load_model_fbx.py index 827cf63b39..81ad84546a 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_model_fbx.py +++ b/client/ayon_core/hosts/max/plugins/load/load_model_fbx.py @@ -1,8 +1,10 @@ import os from ayon_core.pipeline import load, get_representation_path from ayon_core.hosts.max.api.pipeline import ( - containerise, get_previous_loaded_object, - update_custom_attribute_data + containerise, + get_previous_loaded_object, + update_custom_attribute_data, + remove_container_data ) from ayon_core.hosts.max.api import lib from ayon_core.hosts.max.api.lib import ( @@ -92,6 +94,5 @@ class FbxModelLoader(load.LoaderPlugin): def remove(self, container): from pymxs import runtime as rt - node = rt.GetNodeByName(container["instance_node"]) - rt.Delete(node) + remove_container_data(node) diff --git a/client/ayon_core/hosts/max/plugins/load/load_model_obj.py b/client/ayon_core/hosts/max/plugins/load/load_model_obj.py index 22d3d4b58a..1023b67f0c 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_model_obj.py +++ b/client/ayon_core/hosts/max/plugins/load/load_model_obj.py @@ -11,7 +11,8 @@ from ayon_core.hosts.max.api.lib import maintained_selection from ayon_core.hosts.max.api.pipeline import ( containerise, get_previous_loaded_object, - update_custom_attribute_data + update_custom_attribute_data, + remove_container_data ) from ayon_core.pipeline import get_representation_path, load @@ -84,6 +85,5 @@ class ObjLoader(load.LoaderPlugin): def remove(self, container): from pymxs import runtime as rt - node = rt.GetNodeByName(container["instance_node"]) - rt.Delete(node) + remove_container_data(node) diff --git a/client/ayon_core/hosts/max/plugins/load/load_model_usd.py b/client/ayon_core/hosts/max/plugins/load/load_model_usd.py index 8d42219217..6a08bebf5a 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_model_usd.py +++ b/client/ayon_core/hosts/max/plugins/load/load_model_usd.py @@ -13,7 +13,8 @@ from ayon_core.hosts.max.api.lib import maintained_selection from ayon_core.hosts.max.api.pipeline import ( containerise, get_previous_loaded_object, - update_custom_attribute_data + update_custom_attribute_data, + remove_container_data ) from ayon_core.pipeline import get_representation_path, load @@ -114,4 +115,4 @@ class ModelUSDLoader(load.LoaderPlugin): def remove(self, container): node = rt.GetNodeByName(container["instance_node"]) - rt.Delete(node) + remove_container_data(node) diff --git a/client/ayon_core/hosts/max/plugins/load/load_pointcache.py b/client/ayon_core/hosts/max/plugins/load/load_pointcache.py index a92fa66757..d7267afb7d 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_pointcache.py +++ b/client/ayon_core/hosts/max/plugins/load/load_pointcache.py @@ -10,7 +10,8 @@ from ayon_core.hosts.max.api import lib, maintained_selection from ayon_core.hosts.max.api.lib import unique_namespace from ayon_core.hosts.max.api.pipeline import ( containerise, - get_previous_loaded_object + get_previous_loaded_object, + remove_container_data ) @@ -105,7 +106,7 @@ class AbcLoader(load.LoaderPlugin): from pymxs import runtime as rt node = rt.GetNodeByName(container["instance_node"]) - rt.Delete(node) + remove_container_data(node) @staticmethod def get_container_children(parent, type_name): diff --git a/client/ayon_core/hosts/max/plugins/load/load_pointcache_ornatrix.py b/client/ayon_core/hosts/max/plugins/load/load_pointcache_ornatrix.py index 27b2e271d2..5b48e5d189 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_pointcache_ornatrix.py +++ b/client/ayon_core/hosts/max/plugins/load/load_pointcache_ornatrix.py @@ -4,7 +4,8 @@ from ayon_core.pipeline.load import LoadError from ayon_core.hosts.max.api.pipeline import ( containerise, get_previous_loaded_object, - update_custom_attribute_data + update_custom_attribute_data, + remove_container_data ) from ayon_core.hosts.max.api.lib import ( @@ -105,4 +106,4 @@ class OxAbcLoader(load.LoaderPlugin): def remove(self, container): node = rt.GetNodeByName(container["instance_node"]) - rt.Delete(node) + remove_container_data(node) \ No newline at end of file diff --git a/client/ayon_core/hosts/max/plugins/load/load_pointcloud.py b/client/ayon_core/hosts/max/plugins/load/load_pointcloud.py index 45e3da5621..7f4fba50b3 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_pointcloud.py +++ b/client/ayon_core/hosts/max/plugins/load/load_pointcloud.py @@ -8,7 +8,8 @@ from ayon_core.hosts.max.api.lib import ( from ayon_core.hosts.max.api.pipeline import ( containerise, get_previous_loaded_object, - update_custom_attribute_data + update_custom_attribute_data, + remove_container_data ) from ayon_core.pipeline import get_representation_path, load @@ -63,6 +64,5 @@ class PointCloudLoader(load.LoaderPlugin): def remove(self, container): """remove the container""" from pymxs import runtime as rt - node = rt.GetNodeByName(container["instance_node"]) - rt.Delete(node) + remove_container_data(node) diff --git a/client/ayon_core/hosts/max/plugins/load/load_redshift_proxy.py b/client/ayon_core/hosts/max/plugins/load/load_redshift_proxy.py index 3f73210c24..3ccc5cc5e1 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_redshift_proxy.py +++ b/client/ayon_core/hosts/max/plugins/load/load_redshift_proxy.py @@ -9,7 +9,8 @@ from ayon_core.pipeline.load import LoadError from ayon_core.hosts.max.api.pipeline import ( containerise, update_custom_attribute_data, - get_previous_loaded_object + get_previous_loaded_object, + remove_container_data ) from ayon_core.hosts.max.api import lib from ayon_core.hosts.max.api.lib import ( @@ -72,6 +73,5 @@ class RedshiftProxyLoader(load.LoaderPlugin): def remove(self, container): from pymxs import runtime as rt - node = rt.getNodeByName(container["instance_node"]) - rt.delete(node) + remove_container_data(node) diff --git a/client/ayon_core/hosts/max/plugins/load/load_tycache.py b/client/ayon_core/hosts/max/plugins/load/load_tycache.py index 48fb5c447a..97f41026b4 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_tycache.py +++ b/client/ayon_core/hosts/max/plugins/load/load_tycache.py @@ -1,13 +1,13 @@ import os from ayon_core.hosts.max.api import lib, maintained_selection from ayon_core.hosts.max.api.lib import ( - unique_namespace, - + unique_namespace ) from ayon_core.hosts.max.api.pipeline import ( containerise, get_previous_loaded_object, - update_custom_attribute_data + update_custom_attribute_data, + remove_container_data ) from ayon_core.pipeline import get_representation_path, load @@ -59,6 +59,5 @@ class TyCacheLoader(load.LoaderPlugin): def remove(self, container): """remove the container""" from pymxs import runtime as rt - node = rt.GetNodeByName(container["instance_node"]) - rt.Delete(node) + remove_container_data(node) From 8e9fd531d0935aa56d20efaacd3b91088a01c4a5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 10:23:56 +0100 Subject: [PATCH 111/573] change color types back to their previous type --- .../plugins/publish/extract_sequence.py | 2 +- server/settings/publish_plugins.py | 18 +- server_addon/maya/server/settings/loaders.py | 192 ++++++++++-------- .../maya/server/settings/publish_playblast.py | 44 ++-- .../server/settings/publish_plugins.py | 13 +- 5 files changed, 157 insertions(+), 112 deletions(-) diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/extract_sequence.py b/client/ayon_core/hosts/tvpaint/plugins/publish/extract_sequence.py index 6d54d8ec32..0ab9fbd038 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/publish/extract_sequence.py +++ b/client/ayon_core/hosts/tvpaint/plugins/publish/extract_sequence.py @@ -31,7 +31,7 @@ class ExtractSequence(pyblish.api.Extractor): families = ["review", "render"] # Modifiable with settings - review_bg = [255, 255, 255, 255] + review_bg = [255, 255, 255, 1.0] def process(self, instance): self.log.info( diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index c01fb92b9c..9b5f3ae571 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -387,14 +387,22 @@ class ExtractReviewOutputDefModel(BaseSettingsModel): "Crop input overscan. See the documentation for more information." ) ) - overscan_color: ColorRGB_uint8 = SettingsField( - (0, 0, 0), + overscan_color: ColorRGBA_uint8 = SettingsField( + (0, 0, 0, 0.0), title="Overscan color", description=( "Overscan color is used when input aspect ratio is not" " same as output aspect ratio." ) ) + # overscan_color: ColorRGB_uint8 = SettingsField( + # (0, 0, 0), + # title="Overscan color", + # description=( + # "Overscan color is used when input aspect ratio is not" + # " same as output aspect ratio." + # ) + # ) width: int = SettingsField( 0, ge=0, @@ -901,7 +909,8 @@ DEFAULT_PUBLISH_VALUES = { "single_frame_filter": "single_frame" }, "overscan_crop": "", - "overscan_color": [0, 0, 0], + # "overscan_color": [0, 0, 0], + "overscan_color": [0, 0, 0, 0.0], "width": 1920, "height": 1080, "scale_pixel_aspect": True, @@ -946,7 +955,8 @@ DEFAULT_PUBLISH_VALUES = { "single_frame_filter": "multi_frame" }, "overscan_crop": "", - "overscan_color": [0, 0, 0], + # "overscan_color": [0, 0, 0], + "overscan_color": [0, 0, 0, 0.0], "width": 0, "height": 0, "scale_pixel_aspect": True, diff --git a/server_addon/maya/server/settings/loaders.py b/server_addon/maya/server/settings/loaders.py index b8264a56ef..3e8d368a6b 100644 --- a/server_addon/maya/server/settings/loaders.py +++ b/server_addon/maya/server/settings/loaders.py @@ -3,70 +3,86 @@ from ayon_server.types import ColorRGB_float class ColorsSetting(BaseSettingsModel): - model: ColorRGB_float = SettingsField( - (0.82, 0.52, 0.12), - title="Model:" - ) - rig: ColorRGB_float = SettingsField( - (0.23, 0.89, 0.92), - title="Rig:" - ) - pointcache: ColorRGB_float = SettingsField( - (0.37, 0.82, 0.12), - title="Pointcache:" - ) - animation: ColorRGB_float = SettingsField( - (0.37, 0.82, 0.12), - title="Animation:" - ) - ass: ColorRGB_float = SettingsField( - (0.98, 0.53, 0.21), - title="Arnold StandIn:" - ) - camera: ColorRGB_float = SettingsField( - (0.53, 0.45, 0.96), - title="Camera:" - ) - fbx: ColorRGB_float = SettingsField( - (0.84, 0.65, 1.0), - title="FBX:" - ) - mayaAscii: ColorRGB_float = SettingsField( - (0.26, 0.68, 1.0), - title="Maya Ascii:" - ) - mayaScene: ColorRGB_float = SettingsField( - (0.26, 0.68, 1.0), - title="Maya Scene:" - ) - setdress: ColorRGB_float = SettingsField( - (1.0, 0.98, 0.35), - title="Set Dress:" - ) - layout: ColorRGB_float = SettingsField( - (1.0, 0.98, 0.35), - title="Layout:" - ) - vdbcache: ColorRGB_float = SettingsField( - (0.98, 0.21, 0.0), - title="VDB Cache:" - ) - vrayproxy: ColorRGB_float = SettingsField( - (1.0, 0.59, 0.05), - title="VRay Proxy:" - ) - vrayscene_layer: ColorRGB_float = SettingsField( - (1.0, 0.59, 0.05), - title="VRay Scene:" - ) - yeticache: ColorRGB_float = SettingsField( - (0.39, 0.81, 0.86), - title="Yeti Cache:" - ) - yetiRig: ColorRGB_float = SettingsField( - (0.0, 0.80, 0.49), - title="Yeti Rig:" - ) + model: ColorRGBA_uint8 = SettingsField( + (209, 132, 30, 1.0), title="Model:") + rig: ColorRGBA_uint8 = SettingsField( + (59, 226, 235, 1.0), title="Rig:") + pointcache: ColorRGBA_uint8 = SettingsField( + (94, 209, 30, 1.0), title="Pointcache:") + animation: ColorRGBA_uint8 = SettingsField( + (94, 209, 30, 1.0), title="Animation:") + ass: ColorRGBA_uint8 = SettingsField( + (249, 135, 53, 1.0), title="Arnold StandIn:") + camera: ColorRGBA_uint8 = SettingsField( + (136, 114, 244, 1.0), title="Camera:") + fbx: ColorRGBA_uint8 = SettingsField( + (215, 166, 255, 1.0), title="FBX:") + mayaAscii: ColorRGBA_uint8 = SettingsField( + (67, 174, 255, 1.0), title="Maya Ascii:") + mayaScene: ColorRGBA_uint8 = SettingsField( + (67, 174, 255, 1.0), title="Maya Scene:") + setdress: ColorRGBA_uint8 = SettingsField( + (255, 250, 90, 1.0), title="Set Dress:") + layout: ColorRGBA_uint8 = SettingsField(( + 255, 250, 90, 1.0), title="Layout:") + vdbcache: ColorRGBA_uint8 = SettingsField( + (249, 54, 0, 1.0), title="VDB Cache:") + vrayproxy: ColorRGBA_uint8 = SettingsField( + (255, 150, 12, 1.0), title="VRay Proxy:") + vrayscene_layer: ColorRGBA_uint8 = SettingsField( + (255, 150, 12, 1.0), title="VRay Scene:") + yeticache: ColorRGBA_uint8 = SettingsField( + (99, 206, 220, 1.0), title="Yeti Cache:") + yetiRig: ColorRGBA_uint8 = SettingsField( + (0, 205, 125, 1.0), title="Yeti Rig:") + # model: ColorRGB_float = SettingsField( + # (0.82, 0.52, 0.12), title="Model:" + # ) + # rig: ColorRGB_float = SettingsField( + # (0.23, 0.89, 0.92), title="Rig:" + # ) + # pointcache: ColorRGB_float = SettingsField( + # (0.37, 0.82, 0.12), title="Pointcache:" + # ) + # animation: ColorRGB_float = SettingsField( + # (0.37, 0.82, 0.12), title="Animation:" + # ) + # ass: ColorRGB_float = SettingsField( + # (0.98, 0.53, 0.21), title="Arnold StandIn:" + # ) + # camera: ColorRGB_float = SettingsField( + # (0.53, 0.45, 0.96), title="Camera:" + # ) + # fbx: ColorRGB_float = SettingsField( + # (0.84, 0.65, 1.0), title="FBX:" + # ) + # mayaAscii: ColorRGB_float = SettingsField( + # (0.26, 0.68, 1.0), title="Maya Ascii:" + # ) + # mayaScene: ColorRGB_float = SettingsField( + # (0.26, 0.68, 1.0), title="Maya Scene:" + # ) + # setdress: ColorRGB_float = SettingsField( + # (1.0, 0.98, 0.35), title="Set Dress:" + # ) + # layout: ColorRGB_float = SettingsField( + # (1.0, 0.98, 0.35), title="Layout:" + # ) + # vdbcache: ColorRGB_float = SettingsField( + # (0.98, 0.21, 0.0), title="VDB Cache:" + # ) + # vrayproxy: ColorRGB_float = SettingsField( + # (1.0, 0.59, 0.05), title="VRay Proxy:" + # ) + # vrayscene_layer: ColorRGB_float = SettingsField( + # (1.0, 0.59, 0.05), title="VRay Scene:" + # ) + # yeticache: ColorRGB_float = SettingsField( + # (0.39, 0.81, 0.86), title="Yeti Cache:" + # ) + # yetiRig: ColorRGB_float = SettingsField( + # (0.0, 0.80, 0.49), title="Yeti Rig:" + # ) class ReferenceLoaderModel(BaseSettingsModel): @@ -99,22 +115,38 @@ class LoadersModel(BaseSettingsModel): DEFAULT_LOADERS_SETTING = { "colors": { - "model": [0.82, 0.52, 0.12], - "rig": [0.23, 0.89, 0.92], - "pointcache": [0.37, 0.82, 0.12], - "animation": [0.37, 0.82, 0.12], - "ass": [0.98, 0.53, 0.21], - "camera":[0.53, 0.45, 0.96], - "fbx": [0.84, 0.65, 1.0], - "mayaAscii": [0.26, 0.68, 1.0], - "mayaScene": [0.26, 0.68, 1.0], - "setdress": [1.0, 0.98, 0.35], - "layout": [1.0, 0.98, 0.35], - "vdbcache": [0.98, 0.21, 0.0], - "vrayproxy": [1.0, 0.59, 0.05], - "vrayscene_layer": [1.0, 0.59, 0.05], - "yeticache": [0.39, 0.81, 0.86], - "yetiRig": [0.0, 0.80, 0.49], + "model": [209, 132, 30, 1.0], + "rig": [59, 226, 235, 1.0], + "pointcache": [94, 209, 30, 1.0], + "animation": [94, 209, 30, 1.0], + "ass": [249, 135, 53, 1.0], + "camera": [136, 114, 244, 1.0], + "fbx": [215, 166, 255, 1.0], + "mayaAscii": [67, 174, 255, 1.0], + "mayaScene": [67, 174, 255, 1.0], + "setdress": [255, 250, 90, 1.0], + "layout": [255, 250, 90, 1.0], + "vdbcache": [249, 54, 0, 1.0], + "vrayproxy": [255, 150, 12, 1.0], + "vrayscene_layer": [255, 150, 12, 1.0], + "yeticache": [99, 206, 220, 1.0], + "yetiRig": [0, 205, 125, 1.0] + # "model": [0.82, 0.52, 0.12], + # "rig": [0.23, 0.89, 0.92], + # "pointcache": [0.37, 0.82, 0.12], + # "animation": [0.37, 0.82, 0.12], + # "ass": [0.98, 0.53, 0.21], + # "camera":[0.53, 0.45, 0.96], + # "fbx": [0.84, 0.65, 1.0], + # "mayaAscii": [0.26, 0.68, 1.0], + # "mayaScene": [0.26, 0.68, 1.0], + # "setdress": [1.0, 0.98, 0.35], + # "layout": [1.0, 0.98, 0.35], + # "vdbcache": [0.98, 0.21, 0.0], + # "vrayproxy": [1.0, 0.59, 0.05], + # "vrayscene_layer": [1.0, 0.59, 0.05], + # "yeticache": [0.39, 0.81, 0.86], + # "yetiRig": [0.0, 0.80, 0.49], }, "reference_loader": { "namespace": "{folder[name]}_{product[name]}_##_", diff --git a/server_addon/maya/server/settings/publish_playblast.py b/server_addon/maya/server/settings/publish_playblast.py index dcedf3ccc9..39f48bacbe 100644 --- a/server_addon/maya/server/settings/publish_playblast.py +++ b/server_addon/maya/server/settings/publish_playblast.py @@ -6,7 +6,7 @@ from ayon_server.settings import ( ensure_unique_names, task_types_enum, ) -from ayon_server.types import ColorRGB_float +from ayon_server.types import ColorRGBA_uint8, ColorRGB_float def hardware_falloff_enum(): @@ -54,18 +54,27 @@ class DisplayOptionsSetting(BaseSettingsModel): override_display: bool = SettingsField( True, title="Override display options" ) - background: ColorRGB_float = SettingsField( - (0.5, 0.5, 0.5), title="Background Color" + background: ColorRGBA_uint8 = SettingsField( + (125, 125, 125, 1.0), title="Background Color" ) + # background: ColorRGB_float = SettingsField( + # (0.5, 0.5, 0.5), title="Background Color" + # ) displayGradient: bool = SettingsField( True, title="Display background gradient" ) - backgroundTop: ColorRGB_float = SettingsField( - (0.5, 0.5, 0.5), title="Background Top" + backgroundTop: ColorRGBA_uint8 = SettingsField( + (125, 125, 125, 1.0), title="Background Top" ) - backgroundBottom: ColorRGB_float = SettingsField( - (0.5, 0.5, 0.5), title="Background Bottom" + backgroundBottom: ColorRGBA_uint8 = SettingsField( + (125, 125, 125, 1.0), title="Background Bottom" ) + # backgroundTop: ColorRGB_float = SettingsField( + # (0.5, 0.5, 0.5), title="Background Top" + # ) + # backgroundBottom: ColorRGB_float = SettingsField( + # (0.5, 0.5, 0.5), title="Background Bottom" + # ) class GenericSetting(BaseSettingsModel): @@ -282,21 +291,12 @@ DEFAULT_PLAYBLAST_SETTING = { }, "DisplayOptions": { "override_display": True, - "background": [ - 0.5, - 0.5, - 0.5 - ], - "backgroundBottom": [ - 0.5, - 0.5, - 0.5 - ], - "backgroundTop": [ - 0.5, - 0.5, - 0.5 - ], + "background": [125, 125, 125, 1.0], + "backgroundBottom": [125, 125, 125, 1.0], + "backgroundTop": [125, 125, 125, 1.0], + # "background": [0.5, 0.5, 0.5], + # "backgroundBottom": [0.5, 0.5, 0.5], + # "backgroundTop": [0.5, 0.5, 0.5], "displayGradient": True }, "Generic": { diff --git a/server_addon/tvpaint/server/settings/publish_plugins.py b/server_addon/tvpaint/server/settings/publish_plugins.py index 37ad3e0e70..0d978e5714 100644 --- a/server_addon/tvpaint/server/settings/publish_plugins.py +++ b/server_addon/tvpaint/server/settings/publish_plugins.py @@ -1,5 +1,5 @@ from ayon_server.settings import BaseSettingsModel, SettingsField -from ayon_server.types import ColorRGB_uint8 +from ayon_server.types import ColorRGBA_uint8, ColorRGB_uint8 class CollectRenderInstancesModel(BaseSettingsModel): @@ -10,10 +10,12 @@ class CollectRenderInstancesModel(BaseSettingsModel): class ExtractSequenceModel(BaseSettingsModel): """Review BG color is used for whole scene review and for thumbnails.""" - # TODO Use alpha color - review_bg: ColorRGB_uint8 = SettingsField( - (255, 255, 255), + review_bg: ColorRGBA_uint8 = SettingsField( + (255, 255, 255, 1.0), title="Review BG color") + # review_bg: ColorRGB_uint8 = SettingsField( + # (255, 255, 255), + # title="Review BG color") class ValidatePluginModel(BaseSettingsModel): @@ -100,7 +102,8 @@ DEFAULT_PUBLISH_SETTINGS = { "ignore_render_pass_transparency": False }, "ExtractSequence": { - "review_bg": [255, 255, 255] + # "review_bg": [255, 255, 255] + "review_bg": [255, 255, 255, 1.0] }, "ValidateProjectSettings": { "enabled": True, From a811ef9a0ce9c79b99a1a41bd9f5730d35e4a93c Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 15 Feb 2024 17:58:05 +0800 Subject: [PATCH 112/573] remove unrelated code --- client/ayon_core/hosts/max/api/pipeline.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/client/ayon_core/hosts/max/api/pipeline.py b/client/ayon_core/hosts/max/api/pipeline.py index 106c29fd26..c26e697429 100644 --- a/client/ayon_core/hosts/max/api/pipeline.py +++ b/client/ayon_core/hosts/max/api/pipeline.py @@ -242,20 +242,3 @@ def get_previous_loaded_object(container: str): if str(obj) in sel_list: node_list.append(obj) return node_list - - -def remove_container_data(container: str): - """Function to remove container data after updating, switching or deleting it. - - Args: - container (str): container - """ - if container.modifiers[0].name == "OP Data": - all_set_members_names = [ - member.node for member - in container.modifiers[0].openPypeData.all_handles] - for current_set_member in all_set_members_names: - rt.Delete(current_set_member) - rt.deleteModifier(container, container.modifiers[0]) - - rt.Delete(container) From 87df73da2242ac3ba32378f681d979ab6a011ed2 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 15 Feb 2024 18:12:32 +0800 Subject: [PATCH 113/573] restored unrelated code --- client/ayon_core/hosts/max/api/pipeline.py | 17 ----------------- .../hosts/max/plugins/load/load_camera_fbx.py | 8 ++++---- .../hosts/max/plugins/load/load_max_scene.py | 9 ++++----- .../hosts/max/plugins/load/load_model.py | 8 +++----- .../hosts/max/plugins/load/load_model_fbx.py | 9 ++++----- .../hosts/max/plugins/load/load_model_obj.py | 6 +++--- .../hosts/max/plugins/load/load_model_usd.py | 5 ++--- .../hosts/max/plugins/load/load_pointcache.py | 5 ++--- .../plugins/load/load_pointcache_ornatrix.py | 5 ++--- .../hosts/max/plugins/load/load_pointcloud.py | 6 +++--- .../max/plugins/load/load_redshift_proxy.py | 6 +++--- .../hosts/max/plugins/load/load_tycache.py | 9 +++++---- 12 files changed, 35 insertions(+), 58 deletions(-) diff --git a/client/ayon_core/hosts/max/api/pipeline.py b/client/ayon_core/hosts/max/api/pipeline.py index 106c29fd26..c26e697429 100644 --- a/client/ayon_core/hosts/max/api/pipeline.py +++ b/client/ayon_core/hosts/max/api/pipeline.py @@ -242,20 +242,3 @@ def get_previous_loaded_object(container: str): if str(obj) in sel_list: node_list.append(obj) return node_list - - -def remove_container_data(container: str): - """Function to remove container data after updating, switching or deleting it. - - Args: - container (str): container - """ - if container.modifiers[0].name == "OP Data": - all_set_members_names = [ - member.node for member - in container.modifiers[0].openPypeData.all_handles] - for current_set_member in all_set_members_names: - rt.Delete(current_set_member) - rt.deleteModifier(container, container.modifiers[0]) - - rt.Delete(container) diff --git a/client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py b/client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py index d7eebdbc3a..34b120c179 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py +++ b/client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py @@ -1,6 +1,6 @@ import os -from ayon_core.hosts.max.api import lib +from ayon_core.hosts.max.api import lib, maintained_selection from ayon_core.hosts.max.api.lib import ( unique_namespace, get_namespace, @@ -9,8 +9,7 @@ from ayon_core.hosts.max.api.lib import ( from ayon_core.hosts.max.api.pipeline import ( containerise, get_previous_loaded_object, - update_custom_attribute_data, - remove_container_data + update_custom_attribute_data ) from ayon_core.pipeline import get_representation_path, load @@ -95,5 +94,6 @@ class FbxLoader(load.LoaderPlugin): def remove(self, container): from pymxs import runtime as rt + node = rt.GetNodeByName(container["instance_node"]) - remove_container_data(node) + rt.Delete(node) diff --git a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py index 81f3af089f..7267d7a59e 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py +++ b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py @@ -7,10 +7,8 @@ from ayon_core.hosts.max.api.lib import ( object_transform_set ) from ayon_core.hosts.max.api.pipeline import ( - containerise, - get_previous_loaded_object, - update_custom_attribute_data, - remove_container_data + containerise, get_previous_loaded_object, + update_custom_attribute_data ) from ayon_core.pipeline import get_representation_path, load @@ -95,5 +93,6 @@ class MaxSceneLoader(load.LoaderPlugin): def remove(self, container): from pymxs import runtime as rt + node = rt.GetNodeByName(container["instance_node"]) - remove_container_data(node) + rt.Delete(node) diff --git a/client/ayon_core/hosts/max/plugins/load/load_model.py b/client/ayon_core/hosts/max/plugins/load/load_model.py index 28ec7be01f..796e1b80ad 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_model.py +++ b/client/ayon_core/hosts/max/plugins/load/load_model.py @@ -2,13 +2,11 @@ import os from ayon_core.pipeline import load, get_representation_path from ayon_core.hosts.max.api.pipeline import ( containerise, - get_previous_loaded_object, - remove_container_data + get_previous_loaded_object ) from ayon_core.hosts.max.api import lib from ayon_core.hosts.max.api.lib import ( - maintained_selection, - unique_namespace + maintained_selection, unique_namespace ) @@ -101,7 +99,7 @@ class ModelAbcLoader(load.LoaderPlugin): from pymxs import runtime as rt node = rt.GetNodeByName(container["instance_node"]) - remove_container_data(node) + rt.Delete(node) @staticmethod def get_container_children(parent, type_name): diff --git a/client/ayon_core/hosts/max/plugins/load/load_model_fbx.py b/client/ayon_core/hosts/max/plugins/load/load_model_fbx.py index 81ad84546a..827cf63b39 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_model_fbx.py +++ b/client/ayon_core/hosts/max/plugins/load/load_model_fbx.py @@ -1,10 +1,8 @@ import os from ayon_core.pipeline import load, get_representation_path from ayon_core.hosts.max.api.pipeline import ( - containerise, - get_previous_loaded_object, - update_custom_attribute_data, - remove_container_data + containerise, get_previous_loaded_object, + update_custom_attribute_data ) from ayon_core.hosts.max.api import lib from ayon_core.hosts.max.api.lib import ( @@ -94,5 +92,6 @@ class FbxModelLoader(load.LoaderPlugin): def remove(self, container): from pymxs import runtime as rt + node = rt.GetNodeByName(container["instance_node"]) - remove_container_data(node) + rt.Delete(node) diff --git a/client/ayon_core/hosts/max/plugins/load/load_model_obj.py b/client/ayon_core/hosts/max/plugins/load/load_model_obj.py index 1023b67f0c..22d3d4b58a 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_model_obj.py +++ b/client/ayon_core/hosts/max/plugins/load/load_model_obj.py @@ -11,8 +11,7 @@ from ayon_core.hosts.max.api.lib import maintained_selection from ayon_core.hosts.max.api.pipeline import ( containerise, get_previous_loaded_object, - update_custom_attribute_data, - remove_container_data + update_custom_attribute_data ) from ayon_core.pipeline import get_representation_path, load @@ -85,5 +84,6 @@ class ObjLoader(load.LoaderPlugin): def remove(self, container): from pymxs import runtime as rt + node = rt.GetNodeByName(container["instance_node"]) - remove_container_data(node) + rt.Delete(node) diff --git a/client/ayon_core/hosts/max/plugins/load/load_model_usd.py b/client/ayon_core/hosts/max/plugins/load/load_model_usd.py index 6a08bebf5a..8d42219217 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_model_usd.py +++ b/client/ayon_core/hosts/max/plugins/load/load_model_usd.py @@ -13,8 +13,7 @@ from ayon_core.hosts.max.api.lib import maintained_selection from ayon_core.hosts.max.api.pipeline import ( containerise, get_previous_loaded_object, - update_custom_attribute_data, - remove_container_data + update_custom_attribute_data ) from ayon_core.pipeline import get_representation_path, load @@ -115,4 +114,4 @@ class ModelUSDLoader(load.LoaderPlugin): def remove(self, container): node = rt.GetNodeByName(container["instance_node"]) - remove_container_data(node) + rt.Delete(node) diff --git a/client/ayon_core/hosts/max/plugins/load/load_pointcache.py b/client/ayon_core/hosts/max/plugins/load/load_pointcache.py index d7267afb7d..a92fa66757 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_pointcache.py +++ b/client/ayon_core/hosts/max/plugins/load/load_pointcache.py @@ -10,8 +10,7 @@ from ayon_core.hosts.max.api import lib, maintained_selection from ayon_core.hosts.max.api.lib import unique_namespace from ayon_core.hosts.max.api.pipeline import ( containerise, - get_previous_loaded_object, - remove_container_data + get_previous_loaded_object ) @@ -106,7 +105,7 @@ class AbcLoader(load.LoaderPlugin): from pymxs import runtime as rt node = rt.GetNodeByName(container["instance_node"]) - remove_container_data(node) + rt.Delete(node) @staticmethod def get_container_children(parent, type_name): diff --git a/client/ayon_core/hosts/max/plugins/load/load_pointcache_ornatrix.py b/client/ayon_core/hosts/max/plugins/load/load_pointcache_ornatrix.py index 5b48e5d189..27b2e271d2 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_pointcache_ornatrix.py +++ b/client/ayon_core/hosts/max/plugins/load/load_pointcache_ornatrix.py @@ -4,8 +4,7 @@ from ayon_core.pipeline.load import LoadError from ayon_core.hosts.max.api.pipeline import ( containerise, get_previous_loaded_object, - update_custom_attribute_data, - remove_container_data + update_custom_attribute_data ) from ayon_core.hosts.max.api.lib import ( @@ -106,4 +105,4 @@ class OxAbcLoader(load.LoaderPlugin): def remove(self, container): node = rt.GetNodeByName(container["instance_node"]) - remove_container_data(node) \ No newline at end of file + rt.Delete(node) diff --git a/client/ayon_core/hosts/max/plugins/load/load_pointcloud.py b/client/ayon_core/hosts/max/plugins/load/load_pointcloud.py index 7f4fba50b3..45e3da5621 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_pointcloud.py +++ b/client/ayon_core/hosts/max/plugins/load/load_pointcloud.py @@ -8,8 +8,7 @@ from ayon_core.hosts.max.api.lib import ( from ayon_core.hosts.max.api.pipeline import ( containerise, get_previous_loaded_object, - update_custom_attribute_data, - remove_container_data + update_custom_attribute_data ) from ayon_core.pipeline import get_representation_path, load @@ -64,5 +63,6 @@ class PointCloudLoader(load.LoaderPlugin): def remove(self, container): """remove the container""" from pymxs import runtime as rt + node = rt.GetNodeByName(container["instance_node"]) - remove_container_data(node) + rt.Delete(node) diff --git a/client/ayon_core/hosts/max/plugins/load/load_redshift_proxy.py b/client/ayon_core/hosts/max/plugins/load/load_redshift_proxy.py index 3ccc5cc5e1..3f73210c24 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_redshift_proxy.py +++ b/client/ayon_core/hosts/max/plugins/load/load_redshift_proxy.py @@ -9,8 +9,7 @@ from ayon_core.pipeline.load import LoadError from ayon_core.hosts.max.api.pipeline import ( containerise, update_custom_attribute_data, - get_previous_loaded_object, - remove_container_data + get_previous_loaded_object ) from ayon_core.hosts.max.api import lib from ayon_core.hosts.max.api.lib import ( @@ -73,5 +72,6 @@ class RedshiftProxyLoader(load.LoaderPlugin): def remove(self, container): from pymxs import runtime as rt + node = rt.getNodeByName(container["instance_node"]) - remove_container_data(node) + rt.delete(node) diff --git a/client/ayon_core/hosts/max/plugins/load/load_tycache.py b/client/ayon_core/hosts/max/plugins/load/load_tycache.py index 97f41026b4..48fb5c447a 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_tycache.py +++ b/client/ayon_core/hosts/max/plugins/load/load_tycache.py @@ -1,13 +1,13 @@ import os from ayon_core.hosts.max.api import lib, maintained_selection from ayon_core.hosts.max.api.lib import ( - unique_namespace + unique_namespace, + ) from ayon_core.hosts.max.api.pipeline import ( containerise, get_previous_loaded_object, - update_custom_attribute_data, - remove_container_data + update_custom_attribute_data ) from ayon_core.pipeline import get_representation_path, load @@ -59,5 +59,6 @@ class TyCacheLoader(load.LoaderPlugin): def remove(self, container): """remove the container""" from pymxs import runtime as rt + node = rt.GetNodeByName(container["instance_node"]) - remove_container_data(node) + rt.Delete(node) From a6004505af963122ba0eade8bfd896c761bf767b Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 15 Feb 2024 18:43:57 +0800 Subject: [PATCH 114/573] rename container to container node --- client/ayon_core/hosts/max/api/pipeline.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/hosts/max/api/pipeline.py b/client/ayon_core/hosts/max/api/pipeline.py index 106c29fd26..98cb95eb9a 100644 --- a/client/ayon_core/hosts/max/api/pipeline.py +++ b/client/ayon_core/hosts/max/api/pipeline.py @@ -244,18 +244,18 @@ def get_previous_loaded_object(container: str): return node_list -def remove_container_data(container: str): +def remove_container_data(container_node: str): """Function to remove container data after updating, switching or deleting it. Args: - container (str): container + container_node (str): container node """ - if container.modifiers[0].name == "OP Data": + if container_node.modifiers[0].name == "OP Data": all_set_members_names = [ member.node for member - in container.modifiers[0].openPypeData.all_handles] + in container_node.modifiers[0].openPypeData.all_handles] for current_set_member in all_set_members_names: rt.Delete(current_set_member) - rt.deleteModifier(container, container.modifiers[0]) + rt.deleteModifier(container_node, container_node.modifiers[0]) - rt.Delete(container) + rt.Delete(container_node) From 3c726393fc64789ea4988009fda701d6e0969eb8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 14:27:48 +0100 Subject: [PATCH 115/573] bump version to 0.2.1 --- client/ayon_core/version.py | 2 +- package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 914e415b8c..46dc6c6876 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON core addon version.""" -__version__ = "0.2.1-dev.1" +__version__ = "0.2.1" diff --git a/package.py b/package.py index 3b451e0078..5a1376c209 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "0.2.1-dev.1" +version = "0.2.1" client_dir = "ayon_core" From 6baba8633f80698639ee0fa8e0493924830e8d3a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 14:33:59 +0100 Subject: [PATCH 116/573] bump version to '0.3.0-dev.1' --- client/ayon_core/version.py | 2 +- package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 46dc6c6876..f3ad9713d5 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON core addon version.""" -__version__ = "0.2.1" +__version__ = "0.3.0-dev.1" diff --git a/package.py b/package.py index 5a1376c209..470bbf256b 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "0.2.1" +version = "0.3.0-dev.1" client_dir = "ayon_core" From b1bfe48d992095c302c2492e3378f3471e149638 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 15:04:42 +0100 Subject: [PATCH 117/573] moved load clip settings conversion to the code --- .../hosts/hiero/plugins/load/load_clip.py | 25 +++++++++++++------ client/ayon_core/settings/ayon_settings.py | 11 -------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/client/ayon_core/hosts/hiero/plugins/load/load_clip.py b/client/ayon_core/hosts/hiero/plugins/load/load_clip.py index d77a28872f..b8c51e7536 100644 --- a/client/ayon_core/hosts/hiero/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/hiero/plugins/load/load_clip.py @@ -54,25 +54,36 @@ class LoadClip(phiero.SequenceLoader): plugin_name = cls.__name__ - plugin_settings = None # Look for plugin settings in host specific settings - if plugin_name in plugin_type_settings: - plugin_settings = plugin_type_settings[plugin_name] - + plugin_settings = plugin_type_settings.get(plugin_name) if not plugin_settings: return print(">>> We have preset for {}".format(plugin_name)) for option, value in plugin_settings.items(): + if option == "representations": + continue + + if option == "product_types": + # TODO remove the key conversion when loaders can filter by + # product types + # convert 'product_types' to 'families' + option = "families" + + elif option == "clip_name_template": + # TODO remove the formatting replacement + value = ( + value + .replace("{folder[name]}", "{asset}") + .replace("{product[name]}", "{subset}") + ) + if option == "enabled" and value is False: print(" - is disabled by preset") - elif option == "representations": - continue else: print(" - setting `{}`: `{}`".format(option, value)) setattr(cls, option, value) - def load(self, context, name, namespace, options): # add clip name template to options options.update({ diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index 190970b908..ec044479f0 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -371,17 +371,6 @@ def _convert_hiero_project_settings(ayon_settings, output): new_gui_filters[key] = subvalue ayon_hiero["filters"] = new_gui_filters - ayon_load_clip = ayon_hiero["load"]["LoadClip"] - if "product_types" in ayon_load_clip: - ayon_load_clip["families"] = ayon_load_clip.pop("product_types") - - ayon_load_clip = ayon_hiero["load"]["LoadClip"] - ayon_load_clip["clip_name_template"] = ( - ayon_load_clip["clip_name_template"] - .replace("{folder[name]}", "{asset}") - .replace("{product[name]}", "{subset}") - ) - output["hiero"] = ayon_hiero From e0d4f2b9d37376f641c520da146e278786d3a2c5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 15:05:07 +0100 Subject: [PATCH 118/573] pyblish pype can use AYON settings for filters --- client/ayon_core/settings/ayon_settings.py | 9 -------- .../ayon_core/tools/pyblish_pype/control.py | 22 ++++++++++++++++++- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index ec044479f0..e2882fc57f 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -362,15 +362,6 @@ def _convert_hiero_project_settings(ayon_settings, output): ayon_hiero = ayon_settings["hiero"] - new_gui_filters = {} - for item in ayon_hiero.pop("filters", []): - subvalue = {} - key = item["name"] - for subitem in item["value"]: - subvalue[subitem["name"]] = subitem["value"] - new_gui_filters[key] = subvalue - ayon_hiero["filters"] = new_gui_filters - output["hiero"] = ayon_hiero diff --git a/client/ayon_core/tools/pyblish_pype/control.py b/client/ayon_core/tools/pyblish_pype/control.py index 1a3e7a15f0..c5034e2736 100644 --- a/client/ayon_core/tools/pyblish_pype/control.py +++ b/client/ayon_core/tools/pyblish_pype/control.py @@ -202,19 +202,39 @@ class Controller(QtCore.QObject): def current_state(self): return self._current_state + @staticmethod + def _convert_filter_presets(filter_presets): + """Convert AYON settings presets to dictionary. + + Returns: + dict[str, dict[str, Any]]: Filter presets converted to dictionary. + """ + if not isinstance(filter_presets, list): + return filter_presets + + return { + filter_preset["name"]: { + item["name"]: item["value"] + for item in filter_preset["value"] + } + for filter_preset in filter_presets + } + def presets_by_hosts(self): # Get global filters as base presets = get_current_project_settings() if not presets: return {} - result = presets.get("global", {}).get("filters", {}) + result = {} hosts = pyblish.api.registered_hosts() for host in hosts: host_presets = presets.get(host, {}).get("filters") if not host_presets: continue + host_presets = self._convert_filter_presets(host_presets) + for key, value in host_presets.items(): if value is None: if key in result: From 09867cd83d6da46e1d11d110768935199ad490fa Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 15:05:17 +0100 Subject: [PATCH 119/573] removed specific hiero conversion --- client/ayon_core/settings/ayon_settings.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index e2882fc57f..6597e12cd7 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -356,15 +356,6 @@ def _convert_nuke_project_settings(ayon_settings, output): output["nuke"] = ayon_nuke -def _convert_hiero_project_settings(ayon_settings, output): - if "hiero" not in ayon_settings: - return - - ayon_hiero = ayon_settings["hiero"] - - output["hiero"] = ayon_hiero - - def _convert_royalrender_project_settings(ayon_settings, output): if "royalrender" not in ayon_settings: return @@ -554,7 +545,6 @@ def convert_project_settings(ayon_settings, default_settings): output = {} _convert_nuke_project_settings(ayon_settings, output) - _convert_hiero_project_settings(ayon_settings, output) _convert_royalrender_project_settings(ayon_settings, output) From ae91da1d003c2538b1fb039382b1d900df68284e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 15:17:44 +0100 Subject: [PATCH 120/573] nuke loaders are using AYON settings --- client/ayon_core/hosts/nuke/plugins/load/load_clip.py | 4 ++-- client/ayon_core/hosts/nuke/plugins/load/load_image.py | 4 ++-- client/ayon_core/settings/ayon_settings.py | 9 --------- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py index 8bce2eac6e..d2a8f2c93b 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py @@ -53,7 +53,7 @@ class LoadClip(plugin.NukeLoader): color = "white" # Loaded from settings - _representations = [] + representations_include = [] script_start = int(nuke.root()["first_frame"].value()) @@ -82,7 +82,7 @@ class LoadClip(plugin.NukeLoader): @classmethod def get_representations(cls): - return cls._representations or cls.representations + return cls.representations_include or cls.representations def load(self, context, name, namespace, options): """Load asset via database diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_image.py b/client/ayon_core/hosts/nuke/plugins/load/load_image.py index b9f47bddc9..e9435ec10a 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_image.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_image.py @@ -47,7 +47,7 @@ class LoadImage(load.LoaderPlugin): color = "white" # Loaded from settings - _representations = [] + representations_include = [] node_name_template = "{class_name}_{ext}" @@ -64,7 +64,7 @@ class LoadImage(load.LoaderPlugin): @classmethod def get_representations(cls): - return cls._representations or cls.representations + return cls.representations_include or cls.representations def load(self, context, name, namespace, options): self.log.info("__ options: `{}`".format(options)) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index 190970b908..b9b873a805 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -222,15 +222,6 @@ def _convert_nuke_project_settings(ayon_settings, output): ayon_nuke = ayon_settings["nuke"] - # --- Load --- - ayon_load = ayon_nuke["load"] - ayon_load["LoadClip"]["_representations"] = ( - ayon_load["LoadClip"].pop("representations_include") - ) - ayon_load["LoadImage"]["_representations"] = ( - ayon_load["LoadImage"].pop("representations_include") - ) - # --- Create --- ayon_create = ayon_nuke["create"] for creator_name in ( From c133cd486a9c03afec346068849ba25f6ad4f992 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 15:20:46 +0100 Subject: [PATCH 121/573] nuke creators are using AYON settings --- client/ayon_core/hosts/nuke/api/lib.py | 9 ++++---- client/ayon_core/hosts/nuke/api/plugin.py | 19 +++++++++++------ client/ayon_core/settings/ayon_settings.py | 24 ---------------------- 3 files changed, 18 insertions(+), 34 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index c320df9361..01462b5160 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -1175,8 +1175,9 @@ def create_prenodes( ): last_node = None for_dependency = {} - for name, node in nodes_setting.items(): + for node in nodes_setting: # get attributes + name = node["name"] nodeclass = node["nodeclass"] knobs = node["knobs"] @@ -1238,8 +1239,8 @@ def create_write_node( name (str): name of node data (dict): creator write instance data input (node)[optional]: selected node to connect to - prenodes (dict)[optional]: - nodes to be created before write with dependency + prenodes (Optional[list[dict]]): nodes to be created before write + with dependency review (bool)[optional]: adding review knob farm (bool)[optional]: rendering workflow target kwargs (dict)[optional]: additional key arguments for formatting @@ -1268,7 +1269,7 @@ def create_write_node( Return: node (obj): group node with avalon data as Knobs ''' - prenodes = prenodes or {} + prenodes = prenodes or [] # filtering variables plugin_name = data["creator"] diff --git a/client/ayon_core/hosts/nuke/api/plugin.py b/client/ayon_core/hosts/nuke/api/plugin.py index 4b8ddac167..7b6d9b7765 100644 --- a/client/ayon_core/hosts/nuke/api/plugin.py +++ b/client/ayon_core/hosts/nuke/api/plugin.py @@ -394,17 +394,24 @@ class NukeWriteCreator(NukeCreator): # plugin settings plugin_settings = self.get_creator_settings(project_settings) - + temp_rendering_path_template = ( + plugin_settings.get("temp_rendering_path_template") + or self.temp_rendering_path_template + ) + temp_rendering_path_template = ( + temp_rendering_path_template + .replace("{product[name]}", "{subset}") + .replace("{product[type]}", "{family}") + .replace("{task[name]}", "{task}") + .replace("{folder[name]}", "{asset}") + ) # individual attributes self.instance_attributes = plugin_settings.get( "instance_attributes") or self.instance_attributes self.prenodes = plugin_settings["prenodes"] self.default_variants = plugin_settings.get( "default_variants") or self.default_variants - self.temp_rendering_path_template = ( - plugin_settings.get("temp_rendering_path_template") - or self.temp_rendering_path_template - ) + self.temp_rendering_path_template = temp_rendering_path_template class OpenPypeCreator(LegacyCreator): @@ -1059,7 +1066,7 @@ class AbstractWriteRender(OpenPypeCreator): icon = "sign-out" defaults = ["Main", "Mask"] knobs = [] - prenodes = {} + prenodes = [] def __init__(self, *args, **kwargs): super(AbstractWriteRender, self).__init__(*args, **kwargs) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index b9b873a805..023a2eff96 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -222,29 +222,6 @@ def _convert_nuke_project_settings(ayon_settings, output): ayon_nuke = ayon_settings["nuke"] - # --- Create --- - ayon_create = ayon_nuke["create"] - for creator_name in ( - "CreateWritePrerender", - "CreateWriteImage", - "CreateWriteRender", - ): - create_plugin_settings = ayon_create[creator_name] - create_plugin_settings["temp_rendering_path_template"] = ( - create_plugin_settings["temp_rendering_path_template"] - .replace("{product[name]}", "{subset}") - .replace("{product[type]}", "{family}") - .replace("{task[name]}", "{task}") - .replace("{folder[name]}", "{asset}") - ) - new_prenodes = {} - for prenode in create_plugin_settings["prenodes"]: - name = prenode.pop("name") - prenode["knobs"] = _convert_nuke_knobs(prenode["knobs"]) - new_prenodes[name] = prenode - - create_plugin_settings["prenodes"] = new_prenodes - # --- Publish --- ayon_publish = ayon_nuke["publish"] slate_mapping = ayon_publish["ExtractSlateFrame"]["key_value_mapping"] @@ -564,7 +541,6 @@ def convert_project_settings(ayon_settings, default_settings): default_settings = copy.deepcopy(default_settings) output = {} - _convert_nuke_project_settings(ayon_settings, output) _convert_hiero_project_settings(ayon_settings, output) _convert_royalrender_project_settings(ayon_settings, output) From 50beae069047ffa3577ad429a6cabc6c6dad61ab Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 16:11:14 +0100 Subject: [PATCH 122/573] nuke knobs conversion is handled in code --- client/ayon_core/hosts/nuke/api/lib.py | 170 ++++++++++-------- client/ayon_core/hosts/nuke/api/plugin.py | 3 +- .../publish/help/validate_write_nodes.xml | 2 +- .../plugins/publish/validate_write_nodes.py | 2 +- .../hosts/nuke/startup/custom_write_node.py | 4 +- client/ayon_core/settings/ayon_settings.py | 76 -------- 6 files changed, 99 insertions(+), 158 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index 01462b5160..e645055fc7 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -718,17 +718,17 @@ def get_created_node_imageio_setting_legacy(nodeclass, creator, subset): "`{}`: Missing mandatory kwargs `host`, `cls`".format(__file__)) imageio_nodes = get_nuke_imageio_settings()["nodes"] - required_nodes = imageio_nodes["requiredNodes"] + required_nodes = imageio_nodes["required_nodes"] # HACK: for backward compatibility this needs to be optional - override_nodes = imageio_nodes.get("overrideNodes", []) + override_nodes = imageio_nodes.get("override_nodes", []) imageio_node = None for node in required_nodes: log.info(node) if ( - nodeclass in node["nukeNodeClass"] - and creator in node["plugins"] + nodeclass in node["nuke_node_class"] + and creator in node["plugins"] ): imageio_node = node break @@ -739,10 +739,10 @@ def get_created_node_imageio_setting_legacy(nodeclass, creator, subset): override_imageio_node = None for onode in override_nodes: log.info(onode) - if nodeclass not in node["nukeNodeClass"]: + if nodeclass not in onode["nuke_node_class"]: continue - if creator not in node["plugins"]: + if creator not in onode["plugins"]: continue if ( @@ -764,26 +764,33 @@ def get_created_node_imageio_setting_legacy(nodeclass, creator, subset): knob_names = [k["name"] for k in imageio_node["knobs"]] for oknob in override_imageio_node["knobs"]: + oknob_name = oknob["name"] + oknob_type = oknob["type"] + oknob_value = oknob[oknob_type] for knob in imageio_node["knobs"]: - # override matching knob name - if oknob["name"] == knob["name"]: - log.debug( - "_ overriding knob: `{}` > `{}`".format( - knob, oknob - )) - if not oknob["value"]: - # remove original knob if no value found in oknob - imageio_node["knobs"].remove(knob) - else: - # override knob value with oknob's - knob["value"] = oknob["value"] - + knob_name = knob["name"] # add missing knobs into imageio_node - if oknob["name"] not in knob_names: + if oknob_name not in knob_names: log.debug( "_ adding knob: `{}`".format(oknob)) imageio_node["knobs"].append(oknob) - knob_names.append(oknob["name"]) + knob_names.append(oknob_name) + continue + + # override matching knob name + if oknob_name != knob_name: + continue + + knob_type = knob["type"] + log.debug( + "_ overriding knob: `{}` > `{}`".format(knob, oknob) + ) + if not oknob_value: + # remove original knob if no value found in oknob + imageio_node["knobs"].remove(knob) + else: + # override knob value with oknob's + knob[knob_type] = oknob_value log.info("ImageIO node: {}".format(imageio_node)) return imageio_node @@ -793,14 +800,14 @@ def get_imageio_node_setting(node_class, plugin_name, subset): ''' Get preset data for dataflow (fileType, compression, bitDepth) ''' imageio_nodes = get_nuke_imageio_settings()["nodes"] - required_nodes = imageio_nodes["requiredNodes"] + required_nodes = imageio_nodes["required_nodes"] imageio_node = None for node in required_nodes: log.info(node) if ( - node_class in node["nukeNodeClass"] - and plugin_name in node["plugins"] + node_class in node["nuke_node_class"] + and plugin_name in node["plugins"] ): imageio_node = node break @@ -828,14 +835,14 @@ def get_imageio_node_override_setting( ''' Get imageio node overrides from settings ''' imageio_nodes = get_nuke_imageio_settings()["nodes"] - override_nodes = imageio_nodes["overrideNodes"] + override_nodes = imageio_nodes["override_nodes"] # find matching override node override_imageio_node = None for onode in override_nodes: log.debug("__ onode: {}".format(onode)) log.debug("__ subset: {}".format(subset)) - if node_class not in onode["nukeNodeClass"]: + if node_class not in onode["nuke_node_class"]: continue if plugin_name not in onode["plugins"]: @@ -860,26 +867,31 @@ def get_imageio_node_override_setting( knob_names = [k["name"] for k in knobs_settings] for oknob in override_imageio_node["knobs"]: + oknob_name = oknob["name"] + oknob_type = oknob["type"] + oknob_value = oknob[oknob_type] for knob in knobs_settings: - # override matching knob name - if oknob["name"] == knob["name"]: - log.debug( - "_ overriding knob: `{}` > `{}`".format( - knob, oknob - )) - if not oknob["value"]: - # remove original knob if no value found in oknob - knobs_settings.remove(knob) - else: - # override knob value with oknob's - knob["value"] = oknob["value"] - # add missing knobs into imageio_node - if oknob["name"] not in knob_names: - log.debug( - "_ adding knob: `{}`".format(oknob)) + if oknob_name not in knob_names: + log.debug("_ adding knob: `{}`".format(oknob)) knobs_settings.append(oknob) - knob_names.append(oknob["name"]) + knob_names.append(oknob_name) + continue + + if oknob_name != knob["name"]: + continue + + knob_type = knob["type"] + # override matching knob name + log.debug( + "_ overriding knob: `{}` > `{}`".format(knob, oknob) + ) + if not oknob_value: + # remove original knob if no value found in oknob + knobs_settings.remove(knob) + else: + # override knob value with oknob's + knob[knob_type] = oknob_value return knobs_settings @@ -1700,42 +1712,32 @@ def set_node_knobs_from_settings(node, knob_settings, **kwargs): """ for knob in knob_settings: log.debug("__ knob: {}".format(pformat(knob))) - knob_type = knob["type"] knob_name = knob["name"] - if knob_name not in node.knobs(): continue + knob_type = knob["type"] + knob_value = knob[knob_type] if knob_type == "expression": - knob_expression = knob["expression"] - node[knob_name].setExpression( - knob_expression - ) + node[knob_name].setExpression(knob_value) continue # first deal with formattable knob settings if knob_type == "formatable": - template = knob["template"] - to_type = knob["to_type"] + template = knob_value["template"] + to_type = knob_value["to_type"] try: - _knob_value = template.format( - **kwargs - ) + knob_value = template.format(**kwargs) except KeyError as msg: raise KeyError( "Not able to format expression: {}".format(msg)) # convert value to correct type if to_type == "2d_vector": - knob_value = _knob_value.split(";").split(",") - else: - knob_value = _knob_value + knob_value = knob_value.split(";").split(",") knob_type = to_type - else: - knob_value = knob["value"] - if not knob_value: continue @@ -1746,24 +1748,38 @@ def set_node_knobs_from_settings(node, knob_settings, **kwargs): def convert_knob_value_to_correct_type(knob_type, knob_value): - # first convert string types to string - # just to ditch unicode - if isinstance(knob_value, six.text_type): - knob_value = str(knob_value) + # Convert 'text' to string to avoid unicode + if knob_type == "text": + return str(knob_value) - # set correctly knob types - if knob_type == "bool": - knob_value = bool(knob_value) - elif knob_type == "decimal_number": - knob_value = float(knob_value) - elif knob_type == "number": - knob_value = int(knob_value) - elif knob_type == "text": - knob_value = knob_value - elif knob_type == "color_gui": - knob_value = color_gui_to_int(knob_value) - elif knob_type in ["2d_vector", "3d_vector", "color", "box"]: - knob_value = [float(val_) for val_ in knob_value] + if knob_type == "boolean": + return bool(knob_value) + + if knob_type == "decimal_number": + return float(knob_value) + + if knob_type == "number": + return int(knob_value) + + if knob_type == "color_gui": + new_color = [] + for value in knob_value: + if isinstance(value, float): + value = int(value * 255) + new_color.append(value) + return color_gui_to_int(new_color) + + if knob_type == "box": + return [ + knob_value["x"], knob_value["y"], + knob_value["r"], knob_value["t"] + ] + + if knob_type == "vector_2d": + return [knob_value["x"], knob_value["y"]] + + if knob_type == "vector_3d": + return [knob_value["x"], knob_value["y"], knob_value["z"]] return knob_value diff --git a/client/ayon_core/hosts/nuke/api/plugin.py b/client/ayon_core/hosts/nuke/api/plugin.py index 7b6d9b7765..d43e59962a 100644 --- a/client/ayon_core/hosts/nuke/api/plugin.py +++ b/client/ayon_core/hosts/nuke/api/plugin.py @@ -398,6 +398,7 @@ class NukeWriteCreator(NukeCreator): plugin_settings.get("temp_rendering_path_template") or self.temp_rendering_path_template ) + # TODO remove template key replacements temp_rendering_path_template = ( temp_rendering_path_template .replace("{product[name]}", "{subset}") @@ -1172,7 +1173,7 @@ class AbstractWriteRender(OpenPypeCreator): bool: True if legacy """ imageio_nodes = get_nuke_imageio_settings()["nodes"] - node = imageio_nodes["requiredNodes"][0] + node = imageio_nodes["required_nodes"][0] if "type" not in node["knobs"][0]: # if type is not yet in project anatomy return True diff --git a/client/ayon_core/hosts/nuke/plugins/publish/help/validate_write_nodes.xml b/client/ayon_core/hosts/nuke/plugins/publish/help/validate_write_nodes.xml index 1717622a45..96aa6e4494 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/help/validate_write_nodes.xml +++ b/client/ayon_core/hosts/nuke/plugins/publish/help/validate_write_nodes.xml @@ -25,7 +25,7 @@ ### How to repair? Contact your supervisor or fix it in project settings at - 'project_settings/nuke/imageio/nodes/requiredNodes' at knobs. + 'project_settings/nuke/imageio/nodes/required_nodes' at knobs. Each '__legacy__' type has to be defined accordingly to its type. diff --git a/client/ayon_core/hosts/nuke/plugins/publish/validate_write_nodes.py b/client/ayon_core/hosts/nuke/plugins/publish/validate_write_nodes.py index 7679022487..1c922f5d30 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/validate_write_nodes.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/validate_write_nodes.py @@ -97,7 +97,7 @@ class ValidateNukeWriteNode( raise PublishXmlValidationError( self, ( "Please update data in settings 'project_settings" - "/nuke/imageio/nodes/requiredNodes'" + "/nuke/imageio/nodes/required_nodes'" ), key="legacy" ) diff --git a/client/ayon_core/hosts/nuke/startup/custom_write_node.py b/client/ayon_core/hosts/nuke/startup/custom_write_node.py index 89dfde297c..5eb58a3679 100644 --- a/client/ayon_core/hosts/nuke/startup/custom_write_node.py +++ b/client/ayon_core/hosts/nuke/startup/custom_write_node.py @@ -127,8 +127,8 @@ class WriteNodeKnobSettingPanel(nukescripts.PythonPanel): knobs_nodes = [] settings = [ node_settings for node_settings - in get_nuke_imageio_settings()["nodes"]["overrideNodes"] - if node_settings["nukeNodeClass"] == "Write" + in get_nuke_imageio_settings()["nodes"]["override_nodes"] + if node_settings["nuke_node_class"] == "Write" and node_settings["subsets"] ] if not settings: diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index 023a2eff96..b353377d44 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -170,52 +170,6 @@ def convert_system_settings(ayon_settings, default_settings, addon_versions): # --------- Project settings --------- -def _convert_nuke_knobs(knobs): - new_knobs = [] - for knob in knobs: - knob_type = knob["type"] - - if knob_type == "boolean": - knob_type = "bool" - - if knob_type != "bool": - value = knob[knob_type] - elif knob_type in knob: - value = knob[knob_type] - else: - value = knob["boolean"] - - new_knob = { - "type": knob_type, - "name": knob["name"], - } - new_knobs.append(new_knob) - - if knob_type == "formatable": - new_knob["template"] = value["template"] - new_knob["to_type"] = value["to_type"] - continue - - value_key = "value" - if knob_type == "expression": - value_key = "expression" - - elif knob_type == "color_gui": - value = _convert_color(value) - - elif knob_type == "vector_2d": - value = [value["x"], value["y"]] - - elif knob_type == "vector_3d": - value = [value["x"], value["y"], value["z"]] - - elif knob_type == "box": - value = [value["x"], value["y"], value["r"], value["t"]] - - new_knob[value_key] = value - return new_knobs - - def _convert_nuke_project_settings(ayon_settings, output): if "nuke" not in ayon_settings: return @@ -254,17 +208,6 @@ def _convert_nuke_project_settings(ayon_settings, output): item_filter["subsets"] = item_filter.pop("product_names") item_filter["families"] = item_filter.pop("product_types") - reformat_nodes_config = item.get("reformat_nodes_config") or {} - reposition_nodes = reformat_nodes_config.get( - "reposition_nodes") or [] - - for reposition_node in reposition_nodes: - if "knobs" not in reposition_node: - continue - reposition_node["knobs"] = _convert_nuke_knobs( - reposition_node["knobs"] - ) - name = item.pop("name") new_review_data_outputs[name] = item @@ -302,25 +245,6 @@ def _convert_nuke_project_settings(ayon_settings, output): if "regex_inputs" in ayon_imageio: ayon_imageio["regexInputs"] = ayon_imageio.pop("regex_inputs") - # nodes - ayon_imageio_nodes = ayon_imageio["nodes"] - if "required_nodes" in ayon_imageio_nodes: - ayon_imageio_nodes["requiredNodes"] = ( - ayon_imageio_nodes.pop("required_nodes")) - if "override_nodes" in ayon_imageio_nodes: - ayon_imageio_nodes["overrideNodes"] = ( - ayon_imageio_nodes.pop("override_nodes")) - - for item in ayon_imageio_nodes["requiredNodes"]: - if "nuke_node_class" in item: - item["nukeNodeClass"] = item.pop("nuke_node_class") - item["knobs"] = _convert_nuke_knobs(item["knobs"]) - - for item in ayon_imageio_nodes["overrideNodes"]: - if "nuke_node_class" in item: - item["nukeNodeClass"] = item.pop("nuke_node_class") - item["knobs"] = _convert_nuke_knobs(item["knobs"]) - output["nuke"] = ayon_nuke From f1a2200b0ea9fa3c3d4f9db48297b17f9778465f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 16:12:21 +0100 Subject: [PATCH 123/573] nuke publish plugins are using AYON settings --- .../publish/collect_nuke_instance_data.py | 4 +- .../publish/extract_review_intermediates.py | 28 ++++++----- .../plugins/publish/extract_slate_frame.py | 16 +++++-- .../nuke/plugins/publish/validate_knobs.py | 10 ++-- client/ayon_core/settings/ayon_settings.py | 46 ------------------- 5 files changed, 37 insertions(+), 67 deletions(-) diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_nuke_instance_data.py b/client/ayon_core/hosts/nuke/plugins/publish/collect_nuke_instance_data.py index 449a1cc935..75380bf409 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/collect_nuke_instance_data.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/collect_nuke_instance_data.py @@ -12,7 +12,7 @@ class CollectInstanceData(pyblish.api.InstancePlugin): hosts = ["nuke", "nukeassist"] # presets - sync_workfile_version_on_families = [] + sync_workfile_version_on_product_types = [] def process(self, instance): family = instance.data["family"] @@ -25,7 +25,7 @@ class CollectInstanceData(pyblish.api.InstancePlugin): pixel_aspect = format_.pixelAspect() # sync workfile version - if family in self.sync_workfile_version_on_families: + if family in self.sync_workfile_version_on_product_types: self.log.debug( "Syncing version with workfile for '{}'".format( family diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py b/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py index a00c1c593f..1f4410d347 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py @@ -50,6 +50,7 @@ class ExtractReviewIntermediates(publish.Extractor): cls.outputs = current_setting["outputs"] def process(self, instance): + # TODO 'families' should not be included for filtering of outputs families = set(instance.data["families"]) # add main family to make sure all families are compared @@ -75,29 +76,33 @@ class ExtractReviewIntermediates(publish.Extractor): # generate data with maintained_selection(): generated_repres = [] - for o_name, o_data in self.outputs.items(): + for o_data in self.outputs: + o_name = o_data["name"] self.log.debug( "o_name: {}, o_data: {}".format(o_name, pformat(o_data))) - f_families = o_data["filter"]["families"] + f_product_types = o_data["filter"]["product_types"] f_task_types = o_data["filter"]["task_types"] - f_subsets = o_data["filter"]["subsets"] + product_names = o_data["filter"]["product_names"] self.log.debug( - "f_families `{}` > families: {}".format( - f_families, families)) + "f_product_types `{}` > families: {}".format( + f_product_types, families)) self.log.debug( "f_task_types `{}` > task_type: {}".format( f_task_types, task_type)) self.log.debug( - "f_subsets `{}` > subset: {}".format( - f_subsets, subset)) + "product_names `{}` > subset: {}".format( + product_names, subset)) # test if family found in context # using intersection to make sure all defined # families are present in combination - if f_families and not families.intersection(f_families): + if ( + f_product_types + and not families.intersection(f_product_types) + ): continue # test task types from filter @@ -105,8 +110,9 @@ class ExtractReviewIntermediates(publish.Extractor): continue # test subsets from filter - if f_subsets and not any( - re.search(s, subset) for s in f_subsets): + if product_names and not any( + re.search(p, subset) for p in product_names + ): continue self.log.debug( @@ -117,7 +123,7 @@ class ExtractReviewIntermediates(publish.Extractor): # check if settings have more then one preset # so we dont need to add outputName to representation # in case there is only one preset - multiple_presets = len(self.outputs.keys()) > 1 + multiple_presets = len(self.outputs) > 1 # adding bake presets to instance data for other plugins if not instance.data.get("bakePresets"): diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_slate_frame.py b/client/ayon_core/hosts/nuke/plugins/publish/extract_slate_frame.py index 0c4823b1aa..6918e4f50f 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/extract_slate_frame.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/extract_slate_frame.py @@ -29,9 +29,15 @@ class ExtractSlateFrame(publish.Extractor): # Settings values key_value_mapping = { - "f_submission_note": [True, "{comment}"], - "f_submitting_for": [True, "{intent[value]}"], - "f_vfx_scope_of_work": [False, ""] + "f_submission_note": { + "enabled": True, "template": "{comment}" + }, + "f_submitting_for": { + "enabled": True, "template": "{intent[value]}" + }, + "f_vfx_scope_of_work": { + "enabled": False, "template": "" + } } def process(self, instance): @@ -316,11 +322,11 @@ class ExtractSlateFrame(publish.Extractor): }) for key, _values in self.key_value_mapping.items(): - enabled, template = _values - if not enabled: + if not _values["enabled"]: self.log.debug("Key \"{}\" is disabled".format(key)) continue + template = _values["template"] try: value = template.format(**fill_data) diff --git a/client/ayon_core/hosts/nuke/plugins/publish/validate_knobs.py b/client/ayon_core/hosts/nuke/plugins/publish/validate_knobs.py index 84efebab53..bede67dabe 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/validate_knobs.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/validate_knobs.py @@ -30,6 +30,8 @@ class ValidateKnobs(pyblish.api.ContextPlugin): actions = [RepairContextAction] optional = True + knobs = "{}" + def process(self, context): invalid = self.get_invalid(context, compute=True) if invalid: @@ -61,6 +63,8 @@ class ValidateKnobs(pyblish.api.ContextPlugin): invalid_knobs = [] for instance in context: + # Load fresh knobs data for each instance + settings_knobs = json.loads(cls.knobs) # Filter families. families = [instance.data["family"]] @@ -74,12 +78,12 @@ class ValidateKnobs(pyblish.api.ContextPlugin): family = family.split(".")[0] # avoid families not in settings - if family not in cls.knobs: + if family not in settings_knobs: continue # get presets of knobs - for preset in cls.knobs[family]: - knobs[preset] = cls.knobs[family][preset] + for preset in settings_knobs[family]: + knobs[preset] = settings_knobs[family][preset] # Get invalid knobs. nodes = [] diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index b353377d44..6230f27fe9 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -176,52 +176,6 @@ def _convert_nuke_project_settings(ayon_settings, output): ayon_nuke = ayon_settings["nuke"] - # --- Publish --- - ayon_publish = ayon_nuke["publish"] - slate_mapping = ayon_publish["ExtractSlateFrame"]["key_value_mapping"] - for key in tuple(slate_mapping.keys()): - value = slate_mapping[key] - slate_mapping[key] = [value["enabled"], value["template"]] - - ayon_publish["ValidateKnobs"]["knobs"] = json.loads( - ayon_publish["ValidateKnobs"]["knobs"] - ) - - new_review_data_outputs = {} - outputs_settings = [] - # Check deprecated ExtractReviewDataMov - # settings for backwards compatibility - deprecrated_review_settings = ayon_publish["ExtractReviewDataMov"] - current_review_settings = ( - ayon_publish.get("ExtractReviewIntermediates") - ) - if deprecrated_review_settings["enabled"]: - outputs_settings = deprecrated_review_settings["outputs"] - elif current_review_settings is None: - pass - elif current_review_settings["enabled"]: - outputs_settings = current_review_settings["outputs"] - - for item in outputs_settings: - item_filter = item["filter"] - if "product_names" in item_filter: - item_filter["subsets"] = item_filter.pop("product_names") - item_filter["families"] = item_filter.pop("product_types") - - name = item.pop("name") - new_review_data_outputs[name] = item - - if deprecrated_review_settings["enabled"]: - deprecrated_review_settings["outputs"] = new_review_data_outputs - elif current_review_settings["enabled"]: - current_review_settings["outputs"] = new_review_data_outputs - - collect_instance_data = ayon_publish["CollectInstanceData"] - if "sync_workfile_version_on_product_types" in collect_instance_data: - collect_instance_data["sync_workfile_version_on_families"] = ( - collect_instance_data.pop( - "sync_workfile_version_on_product_types")) - # --- ImageIO --- # NOTE 'monitorOutLut' is maybe not yet in v3 (ut should be) ayon_imageio = ayon_nuke["imageio"] From 5064b6f47d5d06db9a8b9085ecde565aa4c13076 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 16:31:24 +0100 Subject: [PATCH 124/573] use AYON workfile imageio --- client/ayon_core/hosts/nuke/api/lib.py | 61 +++++++--------------- client/ayon_core/settings/ayon_settings.py | 15 ------ 2 files changed, 18 insertions(+), 58 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index e645055fc7..ea831e7169 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -2031,41 +2031,21 @@ class WorkfileSettings(object): host_name="nuke" ) - workfile_settings = imageio_host["workfile"] viewer_process_settings = imageio_host["viewer"]["viewerProcess"] + workfile_settings = imageio_host["workfile"] + color_management = workfile_settings["color_management"] + native_ocio_config = workfile_settings["native_ocio_config"] if not config_data: - # TODO: backward compatibility for old projects - remove later - # perhaps old project overrides is having it set to older version - # with use of `customOCIOConfigPath` - resolved_path = None - if workfile_settings.get("customOCIOConfigPath"): - unresolved_path = workfile_settings["customOCIOConfigPath"] - ocio_paths = unresolved_path[platform.system().lower()] + # no ocio config found and no custom path used + if self._root_node["colorManagement"].value() \ + not in color_management: + self._root_node["colorManagement"].setValue(color_management) - for ocio_p in ocio_paths: - resolved_path = str(ocio_p).format(**os.environ) - if not os.path.exists(resolved_path): - continue - - if resolved_path: - # set values to root - self._root_node["colorManagement"].setValue("OCIO") - self._root_node["OCIO_config"].setValue("custom") - self._root_node["customOCIOConfigPath"].setValue( - resolved_path) - else: - # no ocio config found and no custom path used - if self._root_node["colorManagement"].value() \ - not in str(workfile_settings["colorManagement"]): - self._root_node["colorManagement"].setValue( - str(workfile_settings["colorManagement"])) - - # second set ocio version - if self._root_node["OCIO_config"].value() \ - not in str(workfile_settings["OCIO_config"]): - self._root_node["OCIO_config"].setValue( - str(workfile_settings["OCIO_config"])) + # second set ocio version + if self._root_node["OCIO_config"].value() \ + not in native_ocio_config: + self._root_node["OCIO_config"].setValue(native_ocio_config) else: # OCIO config path is defined from prelaunch hook @@ -2078,22 +2058,17 @@ class WorkfileSettings(object): residual_path )) - # we dont need the key anymore - workfile_settings.pop("customOCIOConfigPath", None) - workfile_settings.pop("colorManagement", None) - workfile_settings.pop("OCIO_config", None) - # get monitor lut from settings respecting Nuke version differences - monitor_lut = workfile_settings.pop("monitorLut", None) + monitor_lut = workfile_settings["thumbnail_space"] monitor_lut_data = self._get_monitor_settings( - viewer_process_settings, monitor_lut) - - # set monitor related knobs luts (MonitorOut, Thumbnails) - for knob, value_ in monitor_lut_data.items(): - workfile_settings[knob] = value_ + viewer_process_settings, monitor_lut + ) + monitor_lut_data["workingSpaceLUT"] = ( + workfile_settings["working_space"] + ) # then set the rest - for knob, value_ in workfile_settings.items(): + for knob, value_ in monitor_lut_data.items(): # skip unfilled ocio config path # it will be dict in value if isinstance(value_, dict): diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index 6230f27fe9..9dfb8845d3 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -180,21 +180,6 @@ def _convert_nuke_project_settings(ayon_settings, output): # NOTE 'monitorOutLut' is maybe not yet in v3 (ut should be) ayon_imageio = ayon_nuke["imageio"] - # workfile - imageio_workfile = ayon_imageio["workfile"] - workfile_keys_mapping = ( - ("color_management", "colorManagement"), - ("native_ocio_config", "OCIO_config"), - ("working_space", "workingSpaceLUT"), - ("thumbnail_space", "monitorLut"), - ) - for src, dst in workfile_keys_mapping: - if ( - src in imageio_workfile - and dst not in imageio_workfile - ): - imageio_workfile[dst] = imageio_workfile.pop(src) - # regex inputs if "regex_inputs" in ayon_imageio: ayon_imageio["regexInputs"] = ayon_imageio.pop("regex_inputs") From 6f6737b41dba73333b0dc7b5662fb9fccb5ddbb1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 16:32:12 +0100 Subject: [PATCH 125/573] removed remaining nuke project settings conversions --- client/ayon_core/hosts/nuke/api/lib.py | 4 ++-- .../hosts/nuke/plugins/load/load_clip.py | 2 +- client/ayon_core/settings/ayon_settings.py | 17 ----------------- 3 files changed, 3 insertions(+), 20 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index ea831e7169..adfe753987 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -900,7 +900,7 @@ def get_imageio_input_colorspace(filename): ''' Get input file colorspace based on regex in settings. ''' imageio_regex_inputs = ( - get_nuke_imageio_settings()["regexInputs"]["inputs"]) + get_nuke_imageio_settings()["regex_inputs"]["inputs"]) preset_clrsp = None for regexInput in imageio_regex_inputs: @@ -2442,7 +2442,7 @@ Reopening Nuke should synchronize these paths and resolve any discrepancies. log.error(_error) log.info("Setting colorspace to read nodes...") - read_clrs_inputs = nuke_colorspace["regexInputs"].get("inputs", []) + read_clrs_inputs = nuke_colorspace["regex_inputs"].get("inputs", []) if read_clrs_inputs: self.set_reads_colorspace(read_clrs_inputs) diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py index d2a8f2c93b..31b523fbc8 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py @@ -457,7 +457,7 @@ class LoadClip(plugin.NukeLoader): colorspace = repre_data.get("colorspace") colorspace = colorspace or version_data.get("colorspace") - # colorspace from `project_settings/nuke/imageio/regexInputs` + # colorspace from `project_settings/nuke/imageio/regex_inputs` iio_colorspace = get_imageio_input_colorspace(path) # Set colorspace defined in version data diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index 9dfb8845d3..a91719082e 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -170,23 +170,6 @@ def convert_system_settings(ayon_settings, default_settings, addon_versions): # --------- Project settings --------- -def _convert_nuke_project_settings(ayon_settings, output): - if "nuke" not in ayon_settings: - return - - ayon_nuke = ayon_settings["nuke"] - - # --- ImageIO --- - # NOTE 'monitorOutLut' is maybe not yet in v3 (ut should be) - ayon_imageio = ayon_nuke["imageio"] - - # regex inputs - if "regex_inputs" in ayon_imageio: - ayon_imageio["regexInputs"] = ayon_imageio.pop("regex_inputs") - - output["nuke"] = ayon_nuke - - def _convert_hiero_project_settings(ayon_settings, output): if "hiero" not in ayon_settings: return From 32094aa148629f98799992302cf06de43f4c2d91 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 17:15:28 +0100 Subject: [PATCH 126/573] fix missing import in maya addon --- server_addon/maya/server/settings/loaders.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/maya/server/settings/loaders.py b/server_addon/maya/server/settings/loaders.py index 3e8d368a6b..418a7046ae 100644 --- a/server_addon/maya/server/settings/loaders.py +++ b/server_addon/maya/server/settings/loaders.py @@ -1,5 +1,5 @@ from ayon_server.settings import BaseSettingsModel, SettingsField -from ayon_server.types import ColorRGB_float +from ayon_server.types import ColorRGB_float, ColorRGBA_uint8 class ColorsSetting(BaseSettingsModel): From 33d6f54c2d0d169f0820a8340673622c5e052258 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 17:24:55 +0100 Subject: [PATCH 127/573] ExtractBurnin are using AYON settings --- .../plugins/publish/extract_burnin.py | 89 +++++++++++-------- client/ayon_core/settings/ayon_settings.py | 40 --------- 2 files changed, 51 insertions(+), 78 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_burnin.py b/client/ayon_core/plugins/publish/extract_burnin.py index 2b76527d5f..4da158343e 100644 --- a/client/ayon_core/plugins/publish/extract_burnin.py +++ b/client/ayon_core/plugins/publish/extract_burnin.py @@ -65,8 +65,8 @@ class ExtractBurnin(publish.Extractor): # Default options for burnins for cases that are not set in presets. default_options = { "font_size": 42, - "font_color": [255, 255, 255, 255], - "bg_color": [0, 0, 0, 127], + "font_color": [255, 255, 255, 1.0], + "bg_color": [0, 0, 0, 0.5], "bg_padding": 5, "x_offset": 5, "y_offset": 5 @@ -96,7 +96,9 @@ class ExtractBurnin(publish.Extractor): instance.data["representations"].remove(repre) def _get_burnins_per_representations(self, instance, src_burnin_defs): - self.log.debug("Filtering of representations and their burnins starts") + self.log.debug( + "Filtering of representations and their burnins starts" + ) filtered_repres = [] repres = instance.data.get("representations") or [] @@ -116,11 +118,11 @@ class ExtractBurnin(publish.Extractor): ) # Filter output definition by `burnin` represetation key - repre_linked_burnins = { - name: output - for name, output in burnin_defs.items() - if name in repre_burnin_links - } + repre_linked_burnins = [ + burnin_def + for burnin_def in burnin_defs.items() + if burnin_def["name"] in repre_burnin_links + ] self.log.debug( "repre_linked_burnins: {}".format(repre_linked_burnins) ) @@ -154,19 +156,21 @@ class ExtractBurnin(publish.Extractor): filtering_criteria = { "hosts": host_name, - "families": family, + "product_types": family, + "product_names": subset, "task_names": task_name, "task_types": task_type, - "subset": subset } - profile = filter_profiles(self.profiles, filtering_criteria, - logger=self.log) - + profile = filter_profiles( + self.profiles, + filtering_criteria, + logger=self.log + ) if not profile: self.log.debug(( "Skipped instance. None of profiles in presets are for" - " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" - " | Task type \"{}\" | Subset \"{}\" " + " Host: \"{}\" | Product type: \"{}\" | Task name \"{}\"" + " | Task type \"{}\" | Product name \"{}\" " ).format(host_name, family, task_name, task_type, subset)) return @@ -175,7 +179,7 @@ class ExtractBurnin(publish.Extractor): if not burnin_defs: self.log.debug(( "Skipped instance. Burnin definitions are not set for profile" - " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" + " Host: \"{}\" | Product type: \"{}\" | Task name \"{}\"" " | Profile \"{}\"" ).format(host_name, family, task_name, profile)) return @@ -275,7 +279,8 @@ class ExtractBurnin(publish.Extractor): # it in review? # burnin_data["fps"] = fps - for filename_suffix, burnin_def in repre_burnin_defs.items(): + for burnin_def in repre_burnin_defs: + filename_suffix = burnin_def["name"] new_repre = copy.deepcopy(repre) new_repre["stagingDir"] = src_repre_staging_dir @@ -288,16 +293,28 @@ class ExtractBurnin(publish.Extractor): burnin_values = {} for key in self.positions: value = burnin_def.get(key) - if value: - burnin_values[key] = value.replace( - "{task}", "{task[name]}" - ) + if not value: + continue + # TODO remove replacements + burnin_values[key] = ( + value + .replace("{task}", "{task[name]}") + .replace("{product[name]}", "{subset}") + .replace("{Product[name]}", "{Subset}") + .replace("{PRODUCT[NAME]}", "{SUBSET}") + .replace("{product[type]}", "{family}") + .replace("{Product[type]}", "{Family}") + .replace("{PRODUCT[TYPE]}", "{FAMILY}") + .replace("{folder[name]}", "{asset}") + .replace("{Folder[name]}", "{Asset}") + .replace("{FOLDER[NAME]}", "{ASSET}") + ) # Remove "delete" tag from new representation if "delete" in new_repre["tags"]: new_repre["tags"].remove("delete") - if len(repre_burnin_defs.keys()) > 1: + if len(repre_burnin_defs) > 1: # Update name and outputName to be # able have multiple outputs in case of more burnin presets # Join previous "outputName" with filename suffix @@ -401,8 +418,7 @@ class ExtractBurnin(publish.Extractor): bg_color_hex = "#{0:0>2X}{1:0>2X}{2:0>2X}".format( bg_red, bg_green, bg_blue ) - bg_color_alpha = float(bg_alpha) / 255 - burnin_options["bg_opacity"] = bg_color_alpha + burnin_options["bg_opacity"] = bg_alpha burnin_options["bg_color"] = bg_color_hex # FG Color @@ -412,8 +428,7 @@ class ExtractBurnin(publish.Extractor): fg_color_hex = "#{0:0>2X}{1:0>2X}{2:0>2X}".format( fg_red, fg_green, fg_blue ) - fg_color_alpha = float(fg_alpha) / 255 - burnin_options["opacity"] = fg_color_alpha + burnin_options["opacity"] = fg_alpha burnin_options["font_color"] = fg_color_hex # Define font filepath @@ -543,15 +558,16 @@ class ExtractBurnin(publish.Extractor): Burnin definitions without tags filter are marked as valid. Args: - outputs (list): Contain list of burnin definitions from presets. + burnin_defs (list): Burnin definitions. tags (list): Tags of processed representation. Returns: list: Containg all burnin definitions matching entered tags. + """ - filtered_burnins = {} + filtered_burnins = [] repre_tags_low = set(tag.lower() for tag in tags) - for filename_suffix, burnin_def in burnin_defs.items(): + for burnin_def in burnin_defs: valid = True tag_filters = burnin_def["filter"]["tags"] if tag_filters: @@ -561,8 +577,7 @@ class ExtractBurnin(publish.Extractor): valid = bool(repre_tags_low & tag_filters_low) if valid: - filtered_burnins[filename_suffix] = burnin_def - + filtered_burnins.append(burnin_def) return filtered_burnins def input_output_paths( @@ -724,7 +739,7 @@ class ExtractBurnin(publish.Extractor): Returns: list: Containg all valid output definitions. """ - filtered_burnin_defs = {} + filtered_burnin_defs = [] burnin_defs = profile.get("burnins") if not burnin_defs: @@ -732,13 +747,11 @@ class ExtractBurnin(publish.Extractor): families = self.families_from_instance(instance) - for filename_suffix, orig_burnin_def in burnin_defs.items(): + for orig_burnin_def in burnin_defs: burnin_def = copy.deepcopy(orig_burnin_def) - def_filter = burnin_def.get("filter", None) or {} - for key in ("families", "tags"): - if key not in def_filter: - def_filter[key] = [] + filename_suffix = burnin_def["name"] + def_filter = burnin_def["filter"] families_filters = def_filter["families"] if not self.families_filter_validation( families, families_filters @@ -769,7 +782,7 @@ class ExtractBurnin(publish.Extractor): burnin_values["filter"] = def_filter - filtered_burnin_defs[filename_suffix] = burnin_values + filtered_burnin_defs.append(burnin_values) self.log.debug(( "Burnin definition \"{}\" passed first filtering." diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index 190970b908..f74e6129db 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -449,46 +449,6 @@ def _convert_global_project_settings(ayon_settings, output, default_settings): new_outputs[name] = profile_output profile["outputs"] = new_outputs - # Extract Burnin plugin - extract_burnin = ayon_publish["ExtractBurnin"] - extract_burnin_options = extract_burnin["options"] - for color_key in ("font_color", "bg_color"): - extract_burnin_options[color_key] = _convert_color( - extract_burnin_options[color_key] - ) - - for profile in extract_burnin["profiles"]: - extract_burnin_defs = profile["burnins"] - if "product_names" in profile: - profile["subsets"] = profile.pop("product_names") - profile["families"] = profile.pop("product_types") - - for burnin_def in extract_burnin_defs: - for key in ( - "TOP_LEFT", - "TOP_CENTERED", - "TOP_RIGHT", - "BOTTOM_LEFT", - "BOTTOM_CENTERED", - "BOTTOM_RIGHT", - ): - burnin_def[key] = ( - burnin_def[key] - .replace("{product[name]}", "{subset}") - .replace("{Product[name]}", "{Subset}") - .replace("{PRODUCT[NAME]}", "{SUBSET}") - .replace("{product[type]}", "{family}") - .replace("{Product[type]}", "{Family}") - .replace("{PRODUCT[TYPE]}", "{FAMILY}") - .replace("{folder[name]}", "{asset}") - .replace("{Folder[name]}", "{Asset}") - .replace("{FOLDER[NAME]}", "{ASSET}") - ) - profile["burnins"] = { - extract_burnin_def.pop("name"): extract_burnin_def - for extract_burnin_def in extract_burnin_defs - } - if "IntegrateProductGroup" in ayon_publish: subset_group = ayon_publish.pop("IntegrateProductGroup") subset_group_profiles = subset_group.pop("product_grouping_profiles") From 7669e8ff2b3b17fef8498afc47925ff5a33893d4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 17:30:37 +0100 Subject: [PATCH 128/573] 'ExtractOIIOTranscode is using AYON settings --- .../publish/extract_color_transcode.py | 12 ++++++---- client/ayon_core/settings/ayon_settings.py | 23 ------------------- 2 files changed, 7 insertions(+), 28 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_color_transcode.py b/client/ayon_core/plugins/publish/extract_color_transcode.py index 66ba8ad2be..77c4673bca 100644 --- a/client/ayon_core/plugins/publish/extract_color_transcode.py +++ b/client/ayon_core/plugins/publish/extract_color_transcode.py @@ -81,6 +81,7 @@ class ExtractOIIOTranscode(publish.Extractor): if not profile: return + profile_output_defs = profile["outputs"] new_representations = [] repres = instance.data["representations"] for idx, repre in enumerate(list(repres)): @@ -98,7 +99,8 @@ class ExtractOIIOTranscode(publish.Extractor): self.log.warning("Config file doesn't exist, skipping") continue - for output_name, output_def in profile.get("outputs", {}).items(): + for output_def in profile_output_defs: + output_name = output_def["name"] new_repre = copy.deepcopy(repre) original_staging_dir = new_repre["stagingDir"] @@ -318,10 +320,10 @@ class ExtractOIIOTranscode(publish.Extractor): subset = instance.data["subset"] filtering_criteria = { "hosts": host_name, - "families": family, + "product_types": family, + "product_names": subset, "task_names": task_name, "task_types": task_type, - "subsets": subset } profile = filter_profiles(self.profiles, filtering_criteria, logger=self.log) @@ -329,8 +331,8 @@ class ExtractOIIOTranscode(publish.Extractor): if not profile: self.log.debug(( "Skipped instance. None of profiles in presets are for" - " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" - " | Task type \"{}\" | Subset \"{}\" " + " Host: \"{}\" | Product types: \"{}\" | Task \"{}\"" + " | Task type \"{}\" | Product names: \"{}\" " ).format(host_name, family, task_name, task_type, subset)) return profile diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index f74e6129db..81a0fde844 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -426,29 +426,6 @@ def _convert_global_project_settings(ayon_settings, output, default_settings): ayon_extract_thumbnail["background_color"] ) - # ExtractOIIOTranscode plugin - extract_oiio_transcode = ayon_publish["ExtractOIIOTranscode"] - extract_oiio_transcode_profiles = extract_oiio_transcode["profiles"] - for profile in extract_oiio_transcode_profiles: - new_outputs = {} - name_counter = {} - if "product_names" in profile: - profile["subsets"] = profile.pop("product_names") - for profile_output in profile["outputs"]: - if "name" in profile_output: - name = profile_output.pop("name") - else: - # Backwards compatibility for setting without 'name' in model - name = profile_output["extension"] - if name in new_outputs: - name_counter[name] += 1 - name = "{}_{}".format(name, name_counter[name]) - else: - name_counter[name] = 0 - - new_outputs[name] = profile_output - profile["outputs"] = new_outputs - if "IntegrateProductGroup" in ayon_publish: subset_group = ayon_publish.pop("IntegrateProductGroup") subset_group_profiles = subset_group.pop("product_grouping_profiles") From 266eb53769e7fbc5ac54661652e3676777f80a86 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 17:32:45 +0100 Subject: [PATCH 129/573] IntegrateProductGroup is using AYON settings --- ...ubset_group.py => integrate_product_group.py} | 16 ++++++++-------- client/ayon_core/settings/ayon_settings.py | 8 -------- 2 files changed, 8 insertions(+), 16 deletions(-) rename client/ayon_core/plugins/publish/{integrate_subset_group.py => integrate_product_group.py} (87%) diff --git a/client/ayon_core/plugins/publish/integrate_subset_group.py b/client/ayon_core/plugins/publish/integrate_product_group.py similarity index 87% rename from client/ayon_core/plugins/publish/integrate_subset_group.py rename to client/ayon_core/plugins/publish/integrate_product_group.py index c2f1eac9e3..f7c96893ca 100644 --- a/client/ayon_core/plugins/publish/integrate_subset_group.py +++ b/client/ayon_core/plugins/publish/integrate_product_group.py @@ -17,24 +17,24 @@ from ayon_core.lib import ( ) -class IntegrateSubsetGroup(pyblish.api.InstancePlugin): +class IntegrateProductGroup(pyblish.api.InstancePlugin): """Integrate Subset Group for publish.""" # Run after CollectAnatomyInstanceData order = pyblish.api.IntegratorOrder - 0.1 - label = "Subset Group" + label = "Product Group" # Attributes set by settings - subset_grouping_profiles = None + product_grouping_profiles = None def process(self, instance): """Look into subset group profiles set by settings. - Attribute 'subset_grouping_profiles' is defined by settings. + Attribute 'product_grouping_profiles' is defined by settings. """ - # Skip if 'subset_grouping_profiles' is empty - if not self.subset_grouping_profiles: + # Skip if 'product_grouping_profiles' is empty + if not self.product_grouping_profiles: return if instance.data.get("subsetGroup"): @@ -47,7 +47,7 @@ class IntegrateSubsetGroup(pyblish.api.InstancePlugin): # Skip if there is no matching profile filter_criteria = self.get_profile_filter_criteria(instance) profile = filter_profiles( - self.subset_grouping_profiles, + self.product_grouping_profiles, filter_criteria, logger=self.log ) @@ -91,7 +91,7 @@ class IntegrateSubsetGroup(pyblish.api.InstancePlugin): # Return filter criteria return { - "families": anatomy_data["family"], + "product_types": anatomy_data["family"], "tasks": task.get("name"), "hosts": instance.context.data["hostName"], "task_types": task.get("type") diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index 81a0fde844..9c8deecab0 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -426,14 +426,6 @@ def _convert_global_project_settings(ayon_settings, output, default_settings): ayon_extract_thumbnail["background_color"] ) - if "IntegrateProductGroup" in ayon_publish: - subset_group = ayon_publish.pop("IntegrateProductGroup") - subset_group_profiles = subset_group.pop("product_grouping_profiles") - for profile in subset_group_profiles: - profile["families"] = profile.pop("product_types") - subset_group["subset_grouping_profiles"] = subset_group_profiles - ayon_publish["IntegrateSubsetGroup"] = subset_group - # Cleanup plugin ayon_cleanup = ayon_publish["CleanUp"] if "patterns" in ayon_cleanup: From 508997c7f472d7560ed74a08e8a469738eb3daed Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 17:38:32 +0100 Subject: [PATCH 130/573] get_subset_name is using AYON settings --- .../plugins/create/create_render.py | 6 ++--- .../photoshop/plugins/create/create_image.py | 4 ++-- .../ayon_core/pipeline/create/subset_name.py | 24 ++++++++++++++----- client/ayon_core/settings/ayon_settings.py | 19 --------------- 4 files changed, 23 insertions(+), 30 deletions(-) diff --git a/client/ayon_core/hosts/aftereffects/plugins/create/create_render.py b/client/ayon_core/hosts/aftereffects/plugins/create/create_render.py index 78aa49a562..bd005f5d4f 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/create/create_render.py +++ b/client/ayon_core/hosts/aftereffects/plugins/create/create_render.py @@ -194,13 +194,13 @@ class RenderCreator(Creator): name into created subset name. Position of composition name could be set in - `project_settings/global/tools/creator/subset_name_profiles` with some - form of '{composition}' placeholder. + `project_settings/global/tools/creator/product_name_profiles` with + some form of '{composition}' placeholder. Composition name will be used implicitly if multiple composition should be handled at same time. - If {composition} placeholder is not us 'subset_name_profiles' + If {composition} placeholder is not us 'product_name_profiles' composition name will be capitalized and set at the end of subset name if necessary. diff --git a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py b/client/ayon_core/hosts/photoshop/plugins/create/create_image.py index a28872bba1..2ef746fc94 100644 --- a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/create/create_image.py @@ -209,8 +209,8 @@ class ImageCreator(Creator): 'Use layer name in subset' will explicitly add layer name into subset name. Position of this name is configurable in - `project_settings/global/tools/creator/subset_name_profiles`. - If layer placeholder ({layer}) is not used in `subset_name_profiles` + `project_settings/global/tools/creator/product_name_profiles`. + If layer placeholder ({layer}) is not used in `product_name_profiles` but layer name should be used (set explicitly in UI or implicitly if multiple images should be created), it is added in capitalized form as a suffix to subset name. diff --git a/client/ayon_core/pipeline/create/subset_name.py b/client/ayon_core/pipeline/create/subset_name.py index 93a61b8b8b..81967ffd99 100644 --- a/client/ayon_core/pipeline/create/subset_name.py +++ b/client/ayon_core/pipeline/create/subset_name.py @@ -48,9 +48,9 @@ def get_subset_name_template( if project_settings is None: project_settings = get_project_settings(project_name) tools_settings = project_settings["global"]["tools"] - profiles = tools_settings["creator"]["subset_name_profiles"] + profiles = tools_settings["creator"]["product_name_profiles"] filtering_criteria = { - "families": family, + "product_types": family, "hosts": host_name, "tasks": task_name, "task_types": task_type @@ -59,7 +59,19 @@ def get_subset_name_template( matching_profile = filter_profiles(profiles, filtering_criteria) template = None if matching_profile: - template = matching_profile["template"] + # TODO remove formatting keys replacement + template = ( + matching_profile["template"] + .replace("{task[name]}", "{task}") + .replace("{Task[name]}", "{Task}") + .replace("{TASK[NAME]}", "{TASK}") + .replace("{product[type]}", "{family}") + .replace("{Product[type]}", "{Family}") + .replace("{PRODUCT[TYPE]}", "{FAMILY}") + .replace("{folder[name]}", "{asset}") + .replace("{Folder[name]}", "{Asset}") + .replace("{FOLDER[NAME]}", "{ASSET}") + ) # Make sure template is set (matching may have empty string) if not template: @@ -82,9 +94,9 @@ def get_subset_name( """Calculate subset name based on passed context and OpenPype settings. Subst name templates are defined in `project_settings/global/tools/creator - /subset_name_profiles` where are profiles with host name, family, task name - and task type filters. If context does not match any profile then - `DEFAULT_SUBSET_TEMPLATE` is used as default template. + /product_name_profiles` where are profiles with host name, family, + task name and task type filters. If context does not match any profile + then `DEFAULT_SUBSET_TEMPLATE` is used as default template. That's main reason why so many arguments are required to calculate subset name. diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index 9c8deecab0..43bf8ca471 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -442,26 +442,7 @@ def _convert_global_project_settings(ayon_settings, output, default_settings): # Tools settings ayon_tools = ayon_core["tools"] ayon_create_tool = ayon_tools["creator"] - if "product_name_profiles" in ayon_create_tool: - product_name_profiles = ayon_create_tool.pop("product_name_profiles") - for profile in product_name_profiles: - profile["families"] = profile.pop("product_types") - ayon_create_tool["subset_name_profiles"] = product_name_profiles - for profile in ayon_create_tool["subset_name_profiles"]: - template = profile["template"] - profile["template"] = ( - template - .replace("{task[name]}", "{task}") - .replace("{Task[name]}", "{Task}") - .replace("{TASK[NAME]}", "{TASK}") - .replace("{product[type]}", "{family}") - .replace("{Product[type]}", "{Family}") - .replace("{PRODUCT[TYPE]}", "{FAMILY}") - .replace("{folder[name]}", "{asset}") - .replace("{Folder[name]}", "{Asset}") - .replace("{FOLDER[NAME]}", "{ASSET}") - ) product_smart_select_key = "families_smart_select" if "product_types_smart_select" in ayon_create_tool: From 7beef914f768010e8f00e1e94ccede97add31d1f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 17:39:00 +0100 Subject: [PATCH 131/573] smart family select is using AYON settings --- client/ayon_core/settings/ayon_settings.py | 8 -------- client/ayon_core/tools/creator/window.py | 12 +++++++----- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index 43bf8ca471..8943debba5 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -444,15 +444,7 @@ def _convert_global_project_settings(ayon_settings, output, default_settings): ayon_create_tool = ayon_tools["creator"] - product_smart_select_key = "families_smart_select" - if "product_types_smart_select" in ayon_create_tool: - product_smart_select_key = "product_types_smart_select" - new_smart_select_families = { - item["name"]: item["task_names"] - for item in ayon_create_tool.pop(product_smart_select_key) - } - ayon_create_tool["families_smart_select"] = new_smart_select_families ayon_loader_tool = ayon_tools["loader"] if "product_type_filter_profiles" in ayon_loader_tool: diff --git a/client/ayon_core/tools/creator/window.py b/client/ayon_core/tools/creator/window.py index 676e1c3959..5862725076 100644 --- a/client/ayon_core/tools/creator/window.py +++ b/client/ayon_core/tools/creator/window.py @@ -377,23 +377,25 @@ class CreatorWindow(QtWidgets.QDialog): self._creators_model.reset() - pype_project_setting = ( + product_types_smart_select = ( get_current_project_settings() ["global"] ["tools"] ["creator"] - ["families_smart_select"] + ["product_types_smart_select"] ) current_index = None family = None task_name = get_current_task_name() or None lowered_task_name = task_name.lower() if task_name: - for _family, _task_names in pype_project_setting.items(): - _low_task_names = {name.lower() for name in _task_names} + for smart_item in product_types_smart_select: + _low_task_names = { + name.lower() for name in smart_item["task_names"] + } for _task_name in _low_task_names: if _task_name in lowered_task_name: - family = _family + family = smart_item["name"] break if family: break From eecabc7c89becb1f9c6f4ac6b0199aa0116373eb Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 17:42:33 +0100 Subject: [PATCH 132/573] template name profiles are using AYON settings --- client/ayon_core/pipeline/publish/lib.py | 2 +- client/ayon_core/settings/ayon_settings.py | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index a62c2d9c5b..a14651551f 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -138,7 +138,7 @@ def get_publish_template_name( template = None filter_criteria = { "hosts": host_name, - "families": family, + "product_types": family, "task_names": task_name, "task_types": task_type, } diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index 8943debba5..1c173e438f 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -456,15 +456,6 @@ def _convert_global_project_settings(ayon_settings, output, default_settings): ayon_loader_tool["family_filter_profiles"] = ( product_type_filter_profiles) - ayon_publish_tool = ayon_tools["publish"] - for profile in ayon_publish_tool["hero_template_name_profiles"]: - if "product_types" in profile: - profile["families"] = profile.pop("product_types") - - for profile in ayon_publish_tool["template_name_profiles"]: - if "product_types" in profile: - profile["families"] = profile.pop("product_types") - ayon_core["sync_server"] = ( default_settings["global"]["sync_server"] ) From 698e60f2d57ddbbc6fc528d11931d6e5bdc281b3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 17:42:45 +0100 Subject: [PATCH 133/573] removed conversion of unused settings --- client/ayon_core/settings/ayon_settings.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index 1c173e438f..3362fd2c3e 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -440,22 +440,6 @@ def _convert_global_project_settings(ayon_settings, output, default_settings): )) # Tools settings - ayon_tools = ayon_core["tools"] - ayon_create_tool = ayon_tools["creator"] - - - - - ayon_loader_tool = ayon_tools["loader"] - if "product_type_filter_profiles" in ayon_loader_tool: - product_type_filter_profiles = ( - ayon_loader_tool.pop("product_type_filter_profiles")) - for profile in product_type_filter_profiles: - profile["filter_families"] = profile.pop("filter_product_types") - - ayon_loader_tool["family_filter_profiles"] = ( - product_type_filter_profiles) - ayon_core["sync_server"] = ( default_settings["global"]["sync_server"] ) From b390a7f3b5da5658d418d7642d3db7c6e8894146 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 17:44:11 +0100 Subject: [PATCH 134/573] CleanUp plugin is using AYON settings --- client/ayon_core/plugins/publish/cleanup.py | 20 ++++++++++---------- client/ayon_core/settings/ayon_settings.py | 5 ----- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/client/ayon_core/plugins/publish/cleanup.py b/client/ayon_core/plugins/publish/cleanup.py index df68af7e57..db73d28e7a 100644 --- a/client/ayon_core/plugins/publish/cleanup.py +++ b/client/ayon_core/plugins/publish/cleanup.py @@ -40,7 +40,7 @@ class CleanUp(pyblish.api.InstancePlugin): active = True # Presets - paterns = None # list of regex paterns + patterns = None # list of regex patterns remove_temp_renders = True def process(self, instance): @@ -115,10 +115,10 @@ class CleanUp(pyblish.api.InstancePlugin): src = os.path.normpath(src) dest = os.path.normpath(dest) - # add src dir into clearing dir paths (regex paterns) + # add src dir into clearing dir paths (regex patterns) transfers_dirs.append(os.path.dirname(src)) - # add dest dir into clearing dir paths (regex paterns) + # add dest dir into clearing dir paths (regex patterns) transfers_dirs.append(os.path.dirname(dest)) if src in skip_cleanup_filepaths: @@ -141,13 +141,13 @@ class CleanUp(pyblish.api.InstancePlugin): # add dir for cleanup dirnames.append(os.path.dirname(src)) - # clean by regex paterns + # clean by regex patterns # make unique set transfers_dirs = set(transfers_dirs) self.log.debug("__ transfers_dirs: `{}`".format(transfers_dirs)) - self.log.debug("__ self.paterns: `{}`".format(self.paterns)) - if self.paterns: + self.log.debug("__ self.patterns: `{}`".format(self.patterns)) + if self.patterns: files = list() # get list of all available content of dirs for _dir in transfers_dirs: @@ -159,14 +159,14 @@ class CleanUp(pyblish.api.InstancePlugin): self.log.debug("__ files: `{}`".format(files)) - # remove all files which match regex patern + # remove all files which match regex pattern for f in files: if os.path.normpath(f) in skip_cleanup_filepaths: continue - for p in self.paterns: - patern = re.compile(p) - if not patern.findall(f): + for p in self.patterns: + pattern = re.compile(p) + if not pattern.findall(f): continue if not os.path.exists(f): continue diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index 3362fd2c3e..5c4813f3aa 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -426,11 +426,6 @@ def _convert_global_project_settings(ayon_settings, output, default_settings): ayon_extract_thumbnail["background_color"] ) - # Cleanup plugin - ayon_cleanup = ayon_publish["CleanUp"] - if "patterns" in ayon_cleanup: - ayon_cleanup["paterns"] = ayon_cleanup.pop("patterns") - # Project root settings - json string to dict ayon_core["project_environments"] = json.loads( ayon_core["project_environments"] From 6566900f805f19825b000023c395b358625e55ab Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 18:09:17 +0100 Subject: [PATCH 135/573] fix addon name in package.py for royal render --- server_addon/create_ayon_addons.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server_addon/create_ayon_addons.py b/server_addon/create_ayon_addons.py index 5e06d8ee8a..9553980f5d 100644 --- a/server_addon/create_ayon_addons.py +++ b/server_addon/create_ayon_addons.py @@ -191,8 +191,11 @@ def create_addon_package( # Copy server content package_py = addon_output_dir / "package.py" + addon_name = addon_dir.name + if addon_name == "royal_render": + addon_name = "royalrender" package_py_content = PACKAGE_PY_TEMPLATE.format( - addon_name=addon_dir.name, addon_version=addon_version + addon_name=addon_name, addon_version=addon_version ) with open(package_py, "w+") as pkg_py: From 6499962cb42e868cb92d39cbe8862cff0c60cb60 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 18:21:50 +0100 Subject: [PATCH 136/573] implemented simple folders widget as possible replacement of AssetsWidget --- .../tools/ayon_utils/widgets/__init__.py | 2 + .../ayon_utils/widgets/folders_widget.py | 112 ++++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/client/ayon_core/tools/ayon_utils/widgets/__init__.py b/client/ayon_core/tools/ayon_utils/widgets/__init__.py index f58de17c4a..b1b7dd7527 100644 --- a/client/ayon_core/tools/ayon_utils/widgets/__init__.py +++ b/client/ayon_core/tools/ayon_utils/widgets/__init__.py @@ -9,6 +9,7 @@ from .folders_widget import ( FoldersWidget, FoldersQtModel, FOLDERS_MODEL_SENDER_NAME, + SimpleFoldersWidget, ) from .tasks_widget import ( @@ -31,6 +32,7 @@ __all__ = ( "FoldersWidget", "FoldersQtModel", "FOLDERS_MODEL_SENDER_NAME", + "SimpleFoldersWidget", "TasksWidget", "TasksQtModel", diff --git a/client/ayon_core/tools/ayon_utils/widgets/folders_widget.py b/client/ayon_core/tools/ayon_utils/widgets/folders_widget.py index 1e395b0368..6ca5264a8d 100644 --- a/client/ayon_core/tools/ayon_utils/widgets/folders_widget.py +++ b/client/ayon_core/tools/ayon_utils/widgets/folders_widget.py @@ -2,6 +2,11 @@ import collections from qtpy import QtWidgets, QtGui, QtCore +from ayon_core.lib.events import QueuedEventSystem +from ayon_core.tools.ayon_utils.models import ( + HierarchyModel, + HierarchyExpectedSelection, +) from ayon_core.tools.utils import ( RecursiveSortFilterProxyModel, TreeView, @@ -514,3 +519,110 @@ class FoldersWidget(QtWidgets.QWidget): if folder_id is not None: self.set_selected_folder(folder_id) self._controller.expected_folder_selected(folder_id) + + +class SimpleSelectionModel(object): + """Model handling selection changes. + + Triggering events: + - "selection.project.changed" + - "selection.folder.changed" + """ + + event_source = "selection.model" + + def __init__(self, controller): + self._controller = controller + + self._project_name = None + self._folder_id = None + self._task_id = None + self._task_name = None + + def get_selected_project_name(self): + return self._project_name + + def set_selected_project(self, project_name): + self._project_name = project_name + self._controller.emit_event( + "selection.project.changed", + {"project_name": project_name}, + self.event_source + ) + + def get_selected_folder_id(self): + return self._folder_id + + def set_selected_folder(self, folder_id): + if folder_id == self._folder_id: + return + self._folder_id = folder_id + self._controller.emit_event( + "selection.folder.changed", + { + "project_name": self._project_name, + "folder_id": folder_id, + }, + self.event_source + ) + + +class SimpleFoldersController(object): + def __init__(self): + self._event_system = self._create_event_system() + self._hierarchy_model = HierarchyModel(self) + self._selection_model = SimpleSelectionModel(self) + self._expected_selection = HierarchyExpectedSelection( + self, handle_project=False, handle_folder=True, handle_task=False + ) + + def emit_event(self, topic, data=None, source=None): + """Use implemented event system to trigger event.""" + + if data is None: + data = {} + self._event_system.emit(topic, data, source) + + def register_event_callback(self, topic, callback): + self._event_system.add_callback(topic, callback) + + # Model functions + def get_folder_items(self, project_name, sender=None): + return self._hierarchy_model.get_folder_items(project_name, sender) + + def set_selected_project(self, project_name): + self._selection_model.set_selected_project(project_name) + + def set_selected_folder(self, folder_id): + self._selection_model.set_selected_folder(folder_id) + + def get_expected_selection_data(self): + self._expected_selection.get_expected_selection_data() + + def expected_folder_selected(self, folder_id): + self._expected_selection.expected_folder_selected(folder_id) + + def _create_event_system(self): + return QueuedEventSystem() + + +class SimpleFoldersWidget(FoldersWidget): + def __init__(self, controller=None, *args, **kwargs): + if controller is None: + controller = SimpleFoldersController() + super(SimpleFoldersWidget, self).__init__(controller, *args, **kwargs) + + def set_project_name(self, project_name): + self._controller.set_selected_project(project_name) + super(SimpleFoldersWidget, self).set_project_name(project_name) + + def _on_project_selection_change(self, event): + """Ignore project selection change from controller. + + Only who can trigger project change is this widget with + 'set_project_name' which already cares about project change. + + Args: + event (Event): Triggered event. + """ + pass From 4744ff329ed90d010400c6a3f1943e248e376eb8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 18:28:27 +0100 Subject: [PATCH 137/573] added 'get_selected_folder_path' --- .../tools/ayon_utils/widgets/folders_widget.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/ayon_utils/widgets/folders_widget.py b/client/ayon_core/tools/ayon_utils/widgets/folders_widget.py index 6ca5264a8d..cf81d1c8ff 100644 --- a/client/ayon_core/tools/ayon_utils/widgets/folders_widget.py +++ b/client/ayon_core/tools/ayon_utils/widgets/folders_widget.py @@ -395,6 +395,15 @@ class FoldersWidget(QtWidgets.QWidget): return self._get_selected_item_id() + def get_selected_folder_path(self): + """Get selected folder id. + + Returns: + Union[str, None]: Folder path which is selected. + """ + + return self._get_selected_item_value(FOLDER_PATH_ROLE) + def get_selected_folder_label(self): """Selected folder label. @@ -478,9 +487,12 @@ class FoldersWidget(QtWidgets.QWidget): self.refreshed.emit() def _get_selected_item_id(self): + return self._get_selected_item_value(FOLDER_ID_ROLE) + + def _get_selected_item_value(self, role): selection_model = self._folders_view.selectionModel() for index in selection_model.selectedIndexes(): - item_id = index.data(FOLDER_ID_ROLE) + item_id = index.data(role) if item_id is not None: return item_id return None From 8331c90fcfe3e66dea703af0023ee0cefa81563b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 18:52:05 +0100 Subject: [PATCH 138/573] ExtractThumbnail uses AYON settings --- client/ayon_core/lib/transcoding.py | 9 +++-- .../plugins/publish/extract_thumbnail.py | 39 ++++++++++++------- client/ayon_core/settings/ayon_settings.py | 20 ---------- 3 files changed, 32 insertions(+), 36 deletions(-) diff --git a/client/ayon_core/lib/transcoding.py b/client/ayon_core/lib/transcoding.py index 6c6837dcf9..08e0bc9237 100644 --- a/client/ayon_core/lib/transcoding.py +++ b/client/ayon_core/lib/transcoding.py @@ -1385,23 +1385,26 @@ def _get_image_dimensions(application, input_path, log): def convert_color_values(application, color_value): """Get color mapping for ffmpeg and oiiotool. + Args: application (str): Application for which command should be created. - color_value (list[int]): List of 8bit int values for RGBA. + color_value (tuple[int, int, int, float]): List of 8bit int values + for RGBA. + Returns: str: ffmpeg returns hex string, oiiotool is string with floats. + """ red, green, blue, alpha = color_value if application == "ffmpeg": return "{0:0>2X}{1:0>2X}{2:0>2X}@{3}".format( - red, green, blue, (alpha / 255.0) + red, green, blue, alpha ) elif application == "oiiotool": red = float(red / 255) green = float(green / 255) blue = float(blue / 255) - alpha = float(alpha / 255) return "{0:.3f},{1:.3f},{2:.3f},{3:.3f}".format( red, green, blue, alpha) diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index 3874ddc13c..e392ce630a 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -42,15 +42,27 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): integrate_thumbnail = False target_size = { - "type": "resize", - "width": 1920, - "height": 1080 + "type": "source", + "resize": { + "width": 1920, + "height": 1080 + } } - background_color = None + background_color = (0, 0, 0, 0.0) duration_split = 0.5 # attribute presets from settings - oiiotool_defaults = None - ffmpeg_args = None + oiiotool_defaults = { + "type": "colorspace", + "colorspace": "color_picking", + "display_and_view": { + "display": "default", + "view": "sRGB" + } + } + ffmpeg_args = { + "input": [], + "output": [] + } product_names = [] def process(self, instance): @@ -369,7 +381,6 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): repre_display = colorspace_data.get("display") repre_view = colorspace_data.get("view") - oiio_default_type = None oiio_default_display = None oiio_default_view = None oiio_default_colorspace = None @@ -387,11 +398,12 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): # oiiotool_defaults elif self.oiiotool_defaults: oiio_default_type = self.oiiotool_defaults["type"] - if "colorspace" in oiio_default_type: + if "colorspace" == oiio_default_type: oiio_default_colorspace = self.oiiotool_defaults["colorspace"] else: - oiio_default_display = self.oiiotool_defaults["display"] - oiio_default_view = self.oiiotool_defaults["view"] + display_and_view = self.oiiotool_defaults["display_and_view"] + oiio_default_display = display_and_view["display"] + oiio_default_view = display_and_view["view"] try: convert_colorspace( @@ -507,11 +519,12 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): input_path, ): # get settings - if self.target_size.get("type") == "source": + if self.target_size["type"] == "source": return [] - target_width = self.target_size["width"] - target_height = self.target_size["height"] + resize = self.target_size["resize"] + target_width = resize["width"] + target_height = resize["height"] # form arg string per application return get_rescaled_command_arguments( diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index 5c4813f3aa..12d4833469 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -406,26 +406,6 @@ def _convert_global_project_settings(ayon_settings, output, default_settings): # Publish conversion ayon_publish = ayon_core["publish"] - # ExtractThumbnail plugin - ayon_extract_thumbnail = ayon_publish["ExtractThumbnail"] - # fix display and view at oiio defaults - ayon_default_oiio = copy.deepcopy( - ayon_extract_thumbnail["oiiotool_defaults"]) - display_and_view = ayon_default_oiio.pop("display_and_view") - ayon_default_oiio["display"] = display_and_view["display"] - ayon_default_oiio["view"] = display_and_view["view"] - ayon_extract_thumbnail["oiiotool_defaults"] = ayon_default_oiio - # fix target size - ayon_default_resize = copy.deepcopy(ayon_extract_thumbnail["target_size"]) - resize = ayon_default_resize.pop("resize") - ayon_default_resize["width"] = resize["width"] - ayon_default_resize["height"] = resize["height"] - ayon_extract_thumbnail["target_size"] = ayon_default_resize - # fix background color - ayon_extract_thumbnail["background_color"] = _convert_color( - ayon_extract_thumbnail["background_color"] - ) - # Project root settings - json string to dict ayon_core["project_environments"] = json.loads( ayon_core["project_environments"] From c8ec6ad2b83ba32a9467f31ccb4bcf1ed4969c52 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 18:58:39 +0100 Subject: [PATCH 139/573] 'project_environments' are loaded correctly --- client/ayon_core/lib/applications.py | 1 + client/ayon_core/settings/ayon_settings.py | 8 -------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/client/ayon_core/lib/applications.py b/client/ayon_core/lib/applications.py index 60ef2a6708..d79f18a707 100644 --- a/client/ayon_core/lib/applications.py +++ b/client/ayon_core/lib/applications.py @@ -1658,6 +1658,7 @@ def apply_project_environments_value( env_value = project_settings["global"]["project_environments"] if env_value: + env_value = json.loads(env_value) parsed_value = parse_environments(env_value, env_group) env.update(acre.compute( _merge_env(parsed_value, env), diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index 12d4833469..6d2163ee6e 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -406,14 +406,6 @@ def _convert_global_project_settings(ayon_settings, output, default_settings): # Publish conversion ayon_publish = ayon_core["publish"] - # Project root settings - json string to dict - ayon_core["project_environments"] = json.loads( - ayon_core["project_environments"] - ) - ayon_core["project_folder_structure"] = json.dumps(json.loads( - ayon_core["project_folder_structure"] - )) - # Tools settings ayon_core["sync_server"] = ( default_settings["global"]["sync_server"] From ad6472e9bf55ffea5e00d91e169a991f16089d35 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 15 Feb 2024 18:59:20 +0100 Subject: [PATCH 140/573] removed core settings conversion --- client/ayon_core/hosts/nuke/api/plugin.py | 2 +- client/ayon_core/lib/applications.py | 6 +++--- client/ayon_core/pipeline/colorspace.py | 2 +- client/ayon_core/pipeline/context_tools.py | 4 ++-- .../ayon_core/pipeline/create/legacy_create.py | 2 +- .../ayon_core/pipeline/create/subset_name.py | 2 +- client/ayon_core/pipeline/load/plugins.py | 2 +- client/ayon_core/pipeline/project_folders.py | 2 +- client/ayon_core/pipeline/publish/lib.py | 8 ++++---- client/ayon_core/pipeline/version_start.py | 2 +- .../pipeline/workfile/lock_workfile.py | 2 +- .../pipeline/workfile/path_resolving.py | 4 ++-- .../plugins/publish/collect_comment.py | 2 +- client/ayon_core/settings/ayon_settings.py | 18 ------------------ client/ayon_core/tools/pyblish_pype/control.py | 2 +- 15 files changed, 21 insertions(+), 39 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/plugin.py b/client/ayon_core/hosts/nuke/api/plugin.py index 4b8ddac167..d9be3fad17 100644 --- a/client/ayon_core/hosts/nuke/api/plugin.py +++ b/client/ayon_core/hosts/nuke/api/plugin.py @@ -493,7 +493,7 @@ def get_colorspace_from_node(node): def get_review_presets_config(): settings = get_current_project_settings() review_profiles = ( - settings["global"] + settings["core"] ["publish"] ["ExtractReview"] ["profiles"] diff --git a/client/ayon_core/lib/applications.py b/client/ayon_core/lib/applications.py index d79f18a707..a4e2d9c89d 100644 --- a/client/ayon_core/lib/applications.py +++ b/client/ayon_core/lib/applications.py @@ -1656,7 +1656,7 @@ def apply_project_environments_value( if project_settings is None: project_settings = get_project_settings(project_name) - env_value = project_settings["global"]["project_environments"] + env_value = project_settings["core"]["project_environments"] if env_value: env_value = json.loads(env_value) parsed_value = parse_environments(env_value, env_group) @@ -1917,7 +1917,7 @@ def should_start_last_workfile( project_settings = get_project_settings(project_name) profiles = ( project_settings - ["global"] + ["core"] ["tools"] ["Workfiles"] ["last_workfile_on_startup"] @@ -1967,7 +1967,7 @@ def should_workfile_tool_start( project_settings = get_project_settings(project_name) profiles = ( project_settings - ["global"] + ["core"] ["tools"] ["Workfiles"] ["open_workfile_tool_on_startup"] diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index 1b795e1c39..7100984217 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -1018,7 +1018,7 @@ def _get_imageio_settings(project_settings, host_name): tuple[dict, dict]: image io settings for global and host """ # get image io from global and host_name - imageio_global = project_settings["global"]["imageio"] + imageio_global = project_settings["core"]["imageio"] # host is optional, some might not have any settings imageio_host = project_settings.get(host_name, {}).get("imageio", {}) diff --git a/client/ayon_core/pipeline/context_tools.py b/client/ayon_core/pipeline/context_tools.py index 730852ff4a..7c0db0be27 100644 --- a/client/ayon_core/pipeline/context_tools.py +++ b/client/ayon_core/pipeline/context_tools.py @@ -208,8 +208,8 @@ def install_ayon_plugins(project_name=None, host_name=None): platform_name = platform.system().lower() project_plugins = ( project_settings - .get("global", {}) - .get("project_plugins", {}) + ["core"] + ["project_plugins"] .get(platform_name) ) or [] for path in project_plugins: diff --git a/client/ayon_core/pipeline/create/legacy_create.py b/client/ayon_core/pipeline/create/legacy_create.py index aab6b67e6f..2ab8b8e3e5 100644 --- a/client/ayon_core/pipeline/create/legacy_create.py +++ b/client/ayon_core/pipeline/create/legacy_create.py @@ -54,7 +54,7 @@ class LegacyCreator(object): ) global_type_settings = ( project_settings - .get("global", {}) + .get("core", {}) .get(plugin_type, {}) ) if not global_type_settings and not plugin_type_settings: diff --git a/client/ayon_core/pipeline/create/subset_name.py b/client/ayon_core/pipeline/create/subset_name.py index 81967ffd99..5925ec0f2b 100644 --- a/client/ayon_core/pipeline/create/subset_name.py +++ b/client/ayon_core/pipeline/create/subset_name.py @@ -47,7 +47,7 @@ def get_subset_name_template( if project_settings is None: project_settings = get_project_settings(project_name) - tools_settings = project_settings["global"]["tools"] + tools_settings = project_settings["core"]["tools"] profiles = tools_settings["creator"]["product_name_profiles"] filtering_criteria = { "product_types": family, diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 1d4627689f..962417c6f2 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -47,7 +47,7 @@ class LoaderPlugin(list): ) global_type_settings = ( project_settings - .get("global", {}) + .get("core", {}) .get(plugin_type, {}) ) if not global_type_settings and not plugin_type_settings: diff --git a/client/ayon_core/pipeline/project_folders.py b/client/ayon_core/pipeline/project_folders.py index ad205522a6..811a98ce4b 100644 --- a/client/ayon_core/pipeline/project_folders.py +++ b/client/ayon_core/pipeline/project_folders.py @@ -104,7 +104,7 @@ def _list_path_items(folder_structure): def get_project_basic_paths(project_name): project_settings = get_project_settings(project_name) folder_structure = ( - project_settings["global"]["project_folder_structure"] + project_settings["core"]["project_folder_structure"] ) if not folder_structure: return [] diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index a14651551f..90725e6d79 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -60,7 +60,7 @@ def get_template_name_profiles( return copy.deepcopy( project_settings - ["global"] + ["core"] ["tools"] ["publish"] ["template_name_profiles"] @@ -95,7 +95,7 @@ def get_hero_template_name_profiles( return copy.deepcopy( project_settings - ["global"] + ["core"] ["tools"] ["publish"] ["hero_template_name_profiles"] @@ -383,7 +383,7 @@ def get_plugin_settings(plugin, project_settings, log, category=None): # TODO: change after all plugins are moved one level up if category_from_file in ("ayon_core", "openpype"): - category_from_file = "global" + category_from_file = "core" try: return ( @@ -744,7 +744,7 @@ def get_custom_staging_dir_info(project_name, host_name, family, task_name, ValueError - if misconfigured template should be used """ settings = project_settings or get_project_settings(project_name) - custom_staging_dir_profiles = (settings["global"] + custom_staging_dir_profiles = (settings["core"] ["tools"] ["publish"] ["custom_staging_dir_profiles"]) diff --git a/client/ayon_core/pipeline/version_start.py b/client/ayon_core/pipeline/version_start.py index bd7d800335..4813910bf3 100644 --- a/client/ayon_core/pipeline/version_start.py +++ b/client/ayon_core/pipeline/version_start.py @@ -16,7 +16,7 @@ def get_versioning_start( project_settings = get_project_settings(project_name) version_start = 1 - settings = project_settings["global"] + settings = project_settings["core"] profiles = settings.get("version_start_category", {}).get("profiles", []) if not profiles: diff --git a/client/ayon_core/pipeline/workfile/lock_workfile.py b/client/ayon_core/pipeline/workfile/lock_workfile.py index a6d4348966..7eab3f36dc 100644 --- a/client/ayon_core/pipeline/workfile/lock_workfile.py +++ b/client/ayon_core/pipeline/workfile/lock_workfile.py @@ -64,7 +64,7 @@ def is_workfile_lock_enabled(host_name, project_name, project_setting=None): project_setting = get_project_settings(project_name) workfile_lock_profiles = ( project_setting - ["global"] + ["core"] ["tools"] ["Workfiles"] ["workfile_lock_profiles"]) diff --git a/client/ayon_core/pipeline/workfile/path_resolving.py b/client/ayon_core/pipeline/workfile/path_resolving.py index 2062705d3c..168e775475 100644 --- a/client/ayon_core/pipeline/workfile/path_resolving.py +++ b/client/ayon_core/pipeline/workfile/path_resolving.py @@ -72,7 +72,7 @@ def get_workfile_template_key( try: profiles = ( project_settings - ["global"] + ["core"] ["tools"] ["Workfiles"] ["workfile_template_profiles"] @@ -507,7 +507,7 @@ def create_workdir_extra_folders( # Load extra folders profiles extra_folders_profiles = ( - project_settings["global"]["tools"]["Workfiles"]["extra_folders"] + project_settings["core"]["tools"]["Workfiles"]["extra_folders"] ) # Skip if are empty if not extra_folders_profiles: diff --git a/client/ayon_core/plugins/publish/collect_comment.py b/client/ayon_core/plugins/publish/collect_comment.py index dadb7b9e8d..458c0a5658 100644 --- a/client/ayon_core/plugins/publish/collect_comment.py +++ b/client/ayon_core/plugins/publish/collect_comment.py @@ -43,7 +43,7 @@ class CollectInstanceCommentDef( @classmethod def apply_settings(cls, project_setting, _): - plugin_settings = project_setting["global"]["publish"].get( + plugin_settings = project_setting["core"]["publish"].get( "collect_comment_per_instance" ) if not plugin_settings: diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index 6d2163ee6e..4d45f8cca8 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -397,22 +397,6 @@ def _convert_royalrender_project_settings(ayon_settings, output): } -def _convert_global_project_settings(ayon_settings, output, default_settings): - if "core" not in ayon_settings: - return - - ayon_core = ayon_settings["core"] - - # Publish conversion - ayon_publish = ayon_core["publish"] - - # Tools settings - ayon_core["sync_server"] = ( - default_settings["global"]["sync_server"] - ) - output["global"] = ayon_core - - def convert_project_settings(ayon_settings, default_settings): default_settings = copy.deepcopy(default_settings) output = {} @@ -422,8 +406,6 @@ def convert_project_settings(ayon_settings, default_settings): _convert_royalrender_project_settings(ayon_settings, output) - _convert_global_project_settings(ayon_settings, output, default_settings) - for key, value in ayon_settings.items(): if key not in output: output[key] = value diff --git a/client/ayon_core/tools/pyblish_pype/control.py b/client/ayon_core/tools/pyblish_pype/control.py index 1a3e7a15f0..0e25fa9e27 100644 --- a/client/ayon_core/tools/pyblish_pype/control.py +++ b/client/ayon_core/tools/pyblish_pype/control.py @@ -208,7 +208,7 @@ class Controller(QtCore.QObject): if not presets: return {} - result = presets.get("global", {}).get("filters", {}) + result = presets.get("core", {}).get("filters", {}) hosts = pyblish.api.registered_hosts() for host in hosts: host_presets = presets.get(host, {}).get("filters") From 47d9af00eab4da6b5cc244a718a6bf9a759883c3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 15 Feb 2024 23:14:06 +0100 Subject: [PATCH 141/573] fix burnins def loop --- client/ayon_core/plugins/publish/extract_burnin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/extract_burnin.py b/client/ayon_core/plugins/publish/extract_burnin.py index 4da158343e..499a72bd4f 100644 --- a/client/ayon_core/plugins/publish/extract_burnin.py +++ b/client/ayon_core/plugins/publish/extract_burnin.py @@ -120,7 +120,7 @@ class ExtractBurnin(publish.Extractor): # Filter output definition by `burnin` represetation key repre_linked_burnins = [ burnin_def - for burnin_def in burnin_defs.items() + for burnin_def in burnin_defs if burnin_def["name"] in repre_burnin_links ] self.log.debug( From bc83a61365330b2c19dda4c523c14d20377fdaec Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 15 Feb 2024 23:28:35 +0100 Subject: [PATCH 142/573] fix type check --- client/ayon_core/hosts/maya/api/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/api/plugin.py b/client/ayon_core/hosts/maya/api/plugin.py index aba5fd8903..42c0c7051e 100644 --- a/client/ayon_core/hosts/maya/api/plugin.py +++ b/client/ayon_core/hosts/maya/api/plugin.py @@ -612,7 +612,7 @@ def get_load_color_for_family(family, settings=None): else: raise ValueError("Invalid color definition {}".format(str(color))) - if type(red, int): + if isinstance(red, int): red = red / 255.0 green = green / 255.0 blue = blue / 255.0 From 005e1b70cc2d89e5a1b62087ca3513406d5dc4d5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 09:35:35 +0100 Subject: [PATCH 143/573] removed unused rr_path attribute --- client/ayon_core/modules/royalrender/royal_render_module.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/ayon_core/modules/royalrender/royal_render_module.py b/client/ayon_core/modules/royalrender/royal_render_module.py index 66b09832d8..a413fc4d8d 100644 --- a/client/ayon_core/modules/royalrender/royal_render_module.py +++ b/client/ayon_core/modules/royalrender/royal_render_module.py @@ -20,7 +20,6 @@ class RoyalRenderModule(OpenPypeModule, IPluginPaths): def __init__(self, manager, settings): # type: (ayon_core.addon.AddonsManager, dict) -> None - self.rr_paths = {} self._api = None self.settings = settings super(RoyalRenderModule, self).__init__(manager, settings) @@ -29,7 +28,6 @@ class RoyalRenderModule(OpenPypeModule, IPluginPaths): # type: (dict) -> None rr_settings = module_settings[self.name] self.enabled = rr_settings["enabled"] - self.rr_paths = rr_settings.get("rr_paths") @staticmethod def get_plugin_paths(): From c34daca9b034f00400dc37e84e95858b2f5e5b5e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 09:40:36 +0100 Subject: [PATCH 144/573] removed royalrender settings conversion --- client/ayon_core/settings/ayon_settings.py | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index 190970b908..3a69711d57 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -78,21 +78,6 @@ def _convert_deadline_system_settings( output["modules"]["deadline"] = deadline_settings -def _convert_royalrender_system_settings( - ayon_settings, output, addon_versions, default_settings -): - enabled = addon_versions.get("royalrender") is not None - rr_settings = default_settings["modules"]["royalrender"] - rr_settings["enabled"] = enabled - if enabled: - ayon_royalrender = ayon_settings["royalrender"] - rr_settings["rr_paths"] = { - item["name"]: item["value"] - for item in ayon_royalrender["rr_paths"] - } - output["modules"]["royalrender"] = rr_settings - - def _convert_modules_system( ayon_settings, output, addon_versions, default_settings ): @@ -100,13 +85,13 @@ def _convert_modules_system( # TODO add 'enabled' values for func in ( _convert_deadline_system_settings, - _convert_royalrender_system_settings, ): func(ayon_settings, output, addon_versions, default_settings) for key in { "timers_manager", "clockify", + "royalrender", }: if addon_versions.get(key): output[key] = ayon_settings From 834cea70667cdeb885c34d2aaa2b14e1bd35dfa8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 09:42:21 +0100 Subject: [PATCH 145/573] Modified royal render addon initialization and name --- client/ayon_core/modules/royalrender/__init__.py | 4 ++-- .../modules/royalrender/royal_render_module.py | 12 +++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/modules/royalrender/__init__.py b/client/ayon_core/modules/royalrender/__init__.py index cc92e3b50d..a4769b6086 100644 --- a/client/ayon_core/modules/royalrender/__init__.py +++ b/client/ayon_core/modules/royalrender/__init__.py @@ -1,6 +1,6 @@ -from .royal_render_module import RoyalRenderModule +from .royal_render_module import RoyalRenderAddon __all__ = ( - "RoyalRenderModule", + "RoyalRenderAddon", ) diff --git a/client/ayon_core/modules/royalrender/royal_render_module.py b/client/ayon_core/modules/royalrender/royal_render_module.py index a413fc4d8d..ba3ce67198 100644 --- a/client/ayon_core/modules/royalrender/royal_render_module.py +++ b/client/ayon_core/modules/royalrender/royal_render_module.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- """Module providing support for Royal Render.""" import os -import ayon_core.modules -from ayon_core.modules import OpenPypeModule, IPluginPaths + +from ayon_core.addon import AYONAddon, IPluginPaths -class RoyalRenderModule(OpenPypeModule, IPluginPaths): +class RoyalRenderAddon(AYONAddon, IPluginPaths): """Class providing basic Royal Render implementation logic.""" name = "royalrender" @@ -19,15 +19,13 @@ class RoyalRenderModule(OpenPypeModule, IPluginPaths): return self._api def __init__(self, manager, settings): - # type: (ayon_core.addon.AddonsManager, dict) -> None self._api = None self.settings = settings super(RoyalRenderModule, self).__init__(manager, settings) - def initialize(self, module_settings): + def initialize(self, studio_settings): # type: (dict) -> None - rr_settings = module_settings[self.name] - self.enabled = rr_settings["enabled"] + self.enabled = self.name in studio_settings @staticmethod def get_plugin_paths(): From 722923f3c09c3433bd0aefede385414fe4d581eb Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 09:43:19 +0100 Subject: [PATCH 146/573] modified code to use AYON settings --- client/ayon_core/modules/royalrender/lib.py | 31 ++++----------- .../publish/collect_rr_path_from_instance.py | 39 +++++++------------ .../publish/submit_jobs_to_royalrender.py | 31 ++++----------- 3 files changed, 31 insertions(+), 70 deletions(-) diff --git a/client/ayon_core/modules/royalrender/lib.py b/client/ayon_core/modules/royalrender/lib.py index 5343a5f06f..966eb82ab1 100644 --- a/client/ayon_core/modules/royalrender/lib.py +++ b/client/ayon_core/modules/royalrender/lib.py @@ -213,30 +213,15 @@ class BaseCreateRoyalRenderJob(pyblish.api.InstancePlugin, @staticmethod def _resolve_rr_path(context, rr_path_name): # type: (pyblish.api.Context, str) -> str - rr_settings = ( - context.data - ["system_settings"] - ["modules"] - ["royalrender"] - ) - try: - default_servers = rr_settings["rr_paths"] - project_servers = ( - context.data - ["project_settings"] - ["royalrender"] - ["rr_paths"] - ) - rr_servers = { - k: default_servers[k] - for k in project_servers - if k in default_servers - } - - except (AttributeError, KeyError): - # Handle situation were we had only one url for royal render. - return context.data["defaultRRPath"][platform.system().lower()] + rr_settings = context.data["project_settings"]["royalrender"] + rr_paths = rr_settings["rr_paths"] + selected_paths = rr_settings["selected_rr_paths"] + rr_servers = { + path_key: rr_paths[path_key] + for path_key in selected_paths + if path_key in rr_paths + } return rr_servers[rr_path_name][platform.system().lower()] def expected_files(self, instance, path, start_frame, end_frame): diff --git a/client/ayon_core/modules/royalrender/plugins/publish/collect_rr_path_from_instance.py b/client/ayon_core/modules/royalrender/plugins/publish/collect_rr_path_from_instance.py index e978ce5bed..f43ed1ca49 100644 --- a/client/ayon_core/modules/royalrender/plugins/publish/collect_rr_path_from_instance.py +++ b/client/ayon_core/modules/royalrender/plugins/publish/collect_rr_path_from_instance.py @@ -18,30 +18,21 @@ class CollectRRPathFromInstance(pyblish.api.InstancePlugin): def _collect_rr_path_name(instance): # type: (pyblish.api.Instance) -> str """Get Royal Render pat name from render instance.""" - rr_settings = ( - instance.context.data - ["system_settings"] - ["modules"] - ["royalrender"] - ) - if not instance.data.get("rrPaths"): + + instance_rr_paths = instance.data.get("rrPaths") + if instance_rr_paths is None: return "default" - try: - default_servers = rr_settings["rr_paths"] - project_servers = ( - instance.context.data - ["project_settings"] - ["royalrender"] - ["rr_paths"] - ) - rr_servers = { - k: default_servers[k] - for k in project_servers - if k in default_servers - } - except (AttributeError, KeyError): - # Handle situation were we had only one url for royal render. - return rr_settings["rr_paths"]["default"] + rr_settings = instance.context.data["project_settings"]["royalrender"] + rr_paths = rr_settings["rr_paths"] + selected_paths = rr_settings["selected_rr_paths"] - return list(rr_servers.keys())[int(instance.data.get("rrPaths"))] + rr_servers = { + path_key + for path_key in selected_paths + if path_key in rr_paths + } + for instance_rr_path in instance_rr_paths: + if instance_rr_path in rr_servers: + return instance_rr_path + return "default" diff --git a/client/ayon_core/modules/royalrender/plugins/publish/submit_jobs_to_royalrender.py b/client/ayon_core/modules/royalrender/plugins/publish/submit_jobs_to_royalrender.py index a76bdfc26c..5e25464186 100644 --- a/client/ayon_core/modules/royalrender/plugins/publish/submit_jobs_to_royalrender.py +++ b/client/ayon_core/modules/royalrender/plugins/publish/submit_jobs_to_royalrender.py @@ -104,28 +104,13 @@ class SubmitJobsToRoyalRender(pyblish.api.ContextPlugin): @staticmethod def _resolve_rr_path(context, rr_path_name): # type: (pyblish.api.Context, str) -> str - rr_settings = ( - context.data - ["system_settings"] - ["modules"] - ["royalrender"] - ) - try: - default_servers = rr_settings["rr_paths"] - project_servers = ( - context.data - ["project_settings"] - ["royalrender"] - ["rr_paths"] - ) - rr_servers = { - k: default_servers[k] - for k in project_servers - if k in default_servers - } - - except (AttributeError, KeyError): - # Handle situation were we had only one url for royal render. - return context.data["defaultRRPath"][platform.system().lower()] + rr_settings = context.data["project_settings"]["royalrender"] + rr_paths = rr_settings["rr_paths"] + selected_paths = rr_settings["selected_rr_paths"] + rr_servers = { + path_key: rr_paths[path_key] + for path_key in selected_paths + if path_key in rr_paths + } return rr_servers[rr_path_name][platform.system().lower()] From 50fbc854a3f10db8468e07761d5e2f2e12a534f0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 09:44:31 +0100 Subject: [PATCH 147/573] renamed 'royal_render_module.py' to 'addon.py' --- client/ayon_core/modules/royalrender/__init__.py | 2 +- .../modules/royalrender/{royal_render_module.py => addon.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename client/ayon_core/modules/royalrender/{royal_render_module.py => addon.py} (100%) diff --git a/client/ayon_core/modules/royalrender/__init__.py b/client/ayon_core/modules/royalrender/__init__.py index a4769b6086..121530beda 100644 --- a/client/ayon_core/modules/royalrender/__init__.py +++ b/client/ayon_core/modules/royalrender/__init__.py @@ -1,4 +1,4 @@ -from .royal_render_module import RoyalRenderAddon +from .addon import RoyalRenderAddon __all__ = ( diff --git a/client/ayon_core/modules/royalrender/royal_render_module.py b/client/ayon_core/modules/royalrender/addon.py similarity index 100% rename from client/ayon_core/modules/royalrender/royal_render_module.py rename to client/ayon_core/modules/royalrender/addon.py From 313894c5b1286328a24ca524d1b0b9bc45ad0153 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 09:51:56 +0100 Subject: [PATCH 148/573] commented out unused 'api' property --- client/ayon_core/modules/royalrender/addon.py | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/client/ayon_core/modules/royalrender/addon.py b/client/ayon_core/modules/royalrender/addon.py index ba3ce67198..e9dd694f4c 100644 --- a/client/ayon_core/modules/royalrender/addon.py +++ b/client/ayon_core/modules/royalrender/addon.py @@ -9,19 +9,14 @@ class RoyalRenderAddon(AYONAddon, IPluginPaths): """Class providing basic Royal Render implementation logic.""" name = "royalrender" - @property - def api(self): - if not self._api: - # import royal render modules - from . import api as rr_api - self._api = rr_api.Api(self.settings) - - return self._api - - def __init__(self, manager, settings): - self._api = None - self.settings = settings - super(RoyalRenderModule, self).__init__(manager, settings) + # @property + # def api(self): + # if not self._api: + # # import royal render modules + # from . import api as rr_api + # self._api = rr_api.Api(self.settings) + # + # return self._api def initialize(self, studio_settings): # type: (dict) -> None From c359f05d8d82e15375c83a941a94a3eb0641650b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 09:53:09 +0100 Subject: [PATCH 149/573] renamed 'api' property to 'rr_api' --- client/ayon_core/modules/royalrender/addon.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/modules/royalrender/addon.py b/client/ayon_core/modules/royalrender/addon.py index e9dd694f4c..e69cf9feec 100644 --- a/client/ayon_core/modules/royalrender/addon.py +++ b/client/ayon_core/modules/royalrender/addon.py @@ -9,14 +9,14 @@ class RoyalRenderAddon(AYONAddon, IPluginPaths): """Class providing basic Royal Render implementation logic.""" name = "royalrender" + # _rr_api = None # @property - # def api(self): - # if not self._api: + # def rr_api(self): + # if not self._rr_api: # # import royal render modules - # from . import api as rr_api - # self._api = rr_api.Api(self.settings) - # - # return self._api + # from .api import Api + # self._rr_api = Api(self.settings) + # return self._rr_api def initialize(self, studio_settings): # type: (dict) -> None From fe2909f2d8a8bc339c6ae12ac676ded79ae3ebdc Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 10:22:51 +0100 Subject: [PATCH 150/573] removed deadline conversion --- client/ayon_core/settings/ayon_settings.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index 190970b908..830cbd5e10 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -62,22 +62,6 @@ def _convert_general(ayon_settings, output, default_settings): } -def _convert_deadline_system_settings( - ayon_settings, output, addon_versions, default_settings -): - enabled = addon_versions.get("deadline") is not None - deadline_settings = default_settings["modules"]["deadline"] - deadline_settings["enabled"] = enabled - if enabled: - ayon_deadline = ayon_settings["deadline"] - deadline_settings["deadline_urls"] = { - item["name"]: item["value"] - for item in ayon_deadline["deadline_urls"] - } - - output["modules"]["deadline"] = deadline_settings - - def _convert_royalrender_system_settings( ayon_settings, output, addon_versions, default_settings ): @@ -99,7 +83,6 @@ def _convert_modules_system( # TODO add all modules # TODO add 'enabled' values for func in ( - _convert_deadline_system_settings, _convert_royalrender_system_settings, ): func(ayon_settings, output, addon_versions, default_settings) @@ -107,6 +90,7 @@ def _convert_modules_system( for key in { "timers_manager", "clockify", + "deadline", }: if addon_versions.get(key): output[key] = ayon_settings From b41109559b5e3d3cad29cd3a238d2f6605ac3a5e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 10:28:29 +0100 Subject: [PATCH 151/573] modified deadline addon to use AYON settings --- .../modules/deadline/deadline_module.py | 38 +++++++++---------- .../collect_deadline_server_from_instance.py | 7 +++- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/client/ayon_core/modules/deadline/deadline_module.py b/client/ayon_core/modules/deadline/deadline_module.py index c98d04759e..97d346c287 100644 --- a/client/ayon_core/modules/deadline/deadline_module.py +++ b/client/ayon_core/modules/deadline/deadline_module.py @@ -4,7 +4,7 @@ import six import sys from ayon_core.lib import requests_get, Logger -from ayon_core.modules import OpenPypeModule, IPluginPaths +from ayon_core.modules import AYONAddon, IPluginPaths class DeadlineWebserviceError(Exception): @@ -13,28 +13,28 @@ class DeadlineWebserviceError(Exception): """ -class DeadlineModule(OpenPypeModule, IPluginPaths): +class DeadlineModule(AYONAddon, IPluginPaths): name = "deadline" - def __init__(self, manager, settings): - self.deadline_urls = {} - super(DeadlineModule, self).__init__(manager, settings) - - def initialize(self, modules_settings): + def initialize(self, studio_settings): # This module is always enabled - deadline_settings = modules_settings[self.name] - self.enabled = deadline_settings["enabled"] - deadline_url = deadline_settings.get("DEADLINE_REST_URL") - if deadline_url: - self.deadline_urls = {"default": deadline_url} - else: - self.deadline_urls = deadline_settings.get("deadline_urls") # noqa: E501 + deadline_urls = {} + enabled = self.name in studio_settings + if enabled: + deadline_settings = studio_settings[self.name] + deadline_urls = { + url_item["name"]: url_item["value"] + for url_item in deadline_settings["deadline_urls"] + } - if not self.deadline_urls: - self.enabled = False - self.log.warning(("default Deadline Webservice URL " - "not specified. Disabling module.")) - return + if enabled and not deadline_urls: + enabled = False + self.log.warning(( + "Deadline Webservice URLs are not specified. Disabling addon." + )) + + self.enabled = enabled + self.deadline_urls = deadline_urls def get_plugin_paths(self): """Deadline plugin paths.""" diff --git a/client/ayon_core/modules/deadline/plugins/publish/collect_deadline_server_from_instance.py b/client/ayon_core/modules/deadline/plugins/publish/collect_deadline_server_from_instance.py index 0cfe7c9b39..445971f2b0 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/collect_deadline_server_from_instance.py +++ b/client/ayon_core/modules/deadline/plugins/publish/collect_deadline_server_from_instance.py @@ -47,11 +47,11 @@ class CollectDeadlineServerFromInstance(pyblish.api.InstancePlugin): deadline_settings = ( render_instance.context.data ["system_settings"] - ["modules"] ["deadline"] ) default_server = render_instance.context.data["defaultDeadline"] + # QUESTION How and where is this is set? Should be removed? instance_server = render_instance.data.get("deadlineServers") if not instance_server: self.log.debug("Using default server.") @@ -64,7 +64,10 @@ class CollectDeadlineServerFromInstance(pyblish.api.InstancePlugin): asString=True ) - default_servers = deadline_settings["deadline_urls"] + default_servers = { + url_item["name"]: url_item["value"] + for url_item in deadline_settings["deadline_urls"] + } project_servers = ( render_instance.context.data ["project_settings"] From 3a10a9e09911139c745aebf9f35eefa05f3e49de Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 16 Feb 2024 12:48:42 +0000 Subject: [PATCH 152/573] Expose families transfer attribute --- server_addon/deadline/server/settings/publish_plugins.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/server_addon/deadline/server/settings/publish_plugins.py b/server_addon/deadline/server/settings/publish_plugins.py index 3f5ac3108d..10ec8ac95f 100644 --- a/server_addon/deadline/server/settings/publish_plugins.py +++ b/server_addon/deadline/server/settings/publish_plugins.py @@ -287,6 +287,13 @@ class ProcessSubmittedJobOnFarmModel(BaseSettingsModel): default_factory=list, title="Skip integration of representation with ext" ) + families_transfer: list[str] = SettingsField( + default_factory=list, + title=( + "List of family names to transfer\n" + "to generated instances (AOVs for example)." + ) + ) aov_filter: list[AOVFilterSubmodel] = SettingsField( default_factory=list, title="Reviewable products filter", @@ -470,6 +477,7 @@ DEFAULT_DEADLINE_PLUGINS_SETTINGS = { "deadline_priority": 50, "publishing_script": "", "skip_integration_repre_list": [], + "families_transfer": ["render3d", "render2d", "ftrack", "slate"], "aov_filter": [ { "name": "maya", From aafc6470c29f73fad297077bd256dab943c8d5f3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 14:45:10 +0100 Subject: [PATCH 153/573] use 'folderPath' instead of 'asset' --- .../publish/submit_maya_remote_publish_deadline.py | 2 +- .../plugins/publish/submit_publish_cache_job.py | 6 +++--- .../deadline/plugins/publish/submit_publish_job.py | 12 ++++++------ client/ayon_core/modules/royalrender/lib.py | 2 +- .../plugins/publish/collect_sequences_from_job.py | 4 ++-- .../publish/create_publish_royalrender_job.py | 4 ++-- .../timers_manager/plugins/publish/start_timer.py | 2 +- client/ayon_core/pipeline/create/legacy_create.py | 4 ++-- client/ayon_core/pipeline/farm/pyblish_functions.py | 10 +++++----- .../pipeline/publish/abstract_collect_render.py | 2 +- .../plugins/publish/collect_anatomy_instance_data.py | 4 ++-- client/ayon_core/plugins/publish/collect_audio.py | 2 +- .../plugins/publish/collect_context_entities.py | 2 +- .../plugins/publish/collect_current_context.py | 7 +++---- .../ayon_core/plugins/publish/collect_frames_fix.py | 2 +- .../plugins/publish/collect_from_create_context.py | 3 +-- .../ayon_core/plugins/publish/collect_hierarchy.py | 2 +- .../plugins/publish/collect_rendered_files.py | 11 ++++++++--- .../plugins/publish/extract_hierarchy_to_ayon.py | 2 +- .../plugins/publish/extract_otio_audio_tracks.py | 2 +- .../ayon_core/plugins/publish/integrate_thumbnail.py | 5 +++-- .../plugins/publish/validate_editorial_asset_name.py | 2 +- .../plugins/publish/validate_unique_subsets.py | 2 +- 23 files changed, 49 insertions(+), 45 deletions(-) diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py index 1042c44c33..772fa03628 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py @@ -105,7 +105,7 @@ class MayaSubmitRemotePublishDeadline( } environment["AYON_PROJECT_NAME"] = project_name - environment["AYON_FOLDER_PATH"] = instance.context.data["asset"] + environment["AYON_FOLDER_PATH"] = instance.context.data["folderPath"] environment["AYON_TASK_NAME"] = instance.context.data["task"] environment["AYON_APP_NAME"] = os.environ.get("AYON_APP_NAME") environment["OPENPYPE_PUBLISH_SUBSET"] = instance.data["subset"] diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py index 54d4fb47fc..0b0e293943 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py @@ -112,7 +112,7 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin, output_dir = self._get_publish_folder( anatomy, deepcopy(instance.data["anatomyData"]), - instance.data.get("asset"), + instance.data.get("folderPath"), instance.data["subset"], instance.context, instance.data["family"], @@ -126,7 +126,7 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin, environment = { "AYON_PROJECT_NAME": instance.context.data["projectName"], - "AYON_FOLDER_PATH": instance.context.data["asset"], + "AYON_FOLDER_PATH": instance.context.data["folderPath"], "AYON_TASK_NAME": instance.context.data["task"], "AYON_USERNAME": instance.context.data["user"], "AYON_LOG_NO_COLORS": "1", @@ -359,7 +359,7 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin, # publish job file publish_job = { - "asset": instance_skeleton_data["asset"], + "folderPath": instance_skeleton_data["folderPath"], "frameStart": instance_skeleton_data["frameStart"], "frameEnd": instance_skeleton_data["frameEnd"], "fps": instance_skeleton_data["fps"], diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py index d4d04f79f6..31c845a0b8 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py @@ -189,7 +189,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, output_dir = self._get_publish_folder( anatomy, deepcopy(instance.data["anatomyData"]), - instance.data.get("asset"), + instance.data.get("folderPath"), instances[0]["subset"], instance.context, instances[0]["family"], @@ -203,7 +203,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, environment = { "AYON_PROJECT_NAME": instance.context.data["projectName"], - "AYON_FOLDER_PATH": instance.context.data["asset"], + "AYON_FOLDER_PATH": instance.context.data["folderPath"], "AYON_TASK_NAME": instance.context.data["task"], "AYON_USERNAME": instance.context.data["user"], "AYON_LOG_NO_COLORS": "1", @@ -335,7 +335,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, self.context = context self.anatomy = instance.context.data["anatomy"] - asset = data.get("asset") or context.data["asset"] + folder_path = data.get("folderPath") or context.data["folderPath"] subset = data.get("subset") start = instance.data.get("frameStart") @@ -360,7 +360,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, if data.get("extendFrames", False): start, end = self._extend_frames( - asset, + folder_path, subset, start, end, @@ -402,7 +402,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, "family": family, "subset": subset, "families": families, - "asset": asset, + "folderPath": folder_path, "frameStart": start, "frameEnd": end, "handleStart": handle_start, @@ -620,7 +620,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, # publish job file publish_job = { - "asset": instance_skeleton_data["asset"], + "folderPath": instance_skeleton_data["folderPath"], "frameStart": instance_skeleton_data["frameStart"], "frameEnd": instance_skeleton_data["frameEnd"], "fps": instance_skeleton_data["fps"], diff --git a/client/ayon_core/modules/royalrender/lib.py b/client/ayon_core/modules/royalrender/lib.py index 5343a5f06f..f782cb9984 100644 --- a/client/ayon_core/modules/royalrender/lib.py +++ b/client/ayon_core/modules/royalrender/lib.py @@ -350,7 +350,7 @@ class BaseCreateRoyalRenderJob(pyblish.api.InstancePlugin, add_kwargs = { "project": anatomy_data["project"]["name"], - "asset": instance.context.data["asset"], + "asset": instance.context.data["folderPath"], "task": anatomy_data["task"]["name"], "app": instance.context.data.get("appName"), "envgroup": "farm" diff --git a/client/ayon_core/modules/royalrender/plugins/publish/collect_sequences_from_job.py b/client/ayon_core/modules/royalrender/plugins/publish/collect_sequences_from_job.py index cd34ba9bb3..4460e11004 100644 --- a/client/ayon_core/modules/royalrender/plugins/publish/collect_sequences_from_job.py +++ b/client/ayon_core/modules/royalrender/plugins/publish/collect_sequences_from_job.py @@ -189,8 +189,8 @@ class CollectSequencesFromJob(pyblish.api.ContextPlugin): "family": families[0], # backwards compatibility / pyblish "families": list(families), "subset": subset, - "asset": data.get( - "asset", context.data["asset"] + "folderPath": data.get( + "folderPath", context.data["folderPath"] ), "stagingDir": root, "frameStart": start, diff --git a/client/ayon_core/modules/royalrender/plugins/publish/create_publish_royalrender_job.py b/client/ayon_core/modules/royalrender/plugins/publish/create_publish_royalrender_job.py index 910abfcb15..33f5585cef 100644 --- a/client/ayon_core/modules/royalrender/plugins/publish/create_publish_royalrender_job.py +++ b/client/ayon_core/modules/royalrender/plugins/publish/create_publish_royalrender_job.py @@ -132,7 +132,7 @@ class CreatePublishRoyalRenderJob(pyblish.api.InstancePlugin, # publish job file publish_job = { - "asset": instance_skeleton_data["asset"], + "folderPath": instance_skeleton_data["folderPath"], "frameStart": instance_skeleton_data["frameStart"], "frameEnd": instance_skeleton_data["frameEnd"], "fps": instance_skeleton_data["fps"], @@ -180,7 +180,7 @@ class CreatePublishRoyalRenderJob(pyblish.api.InstancePlugin, environment = RREnvList({ "AYON_PROJECT_NAME": anatomy_data["project"]["name"], - "AYON_FOLDER_PATH": instance.context.data["asset"], + "AYON_FOLDER_PATH": instance.context.data["folderPath"], "AYON_TASK_NAME": anatomy_data["task"]["name"], "AYON_USERNAME": anatomy_data["user"] }) diff --git a/client/ayon_core/modules/timers_manager/plugins/publish/start_timer.py b/client/ayon_core/modules/timers_manager/plugins/publish/start_timer.py index 51f707ecf6..a3eb49ee70 100644 --- a/client/ayon_core/modules/timers_manager/plugins/publish/start_timer.py +++ b/client/ayon_core/modules/timers_manager/plugins/publish/start_timer.py @@ -24,7 +24,7 @@ class StartTimer(pyblish.api.ContextPlugin): return project_name = context.data["projectName"] - asset_name = context.data.get("asset") + asset_name = context.data.get("folderPath") task_name = context.data.get("task") if not project_name or not asset_name or not task_name: self.log.info(( diff --git a/client/ayon_core/pipeline/create/legacy_create.py b/client/ayon_core/pipeline/create/legacy_create.py index aab6b67e6f..a437c7921c 100644 --- a/client/ayon_core/pipeline/create/legacy_create.py +++ b/client/ayon_core/pipeline/create/legacy_create.py @@ -27,7 +27,7 @@ class LegacyCreator(object): log = logging.getLogger("LegacyCreator") log.propagate = True - def __init__(self, name, asset, options=None, data=None): + def __init__(self, name, folder_path, options=None, data=None): self.name = name # For backwards compatibility self.options = options @@ -35,7 +35,7 @@ class LegacyCreator(object): self.data = collections.OrderedDict() self.data["id"] = "pyblish.avalon.instance" self.data["family"] = self.family - self.data["asset"] = asset + self.data["folderPath"] = folder_path self.data["subset"] = name self.data["active"] = True diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 389d3d27ed..4652d34011 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -197,7 +197,7 @@ def create_skeleton_instance( if data.get("extendFrames", False): time_data.start, time_data.end = extend_frames( - data["asset"], + data["folderPath"], data["subset"], time_data.start, time_data.end, @@ -228,7 +228,7 @@ def create_skeleton_instance( "family": family, "subset": data["subset"], "families": families, - "asset": data["asset"], + "folderPath": data["folderPath"], "frameStart": time_data.start, "frameEnd": time_data.end, "handleStart": time_data.handle_start, @@ -777,7 +777,7 @@ def create_skeleton_instance_cache(instance): if data.get("extendFrames", False): time_data.start, time_data.end = extend_frames( - data["asset"], + data["folderPath"], data["subset"], time_data.start, time_data.end, @@ -805,7 +805,7 @@ def create_skeleton_instance_cache(instance): "family": family, "subset": data["subset"], "families": families, - "asset": data["asset"], + "folderPath": data["folderPath"], "frameStart": time_data.start, "frameEnd": time_data.end, "handleStart": time_data.handle_start, @@ -1011,7 +1011,7 @@ def copy_extend_frames(instance, representation): version = get_last_version_by_subset_name( project_name, instance.data.get("subset"), - asset_name=instance.data.get("asset") + asset_name=instance.data.get("folderPath") ) # get its files based on extension diff --git a/client/ayon_core/pipeline/publish/abstract_collect_render.py b/client/ayon_core/pipeline/publish/abstract_collect_render.py index 764532cadb..6ca1d81bc7 100644 --- a/client/ayon_core/pipeline/publish/abstract_collect_render.py +++ b/client/ayon_core/pipeline/publish/abstract_collect_render.py @@ -30,7 +30,7 @@ class RenderInstance(object): label = attr.ib() # label to show in GUI subset = attr.ib() # subset name task = attr.ib() # task name - asset = attr.ib() # asset name + folderPath = attr.ib() # folder path attachTo = attr.ib() # subset name to attach render to setMembers = attr.ib() # list of nodes/members producing render output publish = attr.ib() # bool, True to publish instance diff --git a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py index 336ac02b8e..763aee5b1c 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py @@ -296,7 +296,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): if hierarchy: parent_name = hierarchy.split("/")[-1] - asset_name = instance.data["asset"].split("/")[-1] + asset_name = instance.data["folderPath"].split("/")[-1] anatomy_data.update({ "asset": asset_name, "hierarchy": hierarchy, @@ -337,7 +337,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): # Try to find task data based on hierarchy context and asset name hierarchy_context = instance.context.data.get("hierarchyContext") - asset_name = instance.data.get("asset") + asset_name = instance.data.get("folderPath") if not hierarchy_context or not asset_name: return diff --git a/client/ayon_core/plugins/publish/collect_audio.py b/client/ayon_core/plugins/publish/collect_audio.py index 6da3fd0685..f0efd546e7 100644 --- a/client/ayon_core/plugins/publish/collect_audio.py +++ b/client/ayon_core/plugins/publish/collect_audio.py @@ -66,7 +66,7 @@ class CollectAudio(pyblish.api.ContextPlugin): # Add audio to instance if exists. instances_by_asset_name = collections.defaultdict(list) for instance in filtered_instances: - asset_name = instance.data["asset"] + asset_name = instance.data["folderPath"] instances_by_asset_name[asset_name].append(instance) asset_names = set(instances_by_asset_name.keys()) diff --git a/client/ayon_core/plugins/publish/collect_context_entities.py b/client/ayon_core/plugins/publish/collect_context_entities.py index 30bb184ef5..64ef73e2d9 100644 --- a/client/ayon_core/plugins/publish/collect_context_entities.py +++ b/client/ayon_core/plugins/publish/collect_context_entities.py @@ -25,7 +25,7 @@ class CollectContextEntities(pyblish.api.ContextPlugin): def process(self, context): project_name = context.data["projectName"] - asset_name = context.data["asset"] + asset_name = context.data["folderPath"] task_name = context.data["task"] project_entity = get_project(project_name) diff --git a/client/ayon_core/plugins/publish/collect_current_context.py b/client/ayon_core/plugins/publish/collect_current_context.py index 90b9fcdcbd..6a22daa04a 100644 --- a/client/ayon_core/plugins/publish/collect_current_context.py +++ b/client/ayon_core/plugins/publish/collect_current_context.py @@ -1,7 +1,7 @@ """ Provides: context -> projectName (str) - context -> asset (str) + context -> folderPath (str) context -> task (str) """ @@ -21,7 +21,7 @@ class CollectCurrentContext(pyblish.api.ContextPlugin): def process(self, context): # Check if values are already set project_name = context.data.get("projectName") - asset_name = context.data.get("asset") + asset_name = context.data.get("folderPath") task_name = context.data.get("task") current_context = get_current_context() @@ -29,13 +29,12 @@ class CollectCurrentContext(pyblish.api.ContextPlugin): context.data["projectName"] = current_context["project_name"] if not asset_name: - context.data["asset"] = current_context["asset_name"] + context.data["folderPath"] = current_context["asset_name"] if not task_name: context.data["task"] = current_context["task_name"] # QUESTION should we be explicit with keys? (the same on instances) - # - 'asset' -> 'assetName' # - 'task' -> 'taskName' self.log.info(( diff --git a/client/ayon_core/plugins/publish/collect_frames_fix.py b/client/ayon_core/plugins/publish/collect_frames_fix.py index 4903991d40..6996844eda 100644 --- a/client/ayon_core/plugins/publish/collect_frames_fix.py +++ b/client/ayon_core/plugins/publish/collect_frames_fix.py @@ -41,7 +41,7 @@ class CollectFramesFixDef( instance.data["frames_to_fix"] = frames_to_fix subset_name = instance.data["subset"] - asset_name = instance.data["asset"] + asset_name = instance.data["folderPath"] project_entity = instance.data["projectEntity"] project_name = project_entity["name"] diff --git a/client/ayon_core/plugins/publish/collect_from_create_context.py b/client/ayon_core/plugins/publish/collect_from_create_context.py index 66ca5745b2..7152446de8 100644 --- a/client/ayon_core/plugins/publish/collect_from_create_context.py +++ b/client/ayon_core/plugins/publish/collect_from_create_context.py @@ -38,7 +38,6 @@ class CollectFromCreateContext(pyblish.api.ContextPlugin): for created_instance in create_context.instances: instance_data = created_instance.data_to_store() - instance_data["asset"] = instance_data.pop("folderPath") if instance_data["active"]: thumbnail_path = thumbnail_paths_by_instance_id.get( created_instance.id @@ -80,7 +79,7 @@ class CollectFromCreateContext(pyblish.api.ContextPlugin): instance = context.create_instance(subset) instance.data.update({ "subset": subset, - "asset": in_data["asset"], + "folderPath": in_data["folderPath"], "task": in_data["task"], "label": in_data.get("label") or subset, "name": subset, diff --git a/client/ayon_core/plugins/publish/collect_hierarchy.py b/client/ayon_core/plugins/publish/collect_hierarchy.py index 32f10ba4c8..d7077d030a 100644 --- a/client/ayon_core/plugins/publish/collect_hierarchy.py +++ b/client/ayon_core/plugins/publish/collect_hierarchy.py @@ -62,7 +62,7 @@ class CollectHierarchy(pyblish.api.ContextPlugin): "pixelAspect": instance.data["pixelAspect"] } # Split by '/' for AYON where asset is a path - name = instance.data["asset"].split("/")[-1] + name = instance.data["folderPath"].split("/")[-1] actual = {name: shot_data} for parent in reversed(instance.data["parents"]): diff --git a/client/ayon_core/plugins/publish/collect_rendered_files.py b/client/ayon_core/plugins/publish/collect_rendered_files.py index 9a316b69a4..fcea773208 100644 --- a/client/ayon_core/plugins/publish/collect_rendered_files.py +++ b/client/ayon_core/plugins/publish/collect_rendered_files.py @@ -71,14 +71,19 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin): """ # validate basic necessary data data_err = "invalid json file - missing data" - required = ["asset", "user", "comment", + required = ["user", "comment", "job", "instances", "version"] assert all(elem in data.keys() for elem in required), data_err + if "folderPath" not in data and "asset" not in data: + raise AssertionError(data_err) + + if "folderPath" not in data: + data["folderPath"] = data.pop("asset") # set context by first json file ctx = self._context.data - ctx["asset"] = ctx.get("asset") or data.get("asset") + ctx["folderPath"] = ctx.get("folderPath") or data.get("folderPath") ctx["intent"] = ctx.get("intent") or data.get("intent") ctx["comment"] = ctx.get("comment") or data.get("comment") ctx["user"] = ctx.get("user") or data.get("user") @@ -87,7 +92,7 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin): # basic sanity check to see if we are working in same context # if some other json file has different context, bail out. ctx_err = "inconsistent contexts in json files - %s" - assert ctx.get("asset") == data.get("asset"), ctx_err % "asset" + assert ctx.get("folderPath") == data.get("folderPath"), ctx_err % "folderPath" assert ctx.get("intent") == data.get("intent"), ctx_err % "intent" assert ctx.get("comment") == data.get("comment"), ctx_err % "comment" assert ctx.get("user") == data.get("user"), ctx_err % "user" diff --git a/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py b/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py index 0851b28134..26e448cc6e 100644 --- a/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py +++ b/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py @@ -45,7 +45,7 @@ class ExtractHierarchyToAYON(pyblish.api.ContextPlugin): continue # Skip if instance asset does not match - instance_asset_name = instance.data.get("asset") + instance_asset_name = instance.data.get("folderPath") instances_by_asset_name[instance_asset_name].append(instance) project_doc = context.data["projectEntity"] diff --git a/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py b/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py index c6bdb59f59..dd45f3fc1a 100644 --- a/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py +++ b/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py @@ -68,7 +68,7 @@ class ExtractOtioAudioTracks(pyblish.api.ContextPlugin): def add_audio_to_instances(self, audio_file, instances): created_files = [] for inst in instances: - name = inst.data["asset"] + name = inst.data["folderPath"] recycling_file = [f for f in created_files if name in f] diff --git a/client/ayon_core/plugins/publish/integrate_thumbnail.py b/client/ayon_core/plugins/publish/integrate_thumbnail.py index dd3fdd5073..03bc8d2d3d 100644 --- a/client/ayon_core/plugins/publish/integrate_thumbnail.py +++ b/client/ayon_core/plugins/publish/integrate_thumbnail.py @@ -196,12 +196,13 @@ class IntegrateThumbnailsAYON(pyblish.api.ContextPlugin): )) asset_entity = instance.data["assetEntity"] + folder_path = instance.data["folderPath"] thumbnail_info_by_entity_id[asset_entity["_id"]] = { "thumbnail_id": thumbnail_id, "entity_type": "asset", } - self.log.debug("Setting thumbnail for asset \"{}\" <{}>".format( - asset_entity["name"], version_id + self.log.debug("Setting thumbnail for folder \"{}\" <{}>".format( + folder_path, version_id )) op_session = OperationsSession() diff --git a/client/ayon_core/plugins/publish/validate_editorial_asset_name.py b/client/ayon_core/plugins/publish/validate_editorial_asset_name.py index d40263d7f3..dd1a19f602 100644 --- a/client/ayon_core/plugins/publish/validate_editorial_asset_name.py +++ b/client/ayon_core/plugins/publish/validate_editorial_asset_name.py @@ -113,7 +113,7 @@ class ValidateEditorialAssetName(pyblish.api.ContextPlugin): def get_parents(self, context): return_dict = {} for instance in context: - asset = instance.data["asset"] + asset = instance.data["folderPath"] families = instance.data.get("families", []) + [ instance.data["family"] ] diff --git a/client/ayon_core/plugins/publish/validate_unique_subsets.py b/client/ayon_core/plugins/publish/validate_unique_subsets.py index 75d12f8e01..5e0e90cff6 100644 --- a/client/ayon_core/plugins/publish/validate_unique_subsets.py +++ b/client/ayon_core/plugins/publish/validate_unique_subsets.py @@ -36,7 +36,7 @@ class ValidateSubsetUniqueness(pyblish.api.ContextPlugin): continue # Ignore instance without asset data - asset = instance.data.get("asset") + asset = instance.data.get("folderPath") if asset is None: self.log.warning("Instance found without `asset` data: " "{}".format(instance.name)) From ecba1daaa074a77fc81f21a5fc8d0f54e01657a9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 15:06:28 +0100 Subject: [PATCH 154/573] changed docstrings --- client/ayon_core/pipeline/create/README.md | 2 +- .../ayon_core/plugins/publish/collect_anatomy_instance_data.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/create/README.md b/client/ayon_core/pipeline/create/README.md index 012572a776..3858314a82 100644 --- a/client/ayon_core/pipeline/create/README.md +++ b/client/ayon_core/pipeline/create/README.md @@ -48,7 +48,7 @@ Family tells how should be instance processed and subset what name will publishe : {...}, ... }, - ## Additional data related to instance (`asset`, `task`, etc.) + ## Additional data related to instance (`folderPath`, `task`, etc.) ... } ``` diff --git a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py index 763aee5b1c..b33c438233 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py @@ -3,7 +3,7 @@ Requires: context -> anatomyData context -> projectEntity context -> assetEntity - instance -> asset + instance -> folderPath instance -> subset instance -> family From a8041e55f9daafb50a0229f70393eeb286741a39 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 15:58:18 +0100 Subject: [PATCH 155/573] moved 'usdlib.py' to pipeline --- .../houdini/plugins/publish/collect_instances_usd_layered.py | 2 +- .../hosts/houdini/plugins/publish/collect_usd_bootstrap.py | 2 +- client/ayon_core/{lib => pipeline}/usdlib.py | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename client/ayon_core/{lib => pipeline}/usdlib.py (100%) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_instances_usd_layered.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_instances_usd_layered.py index 800d6fb883..ee259fb70d 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_instances_usd_layered.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_instances_usd_layered.py @@ -2,7 +2,7 @@ import hou import pyblish.api from ayon_core.hosts.houdini.api import lib import ayon_core.hosts.houdini.api.usd as hou_usdlib -import ayon_core.lib.usdlib as usdlib +from ayon_core.pipeline import usdlib class CollectInstancesUsdLayered(pyblish.api.ContextPlugin): diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py index ed54ad8bc1..7342c8f621 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py @@ -5,7 +5,7 @@ from ayon_core.client import ( get_asset_by_name, get_asset_name_identifier, ) -import ayon_core.lib.usdlib as usdlib +from ayon_core.pipeline import usdlib class CollectUsdBootstrap(pyblish.api.InstancePlugin): diff --git a/client/ayon_core/lib/usdlib.py b/client/ayon_core/pipeline/usdlib.py similarity index 100% rename from client/ayon_core/lib/usdlib.py rename to client/ayon_core/pipeline/usdlib.py From 4ab0591047f1b6c9029adc739eb1989b4e9bc850 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 16:08:52 +0100 Subject: [PATCH 156/573] fix used key in collect current context --- client/ayon_core/plugins/publish/collect_current_context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/collect_current_context.py b/client/ayon_core/plugins/publish/collect_current_context.py index 6a22daa04a..418ca8eff6 100644 --- a/client/ayon_core/plugins/publish/collect_current_context.py +++ b/client/ayon_core/plugins/publish/collect_current_context.py @@ -44,6 +44,6 @@ class CollectCurrentContext(pyblish.api.ContextPlugin): "Task: {task_name}" ).format( project_name=context.data["projectName"], - asset_name=context.data["asset"], + asset_name=context.data["folderPath"], task_name=context.data["task"] )) From d250405dd672d75af23b349d1226156e1c20fa59 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 16:29:20 +0100 Subject: [PATCH 157/573] don't use 'get_local_settings' in experimental tools --- client/ayon_core/tools/experimental_tools/tools_def.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/experimental_tools/tools_def.py b/client/ayon_core/tools/experimental_tools/tools_def.py index be6762b239..305060978d 100644 --- a/client/ayon_core/tools/experimental_tools/tools_def.py +++ b/client/ayon_core/tools/experimental_tools/tools_def.py @@ -1,5 +1,4 @@ import os -from ayon_core.settings import get_local_settings # Constant key under which local settings are stored LOCAL_EXPERIMENTAL_KEY = "experimental_tools" @@ -151,7 +150,10 @@ class ExperimentalTools: def refresh_availability(self): """Reload local settings and check if any tool changed ability.""" - local_settings = get_local_settings() + + # NOTE AYON does not have implemented settings for experimental + # tools. + local_settings = {} experimental_settings = ( local_settings.get(LOCAL_EXPERIMENTAL_KEY) ) or {} From 21989f88dd6a1adeb3841e0f44ca33e3c855f35d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 16:29:39 +0100 Subject: [PATCH 158/573] don't use local settings in applications logic --- client/ayon_core/lib/applications.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/client/ayon_core/lib/applications.py b/client/ayon_core/lib/applications.py index 60ef2a6708..69da56a0da 100644 --- a/client/ayon_core/lib/applications.py +++ b/client/ayon_core/lib/applications.py @@ -16,7 +16,6 @@ from ayon_core.client import get_asset_name_identifier from ayon_core.settings import ( get_system_settings, get_project_settings, - get_local_settings ) from ayon_core.settings.constants import ( METADATA_KEYS, @@ -1528,16 +1527,17 @@ def prepare_app_environments( # Use environments from local settings filtered_local_envs = {} - system_settings = data["system_settings"] - whitelist_envs = system_settings["general"].get("local_env_white_list") - if whitelist_envs: - local_settings = get_local_settings() - local_envs = local_settings.get("environments") or {} - filtered_local_envs = { - key: value - for key, value in local_envs.items() - if key in whitelist_envs - } + # NOTE Overrides for environment variables are not implemented in AYON. + # system_settings = data["system_settings"] + # whitelist_envs = system_settings["general"].get("local_env_white_list") + # if whitelist_envs: + # local_settings = get_local_settings() + # local_envs = local_settings.get("environments") or {} + # filtered_local_envs = { + # key: value + # for key, value in local_envs.items() + # if key in whitelist_envs + # } # Apply local environment variables for already existing values for key, value in filtered_local_envs.items(): From f7790f14af0395f496d193796966ffa6cb6b2c72 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 16:30:01 +0100 Subject: [PATCH 159/573] do not add local settings to 'get_all_current_info' --- client/ayon_core/lib/ayon_info.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/ayon_core/lib/ayon_info.py b/client/ayon_core/lib/ayon_info.py index 97a35adcc6..ec37d735d8 100644 --- a/client/ayon_core/lib/ayon_info.py +++ b/client/ayon_core/lib/ayon_info.py @@ -5,7 +5,6 @@ import platform import getpass import socket -from ayon_core.settings.lib import get_local_settings from .execute import get_ayon_launcher_args from .local_settings import get_local_site_id @@ -96,7 +95,6 @@ def get_all_current_info(): return { "workstation": get_workstation_info(), "env": os.environ.copy(), - "local_settings": get_local_settings(), "ayon": get_ayon_info(), } From 02ecdb0ad19a399f6ee68f158c92ecce1c02757a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 16:31:20 +0100 Subject: [PATCH 160/573] don't use local settings in anatomy --- client/ayon_core/pipeline/anatomy.py | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/client/ayon_core/pipeline/anatomy.py b/client/ayon_core/pipeline/anatomy.py index 4864822aa1..e7833a9a15 100644 --- a/client/ayon_core/pipeline/anatomy.py +++ b/client/ayon_core/pipeline/anatomy.py @@ -8,9 +8,6 @@ import numbers import six import time -from ayon_core.settings.lib import ( - get_local_settings, -) from ayon_core.client import get_project, get_ayon_server_api_connection from ayon_core.lib import Logger, get_local_site_id from ayon_core.lib.path_templates import ( @@ -453,7 +450,7 @@ class Anatomy(BaseAnatomy): return cls._sync_server_addon_cache.data @classmethod - def _get_studio_roots_overrides(cls, project_name, local_settings=None): + def _get_studio_roots_overrides(cls, project_name): """This would return 'studio' site override by local settings. Notes: @@ -465,7 +462,6 @@ class Anatomy(BaseAnatomy): Args: project_name (str): Name of project. - local_settings (Optional[dict[str, Any]]): Prepared local settings. Returns: Union[Dict[str, str], None]): Local root overrides. @@ -488,11 +484,6 @@ class Anatomy(BaseAnatomy): should be returned. """ - # Local settings may be used more than once or may not be used at all - # - to avoid slowdowns 'get_local_settings' is not called until it's - # really needed - local_settings = None - # First check if sync server is available and enabled sync_server = cls.get_sync_server_addon() if sync_server is None or not sync_server.enabled: @@ -503,11 +494,8 @@ class Anatomy(BaseAnatomy): # Use sync server to receive active site name project_cache = cls._default_site_id_cache[project_name] if project_cache.is_outdated: - local_settings = get_local_settings() project_cache.update_data( - sync_server.get_active_site_type( - project_name, local_settings - ) + sync_server.get_active_site_type(project_name) ) site_name = project_cache.data @@ -517,12 +505,12 @@ class Anatomy(BaseAnatomy): # Handle studio root overrides without sync server # - studio root overrides can be done even without sync server roots_overrides = cls._get_studio_roots_overrides( - project_name, local_settings + project_name ) else: # Ask sync server to get roots overrides roots_overrides = sync_server.get_site_root_overrides( - project_name, site_name, local_settings + project_name, site_name ) site_cache.update_data(roots_overrides) return site_cache.data From 5224125b3af8310b212926fb0de98258633989f9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 16:32:15 +0100 Subject: [PATCH 161/573] removed 'get_local_settings' function --- client/ayon_core/settings/__init__.py | 2 -- client/ayon_core/settings/lib.py | 5 ----- 2 files changed, 7 deletions(-) diff --git a/client/ayon_core/settings/__init__.py b/client/ayon_core/settings/__init__.py index f0e5c95efe..074dbf8d03 100644 --- a/client/ayon_core/settings/__init__.py +++ b/client/ayon_core/settings/__init__.py @@ -7,7 +7,6 @@ from .lib import ( get_system_settings, get_project_settings, get_current_project_settings, - get_local_settings, ) from .ayon_settings import get_ayon_settings @@ -20,7 +19,6 @@ __all__ = ( "get_system_settings", "get_project_settings", "get_current_project_settings", - "get_local_settings", "get_ayon_settings", ) diff --git a/client/ayon_core/settings/lib.py b/client/ayon_core/settings/lib.py index 4dff7768f7..1508516c71 100644 --- a/client/ayon_core/settings/lib.py +++ b/client/ayon_core/settings/lib.py @@ -48,11 +48,6 @@ def clear_metadata_from_settings(values): clear_metadata_from_settings(item) -def get_local_settings(): - # TODO implement ayon implementation - return {} - - def load_openpype_default_settings(): """Load openpype default settings.""" return load_jsons_from_dir(DEFAULTS_DIR) From 4c1ac5a4f022fe1f5fd89c6f1e9402b4639e5668 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 16:32:28 +0100 Subject: [PATCH 162/573] cleared 'get_site_local_overrides' and added warning --- client/ayon_core/settings/lib.py | 30 ++++-------------------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/client/ayon_core/settings/lib.py b/client/ayon_core/settings/lib.py index 1508516c71..8e358396c4 100644 --- a/client/ayon_core/settings/lib.py +++ b/client/ayon_core/settings/lib.py @@ -198,39 +198,17 @@ def merge_overrides(source_dict, override_dict): def get_site_local_overrides(project_name, site_name, local_settings=None): """Site overrides from local settings for passet project and site name. + Warning: + This function is not implemented for AYON. + Args: project_name (str): For which project are overrides. site_name (str): For which site are overrides needed. local_settings (dict): Preloaded local settings. They are loaded automatically if not passed. """ - # Check if local settings were passed - if local_settings is None: - local_settings = get_local_settings() - output = {} - - # Skip if local settings are empty - if not local_settings: - return output - - local_project_settings = local_settings.get("projects") or {} - - # Prepare overrides for entered project and for default project - project_locals = None - if project_name: - project_locals = local_project_settings.get(project_name) - default_project_locals = local_project_settings.get(DEFAULT_PROJECT_KEY) - - # First load and use local settings from default project - if default_project_locals and site_name in default_project_locals: - output.update(default_project_locals[site_name]) - - # Apply project specific local settings if there are any - if project_locals and site_name in project_locals: - output.update(project_locals[site_name]) - - return output + return {} def get_current_project_settings(): From eb07f945a56440a5e3b5d402a56a0ee9745fe5bd Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 16:34:33 +0100 Subject: [PATCH 163/573] added comment to dirmap implementation --- client/ayon_core/host/dirmap.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/ayon_core/host/dirmap.py b/client/ayon_core/host/dirmap.py index 9756657386..effafb6261 100644 --- a/client/ayon_core/host/dirmap.py +++ b/client/ayon_core/host/dirmap.py @@ -181,6 +181,10 @@ class HostDirmap(object): exclude_locals=False, cached=False) + # TODO implement + # Dirmap is dependent on 'get_site_local_overrides' which + # is not implemented in AYON. The mapping should be received + # from sitesync addon. active_overrides = get_site_local_overrides( project_name, active_site) remote_overrides = get_site_local_overrides( From d10d860623c7c15f3c403016382f7e52c4d2fd56 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 16:44:30 +0100 Subject: [PATCH 164/573] reduced filtering hosts for publisher as experimental tool --- client/ayon_core/tools/experimental_tools/tools_def.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/tools/experimental_tools/tools_def.py b/client/ayon_core/tools/experimental_tools/tools_def.py index be6762b239..716a45c3a0 100644 --- a/client/ayon_core/tools/experimental_tools/tools_def.py +++ b/client/ayon_core/tools/experimental_tools/tools_def.py @@ -89,9 +89,13 @@ class ExperimentalTools: "New publisher", "Combined creation and publishing into one tool.", self._show_publisher, - hosts_filter=["blender", "maya", "nuke", "celaction", "flame", - "fusion", "harmony", "hiero", "resolve", - "tvpaint", "unreal"] + hosts_filter=[ + "celaction", + "flame", + "harmony", + "hiero", + "resolve", + ] ) ] From cd9a240296205699e1911f0049516c9d3562f910 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 16 Feb 2024 16:07:16 +0000 Subject: [PATCH 165/573] Remove redundant code --- .../plugins/publish/submit_publish_job.py | 145 ------------------ 1 file changed, 145 deletions(-) diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py index d4d04f79f6..4023846817 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py @@ -330,151 +330,6 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, self.log.debug("Skipping local instance.") return - data = instance.data.copy() - context = instance.context - self.context = context - self.anatomy = instance.context.data["anatomy"] - - asset = data.get("asset") or context.data["asset"] - subset = data.get("subset") - - start = instance.data.get("frameStart") - if start is None: - start = context.data["frameStart"] - - end = instance.data.get("frameEnd") - if end is None: - end = context.data["frameEnd"] - - handle_start = instance.data.get("handleStart") - if handle_start is None: - handle_start = context.data["handleStart"] - - handle_end = instance.data.get("handleEnd") - if handle_end is None: - handle_end = context.data["handleEnd"] - - fps = instance.data.get("fps") - if fps is None: - fps = context.data["fps"] - - if data.get("extendFrames", False): - start, end = self._extend_frames( - asset, - subset, - start, - end, - data["overrideExistingFrame"]) - - try: - source = data["source"] - except KeyError: - source = context.data["currentFile"] - - success, rootless_path = ( - self.anatomy.find_root_template_from_path(source) - ) - if success: - source = rootless_path - - else: - # `rootless_path` is not set to `source` if none of roots match - self.log.warning(( - "Could not find root path for remapping \"{}\"." - " This may cause issues." - ).format(source)) - - family = "render" - if ("prerender" in instance.data["families"] or - "prerender.farm" in instance.data["families"]): - family = "prerender" - families = [family] - - # pass review to families if marked as review - do_not_add_review = False - if data.get("review"): - families.append("review") - elif data.get("review") is False: - self.log.debug("Instance has review explicitly disabled.") - do_not_add_review = True - - instance_skeleton_data = { - "family": family, - "subset": subset, - "families": families, - "asset": asset, - "frameStart": start, - "frameEnd": end, - "handleStart": handle_start, - "handleEnd": handle_end, - "frameStartHandle": start - handle_start, - "frameEndHandle": end + handle_end, - "comment": instance.data["comment"], - "fps": fps, - "source": source, - "extendFrames": data.get("extendFrames"), - "overrideExistingFrame": data.get("overrideExistingFrame"), - "pixelAspect": data.get("pixelAspect", 1), - "resolutionWidth": data.get("resolutionWidth", 1920), - "resolutionHeight": data.get("resolutionHeight", 1080), - "multipartExr": data.get("multipartExr", False), - "jobBatchName": data.get("jobBatchName", ""), - "useSequenceForReview": data.get("useSequenceForReview", True), - # map inputVersions `ObjectId` -> `str` so json supports it - "inputVersions": list(map(str, data.get("inputVersions", []))), - "colorspace": instance.data.get("colorspace"), - "stagingDir_persistent": instance.data.get( - "stagingDir_persistent", False - ) - } - - # skip locking version if we are creating v01 - instance_version = instance.data.get("version") # take this if exists - if instance_version != 1: - instance_skeleton_data["version"] = instance_version - - # transfer specific families from original instance to new render - for item in self.families_transfer: - if item in instance.data.get("families", []): - instance_skeleton_data["families"] += [item] - - # transfer specific properties from original instance based on - # mapping dictionary `instance_transfer` - for key, values in self.instance_transfer.items(): - if key in instance.data.get("families", []): - for v in values: - instance_skeleton_data[v] = instance.data.get(v) - - # look into instance data if representations are not having any - # which are having tag `publish_on_farm` and include them - for repre in instance.data.get("representations", []): - staging_dir = repre.get("stagingDir") - if staging_dir: - success, rootless_staging_dir = ( - self.anatomy.find_root_template_from_path( - staging_dir - ) - ) - if success: - repre["stagingDir"] = rootless_staging_dir - else: - self.log.warning(( - "Could not find root path for remapping \"{}\"." - " This may cause issues on farm." - ).format(staging_dir)) - repre["stagingDir"] = staging_dir - - if "publish_on_farm" in repre.get("tags"): - # create representations attribute of not there - if "representations" not in instance_skeleton_data.keys(): - instance_skeleton_data["representations"] = [] - - instance_skeleton_data["representations"].append(repre) - - instances = None - assert data.get("expectedFiles"), ("Submission from old Pype version" - " - missing expectedFiles") - anatomy = instance.context.data["anatomy"] instance_skeleton_data = create_skeleton_instance( From f5fb7308e0cafdb3f5d9af538cf9bb93cf1707f1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 17:13:21 +0100 Subject: [PATCH 166/573] use 'folderPath' in hosts --- .../hosts/aftereffects/plugins/publish/collect_render.py | 6 +++--- .../hosts/aftereffects/plugins/publish/collect_workfile.py | 4 ++-- .../plugins/publish/validate_instance_asset.py | 4 ++-- .../hosts/blender/plugins/create/create_action.py | 4 +++- .../plugins/publish/collect_celaction_instances.py | 2 +- .../hosts/flame/plugins/publish/collect_timeline_otio.py | 4 ++-- .../flame/plugins/publish/extract_subset_resources.py | 4 ++-- .../hosts/flame/plugins/publish/integrate_batch_group.py | 4 ++-- .../hosts/fusion/plugins/publish/collect_render.py | 2 +- .../hosts/fusion/plugins/publish/extract_render_local.py | 2 +- .../fusion/plugins/publish/validate_unique_subsets.py | 4 +++- .../hosts/harmony/plugins/publish/collect_farm_render.py | 4 ++-- .../hosts/harmony/plugins/publish/collect_palettes.py | 4 ++-- .../hosts/harmony/plugins/publish/collect_workfile.py | 2 +- .../hosts/harmony/plugins/publish/validate_instances.py | 5 +++-- .../hosts/hiero/plugins/publish/collect_clip_effects.py | 4 ++-- .../hiero/plugins/publish/collect_frame_tag_instances.py | 6 +++--- .../hosts/hiero/plugins/publish/extract_clip_effects.py | 2 +- .../hosts/hiero/plugins/publish/precollect_instances.py | 7 +++---- .../hosts/hiero/plugins/publish/precollect_workfile.py | 2 +- .../hosts/houdini/plugins/publish/collect_instances.py | 2 +- .../ayon_core/hosts/max/plugins/publish/collect_render.py | 2 +- .../hosts/max/plugins/publish/collect_workfile.py | 2 +- client/ayon_core/hosts/maya/api/action.py | 4 ++-- .../hosts/maya/plugins/publish/validate_model_name.py | 4 ++-- .../hosts/nuke/plugins/publish/validate_asset_context.py | 6 +++--- client/ayon_core/hosts/photoshop/api/ws_stub.py | 4 ++-- .../hosts/photoshop/plugins/publish/collect_auto_image.py | 4 ++-- .../hosts/photoshop/plugins/publish/collect_auto_review.py | 4 ++-- .../photoshop/plugins/publish/collect_auto_workfile.py | 4 ++-- .../hosts/photoshop/plugins/publish/collect_batch_data.py | 4 ++-- .../plugins/publish/collect_color_coded_instances.py | 4 ++-- .../photoshop/plugins/publish/validate_instance_asset.py | 4 ++-- client/ayon_core/hosts/resolve/api/lib.py | 2 +- .../hosts/resolve/plugins/publish/precollect_instances.py | 6 +++--- .../hosts/resolve/plugins/publish/precollect_workfile.py | 2 +- .../plugins/publish/collect_textureset_images.py | 4 ++-- .../hosts/tvpaint/plugins/publish/collect_workfile_data.py | 4 ++-- .../hosts/tvpaint/plugins/publish/validate_asset_name.py | 6 +++--- .../unreal/plugins/publish/collect_render_instances.py | 2 +- 40 files changed, 77 insertions(+), 73 deletions(-) diff --git a/client/ayon_core/hosts/aftereffects/plugins/publish/collect_render.py b/client/ayon_core/hosts/aftereffects/plugins/publish/collect_render.py index a8a316ea80..32e3b4f3c3 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/publish/collect_render.py +++ b/client/ayon_core/hosts/aftereffects/plugins/publish/collect_render.py @@ -98,7 +98,7 @@ class CollectAERender(publish.AbstractCollectRender): source=current_file, label="{} - {}".format(subset_name, family), subset=subset_name, - asset=inst.data["asset"], + folderPath=inst.data["folderPath"], task=task_name, attachTo=False, setMembers='', @@ -175,7 +175,7 @@ class CollectAERender(publish.AbstractCollectRender): version_str = "v{:03d}".format(render_instance.version) if "#" not in file_name: # single frame (mov)W path = os.path.join(base_dir, "{}_{}_{}.{}".format( - render_instance.asset, + render_instance.folderPath, render_instance.subset, version_str, ext @@ -184,7 +184,7 @@ class CollectAERender(publish.AbstractCollectRender): else: for frame in range(start, end + 1): path = os.path.join(base_dir, "{}_{}_{}.{}.{}".format( - render_instance.asset, + render_instance.folderPath, render_instance.subset, version_str, str(frame).zfill(self.padding_width), diff --git a/client/ayon_core/hosts/aftereffects/plugins/publish/collect_workfile.py b/client/ayon_core/hosts/aftereffects/plugins/publish/collect_workfile.py index 538d646ab4..107643f56c 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/publish/collect_workfile.py +++ b/client/ayon_core/hosts/aftereffects/plugins/publish/collect_workfile.py @@ -50,11 +50,11 @@ class CollectWorkfile(pyblish.api.ContextPlugin): asset_entity = context.data["assetEntity"] project_entity = context.data["projectEntity"] - asset_name = get_asset_name_identifier(asset_entity) + folder_path = get_asset_name_identifier(asset_entity) instance_data = { "active": True, - "asset": asset_name, + "folderPath": folder_path, "task": task, "frameStart": context.data['frameStart'], "frameEnd": context.data['frameEnd'], diff --git a/client/ayon_core/hosts/aftereffects/plugins/publish/validate_instance_asset.py b/client/ayon_core/hosts/aftereffects/plugins/publish/validate_instance_asset.py index c3938ecbda..e8f2e29a2f 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/publish/validate_instance_asset.py +++ b/client/ayon_core/hosts/aftereffects/plugins/publish/validate_instance_asset.py @@ -30,7 +30,7 @@ class ValidateInstanceAssetRepair(pyblish.api.Action): for instance in instances: data = stub.read(instance[0]) - data["asset"] = get_current_asset_name() + data["folderPath"] = get_current_asset_name() stub.imprint(instance[0].instance_id, data) @@ -53,7 +53,7 @@ class ValidateInstanceAsset(pyblish.api.InstancePlugin): order = ValidateContentsOrder def process(self, instance): - instance_asset = instance.data["asset"] + instance_asset = instance.data["folderPath"] current_asset = get_current_asset_name() msg = ( f"Instance asset {instance_asset} is not the same " diff --git a/client/ayon_core/hosts/blender/plugins/create/create_action.py b/client/ayon_core/hosts/blender/plugins/create/create_action.py index 2331daf7b7..82047fb5c6 100644 --- a/client/ayon_core/hosts/blender/plugins/create/create_action.py +++ b/client/ayon_core/hosts/blender/plugins/create/create_action.py @@ -22,7 +22,9 @@ class CreateAction(plugin.BaseCreator): ) # Get instance name - name = plugin.prepare_scene_name(instance_data["asset"], subset_name) + name = plugin.prepare_scene_name( + instance_data["folderPath"], subset_name + ) if pre_create_data.get("use_selection"): for obj in lib.get_selection(): diff --git a/client/ayon_core/hosts/celaction/plugins/publish/collect_celaction_instances.py b/client/ayon_core/hosts/celaction/plugins/publish/collect_celaction_instances.py index d0f4c59290..ef471dbd05 100644 --- a/client/ayon_core/hosts/celaction/plugins/publish/collect_celaction_instances.py +++ b/client/ayon_core/hosts/celaction/plugins/publish/collect_celaction_instances.py @@ -22,7 +22,7 @@ class CollectCelactionInstances(pyblish.api.ContextPlugin): asset_name = get_asset_name_identifier(asset_entity) shared_instance_data = { - "asset": asset_name, + "folderPath": asset_name, "frameStart": asset_entity["data"]["frameStart"], "frameEnd": asset_entity["data"]["frameEnd"], "handleStart": asset_entity["data"]["handleStart"], diff --git a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_otio.py b/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_otio.py index 6a3e99aa55..2fcfb55e7c 100644 --- a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_otio.py +++ b/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_otio.py @@ -34,7 +34,7 @@ class CollecTimelineOTIO(pyblish.api.ContextPlugin): project_settings=context.data["project_settings"] ) - asset_name = get_asset_name_identifier(asset_doc) + folder_path = get_asset_name_identifier(asset_doc) # adding otio timeline to context with opfapi.maintained_segment_selection(sequence) as selected_seg: @@ -42,7 +42,7 @@ class CollecTimelineOTIO(pyblish.api.ContextPlugin): instance_data = { "name": subset_name, - "asset": asset_name, + "folderPath": folder_path, "subset": subset_name, "family": "workfile", "families": [] diff --git a/client/ayon_core/hosts/flame/plugins/publish/extract_subset_resources.py b/client/ayon_core/hosts/flame/plugins/publish/extract_subset_resources.py index 9e55dbce96..cae08cd76b 100644 --- a/client/ayon_core/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/client/ayon_core/hosts/flame/plugins/publish/extract_subset_resources.py @@ -55,7 +55,7 @@ class ExtractProductResources(publish.Extractor): # flame objects segment = instance.data["item"] - asset_name = instance.data["asset"] + folder_path = instance.data["folderPath"] segment_name = segment.name.get_value() clip_path = instance.data["path"] sequence_clip = instance.context.data["flameSequence"] @@ -249,7 +249,7 @@ class ExtractProductResources(publish.Extractor): out_mark = in_mark + source_duration_handles exporting_clip = self.import_clip(clip_path) exporting_clip.name.set_value("{}_{}".format( - asset_name, segment_name)) + folder_path, segment_name)) # add xml tags modifications modify_xml_data.update({ diff --git a/client/ayon_core/hosts/flame/plugins/publish/integrate_batch_group.py b/client/ayon_core/hosts/flame/plugins/publish/integrate_batch_group.py index 3458bd3002..e36d2a22d5 100644 --- a/client/ayon_core/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/client/ayon_core/hosts/flame/plugins/publish/integrate_batch_group.py @@ -168,10 +168,10 @@ class IntegrateBatchGroup(pyblish.api.InstancePlugin): handle_start = instance.data["handleStart"] handle_end = instance.data["handleEnd"] frame_duration = (frame_end - frame_start) + 1 - asset_name = instance.data["asset"] + folder_path = instance.data["folderPath"] task_name = task_data["name"] - batchgroup_name = "{}_{}".format(asset_name, task_name) + batchgroup_name = "{}_{}".format(folder_path, task_name) batch_data = { "shematic_reels": [ diff --git a/client/ayon_core/hosts/fusion/plugins/publish/collect_render.py b/client/ayon_core/hosts/fusion/plugins/publish/collect_render.py index f8870da1c5..0a0e4b38af 100644 --- a/client/ayon_core/hosts/fusion/plugins/publish/collect_render.py +++ b/client/ayon_core/hosts/fusion/plugins/publish/collect_render.py @@ -68,7 +68,7 @@ class CollectFusionRender( source=current_file, label=inst.data["label"], subset=subset_name, - asset=inst.data["asset"], + folderPath=inst.data["folderPath"], task=task_name, attachTo=False, setMembers='', diff --git a/client/ayon_core/hosts/fusion/plugins/publish/extract_render_local.py b/client/ayon_core/hosts/fusion/plugins/publish/extract_render_local.py index eea232ac29..23a8cdb8a0 100644 --- a/client/ayon_core/hosts/fusion/plugins/publish/extract_render_local.py +++ b/client/ayon_core/hosts/fusion/plugins/publish/extract_render_local.py @@ -72,7 +72,7 @@ class FusionRenderLocal( self.log.info( "Rendered '{nm}' for asset '{ast}' under the task '{tsk}'".format( nm=instance.data["name"], - ast=instance.data["asset"], + ast=instance.data["folderPath"], tsk=instance.data["task"], ) ) diff --git a/client/ayon_core/hosts/fusion/plugins/publish/validate_unique_subsets.py b/client/ayon_core/hosts/fusion/plugins/publish/validate_unique_subsets.py index 619b52077e..3131400de9 100644 --- a/client/ayon_core/hosts/fusion/plugins/publish/validate_unique_subsets.py +++ b/client/ayon_core/hosts/fusion/plugins/publish/validate_unique_subsets.py @@ -21,7 +21,9 @@ class ValidateUniqueSubsets(pyblish.api.ContextPlugin): # Collect instances per subset per asset instances_per_subset_asset = defaultdict(lambda: defaultdict(list)) for instance in context: - asset = instance.data.get("asset", context.data.get("asset")) + asset = instance.data.get( + "folderPath", context.data.get("folderPath") + ) subset = instance.data.get("subset", context.data.get("subset")) instances_per_subset_asset[asset][subset].append(instance) diff --git a/client/ayon_core/hosts/harmony/plugins/publish/collect_farm_render.py b/client/ayon_core/hosts/harmony/plugins/publish/collect_farm_render.py index faeff7bddd..6a9c349185 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/collect_farm_render.py +++ b/client/ayon_core/hosts/harmony/plugins/publish/collect_farm_render.py @@ -98,7 +98,7 @@ class CollectFarmRender(publish.AbstractCollectRender): self_name = self.__class__.__name__ - asset_name = context.data["asset"] + folder_path = context.data["folderPath"] for node in context.data["allNodes"]: data = harmony.read(node) @@ -142,7 +142,7 @@ class CollectFarmRender(publish.AbstractCollectRender): source=context.data["currentFile"], label=node.split("/")[1], subset=subset_name, - asset=asset_name, + folderPath=folder_path, task=task_name, attachTo=False, setMembers=[node], diff --git a/client/ayon_core/hosts/harmony/plugins/publish/collect_palettes.py b/client/ayon_core/hosts/harmony/plugins/publish/collect_palettes.py index 9343fab86d..66b1ee6085 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/collect_palettes.py +++ b/client/ayon_core/hosts/harmony/plugins/publish/collect_palettes.py @@ -31,7 +31,7 @@ class CollectPalettes(pyblish.api.ContextPlugin): if (not any([re.search(pattern, task_name) for pattern in self.allowed_tasks])): return - asset_name = context.data["asset"] + folder_path = context.data["folderPath"] for name, id in palettes.items(): instance = context.create_instance(name) @@ -39,7 +39,7 @@ class CollectPalettes(pyblish.api.ContextPlugin): "id": id, "family": "harmony.palette", 'families': [], - "asset": asset_name, + "folderPath": folder_path, "subset": "{}{}".format("palette", name) }) self.log.info( diff --git a/client/ayon_core/hosts/harmony/plugins/publish/collect_workfile.py b/client/ayon_core/hosts/harmony/plugins/publish/collect_workfile.py index 4be2a0fc26..1ea1f15124 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/collect_workfile.py +++ b/client/ayon_core/hosts/harmony/plugins/publish/collect_workfile.py @@ -36,5 +36,5 @@ class CollectWorkfile(pyblish.api.ContextPlugin): "family": family, "families": [family], "representations": [], - "asset": context.data["asset"] + "folderPath": context.data["folderPath"] }) diff --git a/client/ayon_core/hosts/harmony/plugins/publish/validate_instances.py b/client/ayon_core/hosts/harmony/plugins/publish/validate_instances.py index a57a863d6f..fdba834de6 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/validate_instances.py +++ b/client/ayon_core/hosts/harmony/plugins/publish/validate_instances.py @@ -27,9 +27,10 @@ class ValidateInstanceRepair(pyblish.api.Action): # Apply pyblish.logic to get the instances for the plug-in instances = pyblish.api.instances_by_plugin(failed, plugin) + folder_path = get_current_asset_name() for instance in instances: data = harmony.read(instance.data["setMembers"][0]) - data["asset"] = get_current_asset_name() + data["folderPath"] = folder_path harmony.imprint(instance.data["setMembers"][0], data) @@ -42,7 +43,7 @@ class ValidateInstance(pyblish.api.InstancePlugin): order = ValidateContentsOrder def process(self, instance): - instance_asset = instance.data["asset"] + instance_asset = instance.data["folderPath"] current_asset = get_current_asset_name() msg = ( "Instance asset is not the same as current asset:" diff --git a/client/ayon_core/hosts/hiero/plugins/publish/collect_clip_effects.py b/client/ayon_core/hosts/hiero/plugins/publish/collect_clip_effects.py index d7f646ebc9..3bd5b88942 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/collect_clip_effects.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/collect_clip_effects.py @@ -118,9 +118,9 @@ class CollectClipEffects(pyblish.api.InstancePlugin): data["subset"] = name data["family"] = family data["families"] = [family] - data["name"] = data["subset"] + "_" + data["asset"] + data["name"] = data["subset"] + "_" + data["folderPath"] data["label"] = "{} - {}".format( - data['asset'], data["subset"] + data["folderPath"], data["subset"] ) data["effects"] = effects diff --git a/client/ayon_core/hosts/hiero/plugins/publish/collect_frame_tag_instances.py b/client/ayon_core/hosts/hiero/plugins/publish/collect_frame_tag_instances.py index b981d89eef..6f99e6be29 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/collect_frame_tag_instances.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/collect_frame_tag_instances.py @@ -102,7 +102,7 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin): # first collect all available subset tag frames subset_data = {} context_asset_doc = context.data["assetEntity"] - context_asset_name = get_asset_name_identifier(context_asset_doc) + context_folder_path = get_asset_name_identifier(context_asset_doc) for tag_data in sequence_tags: frame = int(tag_data["start"]) @@ -120,7 +120,7 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin): subset_data[subset] = { "frames": [frame], "format": tag_data["format"], - "asset": context_asset_name + "folderPath": context_folder_path } return subset_data @@ -133,7 +133,7 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin): "label": "{} {}".format(name, subset_data["frames"]), "family": "image", "families": ["frame"], - "asset": subset_data["asset"], + "folderPath": subset_data["folderPath"], "subset": name, "format": subset_data["format"], "frames": subset_data["frames"] diff --git a/client/ayon_core/hosts/hiero/plugins/publish/extract_clip_effects.py b/client/ayon_core/hosts/hiero/plugins/publish/extract_clip_effects.py index afff41fc74..d1edfed0d7 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/extract_clip_effects.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/extract_clip_effects.py @@ -57,7 +57,7 @@ class ExtractClipEffects(publish.Extractor): "sourceStart", "sourceStartH", "sourceEnd", "sourceEndH", "frameStart", "frameEnd", "clipIn", "clipOut", "clipInH", "clipOutH", - "asset", "version" + "folderPath", "version" ] # pass data to version diff --git a/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py b/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py index e41ca74320..4142d2f403 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py @@ -98,7 +98,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin): data.update({ "name": "{}_{}".format(asset, subset), "label": label, - "asset": asset, + "folderPath": asset, "asset_name": asset_name, "item": track_item, "families": families, @@ -189,7 +189,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin): if not hierarchy_data: return - asset = data["asset"] + asset = data["folderPath"] asset_name = data["asset_name"] # insert family into families @@ -241,7 +241,6 @@ class PrecollectInstances(pyblish.api.ContextPlugin): if not master_layer: return - asset = data.get("asset") item = data.get("item") clip_name = item.name() @@ -249,7 +248,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin): if not self.test_any_audio(item): return - asset = data["asset"] + asset = data["folferPath"] asset_name = data["asset_name"] # insert family into families diff --git a/client/ayon_core/hosts/hiero/plugins/publish/precollect_workfile.py b/client/ayon_core/hosts/hiero/plugins/publish/precollect_workfile.py index e9e2aae653..2925e723b8 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/precollect_workfile.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/precollect_workfile.py @@ -17,7 +17,7 @@ class PrecollectWorkfile(pyblish.api.ContextPlugin): order = pyblish.api.CollectorOrder - 0.491 def process(self, context): - asset = context.data["asset"] + asset = context.data["folderPath"] asset_name = asset.split("/")[-1] active_timeline = hiero.ui.activeSequence() diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_instances.py index 2780da95d9..b445397b18 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_instances.py @@ -72,7 +72,7 @@ class CollectInstances(pyblish.api.ContextPlugin): # Create nice name if the instance has a frame range. label = data.get("name", node.name()) - label += " (%s)" % data["asset"] # include asset in name + label += " (%s)" % data["folderPath"] # include folder in name instance = context.create_instance(label) diff --git a/client/ayon_core/hosts/max/plugins/publish/collect_render.py b/client/ayon_core/hosts/max/plugins/publish/collect_render.py index a97e8a154e..66226e24fa 100644 --- a/client/ayon_core/hosts/max/plugins/publish/collect_render.py +++ b/client/ayon_core/hosts/max/plugins/publish/collect_render.py @@ -94,7 +94,7 @@ class CollectRender(pyblish.api.InstancePlugin): renderer = str(renderer_class).split(":")[0] # also need to get the render dir for conversion data = { - "asset": instance.data["asset"], + "folderPath": instance.data["folderPath"], "subset": str(instance.name), "publish": True, "maxversion": str(get_max_version()), diff --git a/client/ayon_core/hosts/max/plugins/publish/collect_workfile.py b/client/ayon_core/hosts/max/plugins/publish/collect_workfile.py index 0eb4bb731e..f5d4ba475d 100644 --- a/client/ayon_core/hosts/max/plugins/publish/collect_workfile.py +++ b/client/ayon_core/hosts/max/plugins/publish/collect_workfile.py @@ -35,7 +35,7 @@ class CollectWorkfile(pyblish.api.ContextPlugin): data.update({ "subset": subset, - "asset": context.data["asset"], + "folderPath": context.data["folderPath"], "label": subset, "publish": True, "family": 'workfile', diff --git a/client/ayon_core/hosts/maya/api/action.py b/client/ayon_core/hosts/maya/api/action.py index 1edca82ee4..4beb1e3e5b 100644 --- a/client/ayon_core/hosts/maya/api/action.py +++ b/client/ayon_core/hosts/maya/api/action.py @@ -15,7 +15,7 @@ class GenerateUUIDsOnInvalidAction(pyblish.api.Action): receive new UUIDs are actually invalid. Requires: - - instance.data["asset"] + - instance.data["folderPath"] """ @@ -78,7 +78,7 @@ class GenerateUUIDsOnInvalidAction(pyblish.api.Action): # should be always available, but kept a way to query it by name. asset_doc = instance.data.get("assetEntity") if not asset_doc: - asset_name = instance.data["asset"] + asset_name = instance.data["folderPath"] project_name = instance.context.data["projectName"] self.log.info(( "Asset is not stored on instance." diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_model_name.py b/client/ayon_core/hosts/maya/plugins/publish/validate_model_name.py index cf2bbcd77c..673cfd0d29 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_model_name.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_model_name.py @@ -67,14 +67,14 @@ class ValidateModelName(pyblish.api.InstancePlugin, r = re.compile(regex) m = r.match(top_group) project_name = instance.context.data["projectName"] - current_asset_name = instance.context.data["asset"] + current_folder_path = instance.context.data["folderPath"] if m is None: cls.log.error("invalid name on: {}".format(top_group)) cls.log.error("name doesn't match regex {}".format(regex)) invalid.append(top_group) else: if "asset" in r.groupindex: - if m.group("asset") != current_asset_name: + if m.group("folderPath") != current_folder_path: cls.log.error("Invalid asset name in top level group.") return top_group if "subset" in r.groupindex: diff --git a/client/ayon_core/hosts/nuke/plugins/publish/validate_asset_context.py b/client/ayon_core/hosts/nuke/plugins/publish/validate_asset_context.py index b4814c6a00..52ef4a58d4 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/validate_asset_context.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/validate_asset_context.py @@ -23,10 +23,10 @@ class ValidateCorrectAssetContext( current asset (shot). This validator checks if this is so. It is optional so it can be disabled when needed. - Checking `asset` and `task` keys. + Checking `folderPath` and `task` keys. """ order = ValidateContentsOrder - label = "Validate asset context" + label = "Validate Folder context" hosts = ["nuke"] actions = [ RepairAction, @@ -85,7 +85,7 @@ class ValidateCorrectAssetContext( """Get invalid keys from instance data and context data.""" invalid_keys = [] - testing_keys = ["asset", "task"] + testing_keys = ["folderPath", "task"] for _key in testing_keys: if _key not in instance.data: invalid_keys.append(_key) diff --git a/client/ayon_core/hosts/photoshop/api/ws_stub.py b/client/ayon_core/hosts/photoshop/api/ws_stub.py index 42bad05f26..8f9d3a876b 100644 --- a/client/ayon_core/hosts/photoshop/api/ws_stub.py +++ b/client/ayon_core/hosts/photoshop/api/ws_stub.py @@ -120,7 +120,7 @@ class PhotoshopServerStub: "subset":"imageBG", "family":"image", "id":"pyblish.avalon.instance", - "asset":"Town", + "folderPath":"Town", "uuid": "8" }] - for created instances OR @@ -421,7 +421,7 @@ class PhotoshopServerStub: example: {"8":{"active":true,"subset":"imageBG", "family":"image","id":"pyblish.avalon.instance", - "asset":"Town"}} + "folderPath":"Town"}} 8 is layer(group) id - used for deletion, update etc. """ res = self.websocketserver.call(self.client.call('Photoshop.read')) diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image.py index 051a3da0a1..479d9139af 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image.py @@ -28,7 +28,7 @@ class CollectAutoImage(pyblish.api.ContextPlugin): task_name = context.data["task"] host_name = context.data["hostName"] asset_doc = context.data["assetEntity"] - asset_name = get_asset_name_identifier(asset_doc) + folder_path = get_asset_name_identifier(asset_doc) auto_creator = proj_settings.get( "photoshop", {}).get( @@ -86,7 +86,7 @@ class CollectAutoImage(pyblish.api.ContextPlugin): instance = context.create_instance(subset_name) instance.data["family"] = family - instance.data["asset"] = asset_name + instance.data["folderPath"] = folder_path instance.data["subset"] = subset_name instance.data["ids"] = publishable_ids instance.data["publish"] = True diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_review.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_review.py index c8d4ddf111..e31508e641 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_review.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_review.py @@ -67,7 +67,7 @@ class CollectAutoReview(pyblish.api.ContextPlugin): host_name = context.data["hostName"] asset_doc = context.data["assetEntity"] - asset_name = get_asset_name_identifier(asset_doc) + folder_path = get_asset_name_identifier(asset_doc) subset_name = get_subset_name( family, @@ -87,7 +87,7 @@ class CollectAutoReview(pyblish.api.ContextPlugin): "family": family, "families": [], "representations": [], - "asset": asset_name, + "folderPath": folder_path, "publish": self.publish }) diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_workfile.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_workfile.py index 365fd0a684..12fc31a2f2 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_workfile.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_workfile.py @@ -71,7 +71,7 @@ class CollectAutoWorkfile(pyblish.api.ContextPlugin): host_name = context.data["hostName"] asset_doc = context.data["assetEntity"] - asset_name = get_asset_name_identifier(asset_doc) + folder_path = get_asset_name_identifier(asset_doc) subset_name = get_subset_name( family, variant, @@ -91,7 +91,7 @@ class CollectAutoWorkfile(pyblish.api.ContextPlugin): "family": family, "families": [], "representations": [], - "asset": asset_name + "folderPath": folder_path }) # creating representation diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_batch_data.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_batch_data.py index 464b6e3999..a32b5f8fa5 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_batch_data.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_batch_data.py @@ -2,7 +2,7 @@ Provides: context -> Loaded batch file. - - asset + - folderPath - task (task name) - taskType - project_name @@ -71,7 +71,7 @@ class CollectBatchData(pyblish.api.ContextPlugin): os.environ["AYON_FOLDER_PATH"] = asset_name os.environ["AYON_TASK_NAME"] = task_name - context.data["asset"] = asset_name + context.data["folderPath"] = asset_name context.data["task"] = task_name context.data["taskType"] = task_type context.data["project_name"] = project_name diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_color_coded_instances.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_color_coded_instances.py index 6a09cff3c7..35538279eb 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_color_coded_instances.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_color_coded_instances.py @@ -56,7 +56,7 @@ class CollectColorCodedInstances(pyblish.api.ContextPlugin): existing_subset_names = self._get_existing_subset_names(context) # from CollectBatchData - asset_name = context.data["asset"] + asset_name = context.data["folderPath"] task_name = context.data["task"] variant = context.data["variant"] project_name = context.data["projectEntity"]["name"] @@ -163,7 +163,7 @@ class CollectColorCodedInstances(pyblish.api.ContextPlugin): instance = context.create_instance(layer.name) instance.data["family"] = family instance.data["publish"] = True - instance.data["asset"] = asset + instance.data["folderPath"] = asset instance.data["task"] = task_name instance.data["subset"] = subset instance.data["layer"] = layer diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/validate_instance_asset.py b/client/ayon_core/hosts/photoshop/plugins/publish/validate_instance_asset.py index dc0f2efd52..67a7303316 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/validate_instance_asset.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/validate_instance_asset.py @@ -31,7 +31,7 @@ class ValidateInstanceAssetRepair(pyblish.api.Action): current_asset_name = get_current_asset_name() for instance in instances: data = stub.read(instance[0]) - data["asset"] = current_asset_name + data["folderPath"] = current_asset_name stub.imprint(instance[0], data) @@ -54,7 +54,7 @@ class ValidateInstanceAsset(OptionalPyblishPluginMixin, order = ValidateContentsOrder def process(self, instance): - instance_asset = instance.data["asset"] + instance_asset = instance.data["folderPath"] current_asset = get_current_asset_name() if instance_asset != current_asset: diff --git a/client/ayon_core/hosts/resolve/api/lib.py b/client/ayon_core/hosts/resolve/api/lib.py index 2c648bb4cc..5eb88afdcb 100644 --- a/client/ayon_core/hosts/resolve/api/lib.py +++ b/client/ayon_core/hosts/resolve/api/lib.py @@ -519,7 +519,7 @@ def imprint(timeline_item, data=None): Examples: data = { - 'asset': 'sq020sh0280', + 'folderPath': 'sq020sh0280', 'family': 'render', 'subset': 'subsetMain' } diff --git a/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py b/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py index 0ae6206496..c288d1bf99 100644 --- a/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py +++ b/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py @@ -66,7 +66,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin): data.update({ "name": "{}_{}".format(asset, subset), "label": "{} {}".format(asset, subset), - "asset": asset, + "folderPath": asset, "item": timeline_item, "publish": get_publish_attribute(timeline_item), "fps": context.data["fps"], @@ -124,7 +124,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin): if not hierarchy_data: return - asset = data["asset"] + asset = data["folderPath"] subset = "shotMain" # insert family into families @@ -134,7 +134,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin): "name": "{}_{}".format(asset, subset), "label": "{} {}".format(asset, subset), "subset": subset, - "asset": asset, + "folderPath": asset, "family": family, "families": [], "publish": get_publish_attribute(timeline_item) diff --git a/client/ayon_core/hosts/resolve/plugins/publish/precollect_workfile.py b/client/ayon_core/hosts/resolve/plugins/publish/precollect_workfile.py index 5f8cf6b5d9..814b1e159f 100644 --- a/client/ayon_core/hosts/resolve/plugins/publish/precollect_workfile.py +++ b/client/ayon_core/hosts/resolve/plugins/publish/precollect_workfile.py @@ -28,7 +28,7 @@ class PrecollectWorkfile(pyblish.api.ContextPlugin): instance_data = { "name": "{}_{}".format(asset_name, subset), "label": "{} {}".format(current_asset_name, subset), - "asset": current_asset_name, + "folderPath": current_asset_name, "subset": subset, "item": project, "family": "workfile", diff --git a/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py b/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py index b8279c99cd..03e17192d2 100644 --- a/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py +++ b/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py @@ -27,8 +27,8 @@ class CollectTextureSet(pyblish.api.InstancePlugin): config = self.get_export_config(instance) asset_doc = get_asset_by_name( - project_name=instance.context.data["projectName"], - asset_name=instance.data["asset"] + instance.context.data["projectName"], + instance.data["folderPath"] ) instance.data["exportConfig"] = config diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile_data.py b/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile_data.py index a6b6f05dc9..1cf21a1fae 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile_data.py +++ b/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile_data.py @@ -65,7 +65,7 @@ class CollectWorkfileData(pyblish.api.ContextPlugin): # Collect and store current context to have reference current_context = { "project_name": context.data["projectName"], - "asset_name": context.data["asset"], + "asset_name": context.data["folderPath"], "task_name": context.data["task"] } self.log.debug("Current context is: {}".format(current_context)) @@ -105,7 +105,7 @@ class CollectWorkfileData(pyblish.api.ContextPlugin): )) # Store context asset name - context.data["asset"] = asset_name + context.data["folderPath"] = asset_name context.data["task"] = task_name self.log.info( "Context is set to Asset: \"{}\" and Task: \"{}\"".format( diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_asset_name.py b/client/ayon_core/hosts/tvpaint/plugins/publish/validate_asset_name.py index 62603a460b..927d601e34 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_asset_name.py +++ b/client/ayon_core/hosts/tvpaint/plugins/publish/validate_asset_name.py @@ -20,7 +20,7 @@ class FixAssetNames(pyblish.api.Action): on = "failed" def process(self, context, plugin): - context_asset_name = context.data["asset"] + context_asset_name = context.data["folderPath"] old_instance_items = list_instances() new_instance_items = [] for instance_item in old_instance_items: @@ -51,9 +51,9 @@ class ValidateAssetName( def process(self, context): if not self.is_active(context.data): return - context_asset_name = context.data["asset"] + context_asset_name = context.data["folderPath"] for instance in context: - asset_name = instance.data.get("asset") + asset_name = instance.data.get("folderPath") if asset_name and asset_name == context_asset_name: continue diff --git a/client/ayon_core/hosts/unreal/plugins/publish/collect_render_instances.py b/client/ayon_core/hosts/unreal/plugins/publish/collect_render_instances.py index 8641094610..8bbf5a5c62 100644 --- a/client/ayon_core/hosts/unreal/plugins/publish/collect_render_instances.py +++ b/client/ayon_core/hosts/unreal/plugins/publish/collect_render_instances.py @@ -64,7 +64,7 @@ class CollectRenderInstances(pyblish.api.InstancePlugin): new_data = new_instance.data - new_data["asset"] = seq_name + new_data["folderPath"] = seq_name new_data["setMembers"] = seq_name new_data["family"] = "render" new_data["families"] = ["render", "review"] From eaf6e0dfdb192fc2a5833631c4df29fd79347b1e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 17:14:53 +0100 Subject: [PATCH 167/573] fix collect anatomy instance data --- .../ayon_core/plugins/publish/collect_anatomy_instance_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py index b33c438233..f6326bb9e8 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py @@ -68,7 +68,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): instances_with_missing_asset_doc = collections.defaultdict(list) for instance in context: instance_asset_doc = instance.data.get("assetEntity") - _asset_name = instance.data["asset"] + _asset_name = instance.data["folderPath"] # There is possibility that assetEntity on instance is already set # which can happen in standalone publisher From 47f51af6e1207d085ca96edb8363a62afc877be0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 16 Feb 2024 17:20:08 +0100 Subject: [PATCH 168/573] fix precollect workfile in hiero --- .../hosts/hiero/plugins/publish/precollect_workfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/hiero/plugins/publish/precollect_workfile.py b/client/ayon_core/hosts/hiero/plugins/publish/precollect_workfile.py index 2925e723b8..15dd0dee26 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/precollect_workfile.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/precollect_workfile.py @@ -64,7 +64,7 @@ class PrecollectWorkfile(pyblish.api.ContextPlugin): "label": "{} - {}Main".format( asset, family), "name": "{}_{}".format(asset_name, family), - "asset": context.data["asset"], + "folderPath": context.data["folderPath"], # TODO use 'get_subset_name' "subset": "{}{}Main".format(asset_name, family.capitalize()), "item": project, From f9a70bc7699dce3e92c609cc0453e12af55fe57c Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 19 Feb 2024 09:09:16 +0000 Subject: [PATCH 169/573] Fix exposed knobs validator --- .../hosts/nuke/plugins/publish/validate_exposed_knobs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/nuke/plugins/publish/validate_exposed_knobs.py b/client/ayon_core/hosts/nuke/plugins/publish/validate_exposed_knobs.py index 9111bcdc2c..c047347481 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/validate_exposed_knobs.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/validate_exposed_knobs.py @@ -65,7 +65,7 @@ class ValidateExposedKnobs( group_node = instance.data["transientData"]["node"] nuke_settings = instance.context.data["project_settings"]["nuke"] create_settings = nuke_settings["create"][plugin] - exposed_knobs = create_settings["exposed_knobs"] + exposed_knobs = create_settings.get("exposed_knobs", []) unexposed_knobs = [] for knob in exposed_knobs: if knob not in group_node.knobs(): From 25ead9ee87ad2b7e1ec32192441aef96054a5965 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 19 Feb 2024 09:11:59 +0000 Subject: [PATCH 170/573] Fix exposed write knobs --- client/ayon_core/hosts/nuke/api/plugin.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/nuke/api/plugin.py b/client/ayon_core/hosts/nuke/api/plugin.py index 4b8ddac167..af7b6d5b2f 100644 --- a/client/ayon_core/hosts/nuke/api/plugin.py +++ b/client/ayon_core/hosts/nuke/api/plugin.py @@ -1348,7 +1348,9 @@ def _remove_old_knobs(node): def exposed_write_knobs(settings, plugin_name, instance_node): - exposed_knobs = settings["nuke"]["create"][plugin_name]["exposed_knobs"] + exposed_knobs = settings["nuke"]["create"][plugin_name].get( + "exposed_knobs", [] + ) if exposed_knobs: instance_node.addKnob(nuke.Text_Knob('', 'Write Knobs')) write_node = nuke.allNodes(group=instance_node, filter="Write")[0] From 2cbbe4b3bcbb5033fbc905691febbf719d12fff8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 19 Feb 2024 10:26:40 +0100 Subject: [PATCH 171/573] file maya collectors --- client/ayon_core/hosts/maya/plugins/publish/collect_render.py | 2 +- .../ayon_core/hosts/maya/plugins/publish/collect_vrayscene.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_render.py b/client/ayon_core/hosts/maya/plugins/publish/collect_render.py index 4ea91ccb0d..d5392fba4a 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/collect_render.py +++ b/client/ayon_core/hosts/maya/plugins/publish/collect_render.py @@ -307,7 +307,7 @@ class CollectMayaRender(pyblish.api.InstancePlugin): _instance.data["version"] = context.data["version"] # Define nice label - label = "{0} ({1})".format(layer_name, instance.data["asset"]) + label = "{0} ({1})".format(layer_name, instance.data["folderPath"]) label += " [{0}-{1}]".format( int(data["frameStartHandle"]), int(data["frameEndHandle"]) ) diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_vrayscene.py b/client/ayon_core/hosts/maya/plugins/publish/collect_vrayscene.py index db008cc2be..979f49f7fe 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/collect_vrayscene.py +++ b/client/ayon_core/hosts/maya/plugins/publish/collect_vrayscene.py @@ -99,7 +99,7 @@ class CollectVrayScene(pyblish.api.InstancePlugin): instance.data.update(data) # Define nice label - label = "{0} ({1})".format(layer_name, instance.data["asset"]) + label = "{0} ({1})".format(layer_name, instance.data["folderPath"]) label += " [{0}-{1}]".format( int(data["frameStartHandle"]), int(data["frameEndHandle"]) ) From 9c22294717ae2df2615f4c3baf28d6c3ee561c92 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 19 Feb 2024 10:28:21 +0100 Subject: [PATCH 172/573] fix another maya plugins --- .../maya/plugins/publish/extract_unreal_skeletalmesh_fbx.py | 2 +- .../maya/plugins/publish/validate_instance_in_context.py | 4 ++-- .../hosts/maya/plugins/publish/validate_shader_name.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_skeletalmesh_fbx.py b/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_skeletalmesh_fbx.py index 7b44c92194..edbb5f845e 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_skeletalmesh_fbx.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_skeletalmesh_fbx.py @@ -61,7 +61,7 @@ class ExtractUnrealSkeletalMeshFbx(publish.Extractor): # we rely on hierarchy under one root. original_parent = to_extract[0].split("|")[1] - parent_node = instance.data.get("asset") + parent_node = instance.data.get("folderPath") # this needs to be done for AYON # WARNING: since AYON supports duplicity of asset names, # this needs to be refactored throughout the pipeline. diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_instance_in_context.py b/client/ayon_core/hosts/maya/plugins/publish/validate_instance_in_context.py index c683c1b30f..43b4f06e3f 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_instance_in_context.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_instance_in_context.py @@ -37,7 +37,7 @@ class ValidateInstanceInContext(pyblish.api.InstancePlugin, if not self.is_active(instance.data): return - asset = instance.data.get("asset") + asset = instance.data.get("folderPath") context_asset = self.get_context_asset(instance) if asset != context_asset: raise PublishValidationError( @@ -74,4 +74,4 @@ class ValidateInstanceInContext(pyblish.api.InstancePlugin, @staticmethod def get_context_asset(instance): - return instance.context.data["asset"] + return instance.context.data["folderPath"] diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_shader_name.py b/client/ayon_core/hosts/maya/plugins/publish/validate_shader_name.py index cb7f975535..86ca0ca400 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_shader_name.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_shader_name.py @@ -51,7 +51,7 @@ class ValidateShaderName(pyblish.api.InstancePlugin, descendants = cmds.ls(descendants, noIntermediate=True, long=True) shapes = cmds.ls(descendants, type=["nurbsSurface", "mesh"], long=True) - asset_name = instance.data.get("asset") + asset_name = instance.data.get("folderPath") # Check the number of connected shadingEngines per shape regex_compile = re.compile(cls.regex) From 4f09e30e204d639032b869c2722e1b22af3c8f57 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 19 Feb 2024 10:42:20 +0100 Subject: [PATCH 173/573] removed debug log --- client/ayon_core/plugins/publish/extract_burnin.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_burnin.py b/client/ayon_core/plugins/publish/extract_burnin.py index 499a72bd4f..d189e36a99 100644 --- a/client/ayon_core/plugins/publish/extract_burnin.py +++ b/client/ayon_core/plugins/publish/extract_burnin.py @@ -113,9 +113,6 @@ class ExtractBurnin(publish.Extractor): ) burnin_defs = copy.deepcopy(src_burnin_defs) - self.log.debug( - "burnin_defs.keys(): {}".format(burnin_defs.keys()) - ) # Filter output definition by `burnin` represetation key repre_linked_burnins = [ From c45a4245361f7e6ff72fea57d792e04470a0a9d5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 19 Feb 2024 10:58:35 +0100 Subject: [PATCH 174/573] add docstring --- client/ayon_core/plugins/publish/extract_burnin.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/client/ayon_core/plugins/publish/extract_burnin.py b/client/ayon_core/plugins/publish/extract_burnin.py index d189e36a99..88670928f2 100644 --- a/client/ayon_core/plugins/publish/extract_burnin.py +++ b/client/ayon_core/plugins/publish/extract_burnin.py @@ -96,6 +96,17 @@ class ExtractBurnin(publish.Extractor): instance.data["representations"].remove(repre) def _get_burnins_per_representations(self, instance, src_burnin_defs): + """ + + Args: + instance (pyblish.api.Instance): Pyblish instance. + src_burnin_defs (list): Burnin definitions. + + Returns: + list[tuple[dict, list]]: List of tuples containing representation + and its burnin definitions. + + """ self.log.debug( "Filtering of representations and their burnins starts" ) From 723d7bbd7b87049f1be4c3fb87471aac09cbff9a Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 19 Feb 2024 11:58:46 +0000 Subject: [PATCH 175/573] Porting from OpenPype --- client/ayon_core/hosts/nuke/api/utils.py | 33 ++++-- .../hosts/nuke/plugins/load/load_clip.py | 107 +++++++++++++----- 2 files changed, 98 insertions(+), 42 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/utils.py b/client/ayon_core/hosts/nuke/api/utils.py index d738ba5464..0a05077a68 100644 --- a/client/ayon_core/hosts/nuke/api/utils.py +++ b/client/ayon_core/hosts/nuke/api/utils.py @@ -103,33 +103,42 @@ def colorspace_exists_on_node(node, colorspace_name): except ValueError: # knob is not available on input node return False - all_clrs = get_colorspace_list(colorspace_knob) - return colorspace_name in all_clrs + return colorspace_name in get_colorspace_list(colorspace_knob) def get_colorspace_list(colorspace_knob): """Get available colorspace profile names + Because the values returned from colorspace_knob.values() do not correspond + to the value returned from colorspace_knob.value(), and the extracted + colorspace comes from using colorspace_knob.value(), we need to iterate + through all values to get the correct value. + + A code example of the above would be: + + for count, value in enumerate(colorspace_knob.values()): + colorspace_knob.setValue(count) + print(colorspace_knob.value() in colorspace_knob.values()) + Args: colorspace_knob (nuke.Knob): nuke knob object Returns: list: list of strings names of profiles """ + original_value = colorspace_knob.value() - all_clrs = list(colorspace_knob.values()) - reduced_clrs = [] + colorspaces = [] - if not colorspace_knob.getFlag(nuke.STRIP_CASCADE_PREFIX): - return all_clrs + try: + for count, _ in enumerate(colorspace_knob.values()): + colorspace_knob.setValue(count) + colorspaces.append(colorspace_knob.value()) + finally: + colorspace_knob.setValue(original_value) - # strip colorspace with nested path - for clrs in all_clrs: - clrs = clrs.split('/')[-1] - reduced_clrs.append(clrs) - - return reduced_clrs + return colorspaces def is_headless(): diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py index 8bce2eac6e..51cf5941ea 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py @@ -101,7 +101,6 @@ class LoadClip(plugin.NukeLoader): filepath = self.filepath_from_context(context) filepath = filepath.replace("\\", "/") - self.log.debug("_ filepath: {}".format(filepath)) start_at_workfile = options.get( "start_at_workfile", self.options_defaults["start_at_workfile"]) @@ -154,8 +153,8 @@ class LoadClip(plugin.NukeLoader): with viewer_update_and_undo_stop(): read_node["file"].setValue(filepath) - used_colorspace = self._set_colorspace( - read_node, version_data, representation["data"], filepath) + self.set_colorspace_to_node( + read_node, filepath, version, representation) self._set_range_to_node(read_node, first, last, start_at_workfile) @@ -180,8 +179,6 @@ class LoadClip(plugin.NukeLoader): colorspace = representation["data"].get(key) colorspace = colorspace or version_data.get(key) data_imprint["db_colorspace"] = colorspace - if used_colorspace: - data_imprint["used_colorspace"] = used_colorspace else: value_ = context["version"]['data'].get( key, str(None)) @@ -302,8 +299,8 @@ class LoadClip(plugin.NukeLoader): # to avoid multiple undo steps for rest of process # we will switch off undo-ing with viewer_update_and_undo_stop(): - used_colorspace = self._set_colorspace( - read_node, version_data, representation["data"], filepath) + self.set_colorspace_to_node( + read_node, filepath, version_doc, representation) self._set_range_to_node(read_node, first, last, start_at_workfile) @@ -320,10 +317,6 @@ class LoadClip(plugin.NukeLoader): "author": version_data.get("author") } - # add used colorspace if found any - if used_colorspace: - updated_dict["used_colorspace"] = used_colorspace - last_version_doc = get_last_version_by_subset_id( project_name, version_doc["parent"], fields=["_id"] ) @@ -350,6 +343,36 @@ class LoadClip(plugin.NukeLoader): self.set_as_member(read_node) + def set_colorspace_to_node( + self, + read_node, + filepath, + version_doc, + representation_doc, + ): + """Set colorspace to read node. + + Sets colorspace with available names validation. + + Args: + read_node (nuke.Node): The nuke's read node + filepath (str): file path + version_doc (dict): version document + representation_doc (dict): representation document + + """ + used_colorspace = self._get_colorspace_data( + version_doc, representation_doc, filepath) + + if ( + used_colorspace + and colorspace_exists_on_node(read_node, used_colorspace) + ): + self.log.info(f"Used colorspace: {used_colorspace}") + read_node["colorspace"].setValue(used_colorspace) + else: + self.log.info("Colorspace not set...") + def remove(self, container): read_node = container["node"] assert read_node.Class() == "Read", "Must be Read" @@ -450,25 +473,49 @@ class LoadClip(plugin.NukeLoader): return self.node_name_template.format(**name_data) - def _set_colorspace(self, node, version_data, repre_data, path): - output_color = None - path = path.replace("\\", "/") - # get colorspace - colorspace = repre_data.get("colorspace") - colorspace = colorspace or version_data.get("colorspace") + def _get_colorspace_data(self, version_doc, representation_doc, filepath): + """Get colorspace data from version and representation documents + + Args: + version_doc (dict): version document + representation_doc (dict): representation document + filepath (str): file path + + Returns: + Any[str,None]: colorspace name or None + """ + # Get backward compatible colorspace key. + colorspace = representation_doc["data"].get("colorspace") + self.log.debug( + f"Colorspace from representation colorspace: {colorspace}" + ) + + # Get backward compatible version data key if colorspace is not found. + colorspace = colorspace or version_doc["data"].get("colorspace") + self.log.debug(f"Colorspace from version colorspace: {colorspace}") + + # Get colorspace from representation colorspaceData if colorspace is + # not found. + colorspace_data = representation_doc["data"].get("colorspaceData", {}) + colorspace = colorspace or colorspace_data.get("colorspace") + self.log.debug( + f"Colorspace from representation colorspaceData: {colorspace}" + ) + + print(f"Colorspace found: {colorspace}") + + # check if any filerules are not applicable + new_parsed_colorspace = get_imageio_file_rules_colorspace_from_filepath( # noqa + filepath, "nuke", get_current_project_name() + ) + self.log.debug(f"Colorspace new filerules: {new_parsed_colorspace}") # colorspace from `project_settings/nuke/imageio/regexInputs` - iio_colorspace = get_imageio_input_colorspace(path) + old_parsed_colorspace = get_imageio_input_colorspace(filepath) + self.log.debug(f"Colorspace old filerules: {old_parsed_colorspace}") - # Set colorspace defined in version data - if ( - colorspace is not None - and colorspace_exists_on_node(node, str(colorspace)) - ): - node["colorspace"].setValue(str(colorspace)) - output_color = str(colorspace) - elif iio_colorspace is not None: - node["colorspace"].setValue(iio_colorspace) - output_color = iio_colorspace - - return output_color + return ( + new_parsed_colorspace + or old_parsed_colorspace + or colorspace + ) From 17e9a0a4a62eb57111163ada47bae20c7c3195dc Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 19 Feb 2024 13:49:31 +0100 Subject: [PATCH 176/573] remove 'ObjectId' usage --- .../publish/collect_input_representations_to_versions.py | 2 -- client/ayon_core/plugins/publish/integrate.py | 2 -- client/ayon_core/tools/push_to_project/models/integrate.py | 3 --- 3 files changed, 7 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_input_representations_to_versions.py b/client/ayon_core/plugins/publish/collect_input_representations_to_versions.py index b5c9872e74..6caee1be6a 100644 --- a/client/ayon_core/plugins/publish/collect_input_representations_to_versions.py +++ b/client/ayon_core/plugins/publish/collect_input_representations_to_versions.py @@ -1,7 +1,5 @@ import pyblish.api -from bson.objectid import ObjectId - from ayon_core.client import get_representations diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index a67c837daf..a502031595 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -6,7 +6,6 @@ import datetime import clique import six -from bson.objectid import ObjectId import pyblish.api from ayon_core.client.operations import ( @@ -988,7 +987,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): """ return { - "_id": ObjectId(), "path": self.get_rootless_path(anatomy, path), "size": os.path.getsize(path), "hash": source_hash(path), diff --git a/client/ayon_core/tools/push_to_project/models/integrate.py b/client/ayon_core/tools/push_to_project/models/integrate.py index 175716cf10..3de1beb2d2 100644 --- a/client/ayon_core/tools/push_to_project/models/integrate.py +++ b/client/ayon_core/tools/push_to_project/models/integrate.py @@ -8,8 +8,6 @@ import sys import traceback import uuid -from bson.objectid import ObjectId - from ayon_core.client import ( get_project, get_assets, @@ -1080,7 +1078,6 @@ class ProjectPushItemProcess: new_repre_files = [] for (path, rootless_path) in repre_filepaths: new_repre_files.append({ - "_id": ObjectId(), "path": rootless_path, "size": os.path.getsize(path), "hash": source_hash(path), From b5cecb728cc4fcacb49b419c03f295aa69d95980 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 19 Feb 2024 15:37:31 +0100 Subject: [PATCH 177/573] fix burnin defs --- client/ayon_core/plugins/publish/extract_burnin.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_burnin.py b/client/ayon_core/plugins/publish/extract_burnin.py index 88670928f2..edba7d6d8a 100644 --- a/client/ayon_core/plugins/publish/extract_burnin.py +++ b/client/ayon_core/plugins/publish/extract_burnin.py @@ -773,10 +773,13 @@ class ExtractBurnin(publish.Extractor): continue # Burnin values + new_burnin_def = {} burnin_values = {} for key, value in tuple(burnin_def.items()): key_low = key.lower() - if key_low in self.positions and value: + if key_low not in self.positions: + new_burnin_def[key] = value + elif value: burnin_values[key_low] = value # Skip processing if burnin values are not set @@ -788,9 +791,9 @@ class ExtractBurnin(publish.Extractor): ).format(filename_suffix, str(orig_burnin_def))) continue - burnin_values["filter"] = def_filter + new_burnin_def.update(burnin_values) - filtered_burnin_defs.append(burnin_values) + filtered_burnin_defs.append(new_burnin_def) self.log.debug(( "Burnin definition \"{}\" passed first filtering." From 86c5f1cbd999e0987dbbb9e107740b239ff22fda Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 19 Feb 2024 15:51:15 +0100 Subject: [PATCH 178/573] added setting for update check interval --- client/ayon_core/tools/tray/tray.py | 34 ++++++++++++++--------------- server/settings/main.py | 5 +++++ 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/client/ayon_core/tools/tray/tray.py b/client/ayon_core/tools/tray/tray.py index c16f1513be..d09f40b7fc 100644 --- a/client/ayon_core/tools/tray/tray.py +++ b/client/ayon_core/tools/tray/tray.py @@ -17,6 +17,7 @@ from ayon_core.lib import ( is_staging_enabled, is_running_from_build, ) +from ayon_core.settings import get_ayon_settings from ayon_core.addon import ( ITrayAction, ITrayService, @@ -46,20 +47,20 @@ class TrayManager: self.log = Logger.get_logger(self.__class__.__name__) - system_settings = get_system_settings() + studio_settings = get_ayon_settings() - version_check_interval = system_settings["general"].get( - "version_check_interval" + update_check_interval = studio_settings["core"].get( + "update_check_interval" ) - if version_check_interval is None: - version_check_interval = 5 - self._version_check_interval = version_check_interval * 60 * 1000 + if update_check_interval is None: + update_check_interval = 5 + self._update_check_interval = update_check_interval * 60 * 1000 self._addons_manager = TrayAddonsManager() self.errors = [] - self._bundle_check_timer = None + self._update_check_timer = None self._outdated_dialog = None self._main_thread_timer = None @@ -137,15 +138,12 @@ class TrayManager: self._main_thread_timer = main_thread_timer - version_check_timer = QtCore.QTimer() - if self._version_check_interval > 0: - version_check_timer.timeout.connect(self._on_bundle_check_timer) - version_check_timer.setInterval(self._version_check_interval) - version_check_timer.start() - self._bundle_check_timer = version_check_timer - - # For storing missing settings dialog - self._settings_validation_dialog = None + update_check_timer = QtCore.QTimer() + if self._update_check_interval > 0: + update_check_timer.timeout.connect(self._on_update_check_timer) + update_check_timer.setInterval(self._update_check_interval) + update_check_timer.start() + self._update_check_timer = update_check_timer self.execute_in_main_thread(self._startup_validations) @@ -204,7 +202,7 @@ class TrayManager: return item - def _on_bundle_check_timer(self): + def _on_update_check_timer(self): try: bundles = ayon_api.get_bundles() user = ayon_api.get_user() @@ -298,7 +296,7 @@ class TrayManager: def _startup_validations(self): """Run possible startup validations.""" # Trigger bundle validation on start - self._bundle_check_timer.timeout.emit() + self._update_check_timer.timeout.emit() def _add_version_item(self): login_action = QtWidgets.QAction("Login", self.tray_widget) diff --git a/server/settings/main.py b/server/settings/main.py index 1bdfcefe19..28a69e182d 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -121,6 +121,11 @@ class CoreSettings(BaseSettingsModel): widget="textarea", scope=["studio"], ) + update_check_interval: int = SettingsField( + 5, + title="Update check interval (minutes)", + ge=0 + ) disk_mapping: DiskMappingModel = SettingsField( default_factory=DiskMappingModel, title="Disk mapping", From afb0a966a2539a14ca1e13db9b242ba04f578671 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 19 Feb 2024 16:33:23 +0100 Subject: [PATCH 179/573] fix integrate product group --- client/ayon_core/plugins/publish/integrate_product_group.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/integrate_product_group.py b/client/ayon_core/plugins/publish/integrate_product_group.py index f7c96893ca..82646ed733 100644 --- a/client/ayon_core/plugins/publish/integrate_product_group.py +++ b/client/ayon_core/plugins/publish/integrate_product_group.py @@ -58,7 +58,7 @@ class IntegrateProductGroup(pyblish.api.InstancePlugin): template = profile["template"] fill_pairs = prepare_template_data({ - "family": filter_criteria["families"], + "family": filter_criteria["product_types"], "task": filter_criteria["tasks"], "host": filter_criteria["hosts"], "subset": instance.data["subset"], From d0b8ea08fbaa496421a04e78d331d34b13fe3f37 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 19 Feb 2024 16:38:08 +0100 Subject: [PATCH 180/573] Add assertion for creating media pool item if not found Asserts creation of media pool item when not found for specified files. This ensures proper handling and error messaging in such cases. --- client/ayon_core/hosts/resolve/api/plugin.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/ayon_core/hosts/resolve/api/plugin.py b/client/ayon_core/hosts/resolve/api/plugin.py index ccb20f712f..bcdc30d20f 100644 --- a/client/ayon_core/hosts/resolve/api/plugin.py +++ b/client/ayon_core/hosts/resolve/api/plugin.py @@ -405,6 +405,11 @@ class ClipLoader: files, self.active_bin ) + + assert media_pool_item, AssertionError( + "Cannot create media pool item for files: `{}`".format(files) + ) + _clip_property = media_pool_item.GetClipProperty source_in = int(_clip_property("Start")) source_out = int(_clip_property("End")) From 76ffe5742b7792d01c6d6b54c1444464c5322617 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 19 Feb 2024 16:58:32 +0100 Subject: [PATCH 181/573] fix extract review --- client/ayon_core/plugins/publish/extract_review.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_review.py b/client/ayon_core/plugins/publish/extract_review.py index b0bc94c317..927c8a3538 100644 --- a/client/ayon_core/plugins/publish/extract_review.py +++ b/client/ayon_core/plugins/publish/extract_review.py @@ -1280,14 +1280,11 @@ class ExtractReview(pyblish.api.InstancePlugin): "FFprobe couldn't read resolution from input file: \"{}\"" ).format(full_input_path_single_file)) - # NOTE Setting only one of `width` or `heigth` is not allowed + # NOTE Setting only one of `width` or `height` is not allowed # - settings value can't have None but has value of 0 - output_width = ( - output_def.get("output_width") or output_width or None - ) - output_height = ( - output_def.get("output_height") or output_height or None - ) + output_width = output_def["width"] or output_width or None + output_height = output_def["height"] or output_height or None + # Force to use input resolution if output resolution was not defined # in settings. Resolution from instance is not used when # 'use_input_res' is set to 'True'. From 940e8a4cc7794ad079b6ae336149d06e689e597a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 19 Feb 2024 17:46:30 +0100 Subject: [PATCH 182/573] Change warning to deprecation and modify message --- client/ayon_core/settings/lib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/settings/lib.py b/client/ayon_core/settings/lib.py index 8e358396c4..4dde488d6c 100644 --- a/client/ayon_core/settings/lib.py +++ b/client/ayon_core/settings/lib.py @@ -198,8 +198,8 @@ def merge_overrides(source_dict, override_dict): def get_site_local_overrides(project_name, site_name, local_settings=None): """Site overrides from local settings for passet project and site name. - Warning: - This function is not implemented for AYON. + Deprecated: + This function is not implemented for AYON and will be removed. Args: project_name (str): For which project are overrides. From e382bb101d9e30acfbcfb9f5db228d1b9d8d7d46 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 19 Feb 2024 17:55:29 +0100 Subject: [PATCH 183/573] removed shader definition editor --- .../maya/api/shader_definition_editor.py | 176 ------------------ 1 file changed, 176 deletions(-) delete mode 100644 client/ayon_core/hosts/maya/api/shader_definition_editor.py diff --git a/client/ayon_core/hosts/maya/api/shader_definition_editor.py b/client/ayon_core/hosts/maya/api/shader_definition_editor.py deleted file mode 100644 index bfa531eb87..0000000000 --- a/client/ayon_core/hosts/maya/api/shader_definition_editor.py +++ /dev/null @@ -1,176 +0,0 @@ -# -*- coding: utf-8 -*- -"""Editor for shader definitions. - -Shader names are stored as simple text file over GridFS in mongodb. - -""" -import os -from qtpy import QtWidgets, QtCore, QtGui -from ayon_core.client.mongo import OpenPypeMongoConnection -from ayon_core import resources -import gridfs - - -DEFINITION_FILENAME = "{}/maya/shader_definition.txt".format( - os.getenv("AYON_PROJECT_NAME")) - - -class ShaderDefinitionsEditor(QtWidgets.QWidget): - """Widget serving as simple editor for shader name definitions.""" - - # name of the file used to store definitions - - def __init__(self, parent=None): - super(ShaderDefinitionsEditor, self).__init__(parent) - self._mongo = OpenPypeMongoConnection.get_mongo_client() - self._gridfs = gridfs.GridFS( - self._mongo[os.getenv("OPENPYPE_DATABASE_NAME")]) - self._editor = None - - self._original_content = self._read_definition_file() - - self.setObjectName("shaderDefinitionEditor") - self.setWindowTitle("OpenPype shader name definition editor") - icon = QtGui.QIcon(resources.get_ayon_icon_filepath()) - self.setWindowIcon(icon) - self.setWindowFlags(QtCore.Qt.Window) - self.setParent(parent) - self.setAttribute(QtCore.Qt.WA_DeleteOnClose) - self.resize(750, 500) - - self._setup_ui() - self._reload() - - def _setup_ui(self): - """Setup UI of Widget.""" - layout = QtWidgets.QVBoxLayout(self) - label = QtWidgets.QLabel() - label.setText("Put shader names here - one name per line:") - layout.addWidget(label) - self._editor = QtWidgets.QPlainTextEdit() - self._editor.setStyleSheet("border: none;") - layout.addWidget(self._editor) - - btn_layout = QtWidgets.QHBoxLayout() - save_btn = QtWidgets.QPushButton("Save") - save_btn.clicked.connect(self._save) - - reload_btn = QtWidgets.QPushButton("Reload") - reload_btn.clicked.connect(self._reload) - - exit_btn = QtWidgets.QPushButton("Exit") - exit_btn.clicked.connect(self._close) - - btn_layout.addWidget(reload_btn) - btn_layout.addWidget(save_btn) - btn_layout.addWidget(exit_btn) - - layout.addLayout(btn_layout) - - def _read_definition_file(self, file=None): - """Read definition file from database. - - Args: - file (gridfs.grid_file.GridOut, Optional): File to read. If not - set, new query will be issued to find it. - - Returns: - str: Content of the file or empty string if file doesn't exist. - - """ - content = "" - if not file: - file = self._gridfs.find_one( - {"filename": DEFINITION_FILENAME}) - if not file: - print(">>> [SNDE]: nothing in database yet") - return content - content = file.read() - file.close() - return content - - def _write_definition_file(self, content, force=False): - """Write content as definition to file in database. - - Before file is written, check is made if its content has not - changed. If is changed, warning is issued to user if he wants - it to overwrite. Note: GridFs doesn't allow changing file content. - You need to delete existing file and create new one. - - Args: - content (str): Content to write. - - Raises: - ContentException: If file is changed in database while - editor is running. - """ - file = self._gridfs.find_one( - {"filename": DEFINITION_FILENAME}) - if file: - content_check = self._read_definition_file(file) - if content == content_check: - print(">>> [SNDE]: content not changed") - return - if self._original_content != content_check: - if not force: - raise ContentException("Content changed") - print(">>> [SNDE]: overwriting data") - file.close() - self._gridfs.delete(file._id) - - file = self._gridfs.new_file( - filename=DEFINITION_FILENAME, - content_type='text/plain', - encoding='utf-8') - file.write(content) - file.close() - QtCore.QTimer.singleShot(200, self._reset_style) - self._editor.setStyleSheet("border: 1px solid #33AF65;") - self._original_content = content - - def _reset_style(self): - """Reset editor style back. - - Used to visually indicate save. - - """ - self._editor.setStyleSheet("border: none;") - - def _close(self): - self.hide() - - def closeEvent(self, event): - event.ignore() - self.hide() - - def _reload(self): - print(">>> [SNDE]: reloading") - self._set_content(self._read_definition_file()) - - def _save(self): - try: - self._write_definition_file(content=self._editor.toPlainText()) - except ContentException: - # content has changed meanwhile - print(">>> [SNDE]: content has changed") - self._show_overwrite_warning() - - def _set_content(self, content): - self._editor.setPlainText(content) - - def _show_overwrite_warning(self): - reply = QtWidgets.QMessageBox.question( - self, - "Warning", - ("Content you are editing was changed meanwhile in database.\n" - "Please, reload and solve the conflict."), - QtWidgets.QMessageBox.OK) - - if reply == QtWidgets.QMessageBox.OK: - # do nothing - pass - - -class ContentException(Exception): - """This is risen during save if file is changed in database.""" - pass From 0a9acb834ff1b445c5bc46e8fbf9749cfd332993 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 19 Feb 2024 17:56:59 +0100 Subject: [PATCH 184/573] modified ValidateModelName plugin --- .../plugins/publish/validate_model_name.py | 44 ++++++++----------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_model_name.py b/client/ayon_core/hosts/maya/plugins/publish/validate_model_name.py index cf2bbcd77c..0d27965971 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_model_name.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_model_name.py @@ -4,16 +4,15 @@ import os import platform import re -import gridfs import pyblish.api from maya import cmds import ayon_core.hosts.maya.api.action -from ayon_core.client.mongo import OpenPypeMongoConnection -from ayon_core.hosts.maya.api.shader_definition_editor import ( - DEFINITION_FILENAME) from ayon_core.pipeline.publish import ( - OptionalPyblishPluginMixin, PublishValidationError, ValidateContentsOrder) + OptionalPyblishPluginMixin, + PublishValidationError, + ValidateContentsOrder, +) class ValidateModelName(pyblish.api.InstancePlugin, @@ -31,13 +30,16 @@ class ValidateModelName(pyblish.api.InstancePlugin, families = ["model"] label = "Model Name" actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction] - material_file = None - database_file = DEFINITION_FILENAME + + material_file = { + "windows": None, + "darwin": None, + "linux": None, + } @classmethod def get_invalid(cls, instance): """Get invalid nodes.""" - use_db = cls.database def is_group(group_name): """Find out if supplied transform is group or not.""" @@ -98,25 +100,15 @@ class ValidateModelName(pyblish.api.InstancePlugin, # load shader list file as utf-8 shaders = [] - if not use_db: - material_file = cls.material_file[platform.system().lower()] - if material_file: - if os.path.isfile(material_file): - shader_file = open(material_file, "r") - shaders = shader_file.readlines() - shader_file.close() - else: - cls.log.error("Missing shader name definition file.") - return True + material_file = cls.material_file[platform.system().lower()] + if material_file: + if os.path.isfile(material_file): + shader_file = open(material_file, "r") + shaders = shader_file.readlines() + shader_file.close() else: - client = OpenPypeMongoConnection.get_mongo_client() - fs = gridfs.GridFS(client[os.getenv("OPENPYPE_DATABASE_NAME")]) - shader_file = fs.find_one({"filename": cls.database_file}) - if not shader_file: - cls.log.error("Missing shader name definition in database.") - return True - shaders = shader_file.read().splitlines() - shader_file.close() + cls.log.error("Missing shader name definition file.") + return True # strip line endings from list shaders = [s.rstrip() for s in shaders if s.rstrip()] From f53b12605e34ee1ca2cd91a707932eb44a95a7a0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 19 Feb 2024 17:57:12 +0100 Subject: [PATCH 185/573] removed unused function 'edit_shader_definitions' --- client/ayon_core/hosts/maya/api/commands.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/client/ayon_core/hosts/maya/api/commands.py b/client/ayon_core/hosts/maya/api/commands.py index b52d5e6c2d..f69dca97a8 100644 --- a/client/ayon_core/hosts/maya/api/commands.py +++ b/client/ayon_core/hosts/maya/api/commands.py @@ -38,25 +38,6 @@ class ToolWindows: cls._windows[tool] = window -def edit_shader_definitions(): - from qtpy import QtWidgets - from ayon_core.hosts.maya.api.shader_definition_editor import ( - ShaderDefinitionsEditor - ) - from ayon_core.tools.utils import qt_app_context - - top_level_widgets = QtWidgets.QApplication.topLevelWidgets() - main_window = next(widget for widget in top_level_widgets - if widget.objectName() == "MayaWindow") - - with qt_app_context(): - window = ToolWindows.get_window("shader_definition_editor") - if not window: - window = ShaderDefinitionsEditor(parent=main_window) - ToolWindows.set_window("shader_definition_editor", window) - window.show() - - def _resolution_from_document(doc): if not doc or "data" not in doc: print("Entered document is not valid. \"{}\"".format(str(doc))) From 73637ae363583fb2ae343982bf589d56c1339178 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 19 Feb 2024 18:06:00 +0100 Subject: [PATCH 186/573] removed plugins and script related to maya remote publish --- .../publish/collect_publishable_instances.py | 39 ------ .../submit_maya_remote_publish_deadline.py | 131 ------------------ client/ayon_core/scripts/remote_publish.py | 12 -- 3 files changed, 182 deletions(-) delete mode 100644 client/ayon_core/modules/deadline/plugins/publish/collect_publishable_instances.py delete mode 100644 client/ayon_core/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py delete mode 100644 client/ayon_core/scripts/remote_publish.py diff --git a/client/ayon_core/modules/deadline/plugins/publish/collect_publishable_instances.py b/client/ayon_core/modules/deadline/plugins/publish/collect_publishable_instances.py deleted file mode 100644 index 347da86360..0000000000 --- a/client/ayon_core/modules/deadline/plugins/publish/collect_publishable_instances.py +++ /dev/null @@ -1,39 +0,0 @@ -# -*- coding: utf-8 -*- -"""Collect instances that should be processed and published on DL. - -""" -import os - -import pyblish.api -from ayon_core.pipeline import PublishValidationError - - -class CollectDeadlinePublishableInstances(pyblish.api.InstancePlugin): - """Collect instances that should be processed and published on DL. - - Some long running publishes (not just renders) could be offloaded to DL, - this plugin compares theirs name against env variable, marks only - publishable by farm. - - Triggered only when running only in headless mode, eg on a farm. - """ - - order = pyblish.api.CollectorOrder + 0.499 - label = "Collect Deadline Publishable Instance" - targets = ["remote"] - - def process(self, instance): - self.log.debug("CollectDeadlinePublishableInstances") - publish_inst = os.environ.get("OPENPYPE_PUBLISH_SUBSET", '') - if not publish_inst: - raise PublishValidationError("OPENPYPE_PUBLISH_SUBSET env var " - "required for remote publishing") - - subset_name = instance.data["subset"] - if subset_name == publish_inst: - self.log.debug("Publish {}".format(subset_name)) - instance.data["publish"] = True - instance.data["farm"] = False - else: - self.log.debug("Skipping {}".format(subset_name)) - instance.data["publish"] = False diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py deleted file mode 100644 index 1042c44c33..0000000000 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py +++ /dev/null @@ -1,131 +0,0 @@ -import os -import attr -from datetime import datetime - -from ayon_core.pipeline import PublishXmlValidationError -from ayon_core.lib import is_in_tests -from openpype_modules.deadline import abstract_submit_deadline -from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo - -import pyblish.api - - -@attr.s -class MayaPluginInfo(object): - Build = attr.ib(default=None) # Don't force build - StrictErrorChecking = attr.ib(default=True) - - SceneFile = attr.ib(default=None) # Input scene - Version = attr.ib(default=None) # Mandatory for Deadline - ProjectPath = attr.ib(default=None) - - ScriptJob = attr.ib(default=True) - ScriptFilename = attr.ib(default=None) - - -class MayaSubmitRemotePublishDeadline( - abstract_submit_deadline.AbstractSubmitDeadline): - """Submit Maya scene to perform a local publish in Deadline. - - Publishing in Deadline can be helpful for scenes that publish very slow. - This way it can process in the background on another machine without the - Artist having to wait for the publish to finish on their local machine. - - Submission is done through the Deadline Web Service. DL then triggers - `openpype/scripts/remote_publish.py`. - - Each publishable instance creates its own full publish job. - - Different from `ProcessSubmittedJobOnFarm` which creates publish job - depending on metadata json containing context and instance data of - rendered files. - """ - - label = "Submit Scene to Deadline" - order = pyblish.api.IntegratorOrder - hosts = ["maya"] - families = ["publish.farm"] - targets = ["local"] - - def process(self, instance): - - # Ensure no errors so far - if not (all(result["success"] - for result in instance.context.data["results"])): - raise PublishXmlValidationError("Publish process has errors") - - if not instance.data["publish"]: - self.log.warning("No active instances found. " - "Skipping submission..") - return - - super(MayaSubmitRemotePublishDeadline, self).process(instance) - - def get_job_info(self): - instance = self._instance - context = instance.context - - project_name = instance.context.data["projectName"] - scene = instance.context.data["currentFile"] - scenename = os.path.basename(scene) - - job_name = "{scene} [PUBLISH]".format(scene=scenename) - batch_name = "{code} - {scene}".format(code=project_name, - scene=scenename) - - if is_in_tests(): - batch_name += datetime.now().strftime("%d%m%Y%H%M%S") - - job_info = DeadlineJobInfo(Plugin="MayaBatch") - job_info.BatchName = batch_name - job_info.Name = job_name - job_info.UserName = context.data.get("user") - job_info.Comment = context.data.get("comment", "") - - # use setting for publish job on farm, no reason to have it separately - project_settings = context.data["project_settings"] - deadline_publish_job_sett = project_settings["deadline"]["publish"]["ProcessSubmittedJobOnFarm"] # noqa - job_info.Department = deadline_publish_job_sett["deadline_department"] - job_info.ChunkSize = deadline_publish_job_sett["deadline_chunk_size"] - job_info.Priority = deadline_publish_job_sett["deadline_priority"] - job_info.Group = deadline_publish_job_sett["deadline_group"] - job_info.Pool = deadline_publish_job_sett["deadline_pool"] - - # Include critical environment variables with submission + Session - keys = [ - "FTRACK_API_USER", - "FTRACK_API_KEY", - "FTRACK_SERVER" - ] - - environment = { - key: os.environ[key] - for key in keys - if key in os.environ - } - - environment["AYON_PROJECT_NAME"] = project_name - environment["AYON_FOLDER_PATH"] = instance.context.data["asset"] - environment["AYON_TASK_NAME"] = instance.context.data["task"] - environment["AYON_APP_NAME"] = os.environ.get("AYON_APP_NAME") - environment["OPENPYPE_PUBLISH_SUBSET"] = instance.data["subset"] - environment["AYON_LOG_NO_COLORS"] = "1" - environment["AYON_USERNAME"] = instance.context.data["user"] - environment["AYON_REMOTE_PUBLISH"] = "1" - - for key, value in environment.items(): - job_info.EnvironmentKeyValue[key] = value - - def get_plugin_info(self): - # Not all hosts can import this module. - from maya import cmds - scene = self._instance.context.data["currentFile"] - - plugin_info = MayaPluginInfo() - plugin_info.SceneFile = scene - plugin_info.ScriptFilename = "{OPENPYPE_REPOS_ROOT}/openpype/scripts/remote_publish.py" # noqa - plugin_info.Version = cmds.about(version=True) - plugin_info.ProjectPath = cmds.workspace(query=True, - rootDirectory=True) - - return attr.asdict(plugin_info) diff --git a/client/ayon_core/scripts/remote_publish.py b/client/ayon_core/scripts/remote_publish.py deleted file mode 100644 index 7e7bf2493b..0000000000 --- a/client/ayon_core/scripts/remote_publish.py +++ /dev/null @@ -1,12 +0,0 @@ -try: - from ayon_core.lib import Logger - from ayon_core.pipeline.publish.lib import remote_publish -except ImportError as exc: - # Ensure Deadline fails by output an error that contains "Fatal Error:" - raise ImportError("Fatal Error: %s" % exc) - - -if __name__ == "__main__": - # Perform remote publish with thorough error checking - log = Logger.get_logger(__name__) - remote_publish(log) From 5bb00e43717c1d736875a94aad668efb183c3cfd Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 19 Feb 2024 18:06:16 +0100 Subject: [PATCH 187/573] removed 'remote_publish' function from publish lib --- client/ayon_core/pipeline/publish/lib.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 90725e6d79..7d980b4bbe 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -485,26 +485,6 @@ def filter_pyblish_plugins(plugins): plugins.remove(plugin) -def remote_publish(log): - """Loops through all plugins, logs to console. Used for tests. - - Args: - log (Logger) - """ - - # Error exit as soon as any error occurs. - error_format = "Failed {plugin.__name__}: {error}\n{error.traceback}" - - for result in pyblish.util.publish_iter(): - if not result["error"]: - continue - - error_message = error_format.format(**result) - log.error(error_message) - # 'Fatal Error: ' is because of Deadline - raise RuntimeError("Fatal Error: {}".format(error_message)) - - def get_errored_instances_from_context(context, plugin=None): """Collect failed instances from pyblish context. From 14ba402c164018fe09cab7e20772c103ddd6a78b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 20 Feb 2024 11:09:41 +0100 Subject: [PATCH 188/573] fix forgotten places --- .../hosts/houdini/plugins/publish/collect_usd_bootstrap.py | 2 +- .../hosts/houdini/plugins/publish/collect_usd_layers.py | 2 +- .../houdini/plugins/publish/validate_usd_shade_model_exists.py | 2 +- .../traypublisher/plugins/publish/collect_shot_instances.py | 2 +- .../traypublisher/plugins/publish/validate_existing_version.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py index ed54ad8bc1..0fb269516c 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py @@ -55,7 +55,7 @@ class CollectUsdBootstrap(pyblish.api.InstancePlugin): self.log.debug("Add bootstrap for: %s" % bootstrap) project_name = instance.context.data["projectName"] - asset_name = instance.data["asset"] + asset_name = instance.data["folderPath"] asset_doc = get_asset_by_name(project_name, asset_name) assert asset_doc, "Asset must exist: %s" % asset_name diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_layers.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_layers.py index e36cd875ba..70dc28e925 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_layers.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_layers.py @@ -55,7 +55,7 @@ class CollectUsdLayers(pyblish.api.InstancePlugin): layer_inst.data["families"] = [family] layer_inst.data["subset"] = "__stub__" layer_inst.data["label"] = label - layer_inst.data["asset"] = instance.data["asset"] + layer_inst.data["folderPath"] = instance.data["folderPath"] layer_inst.data["instance_node"] = instance.data["instance_node"] # include same USD ROP layer_inst.append(rop_node) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_model_exists.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_model_exists.py index 8fa20ace02..c8b9ed9bab 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_model_exists.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_model_exists.py @@ -18,7 +18,7 @@ class ValidateUSDShadeModelExists(pyblish.api.InstancePlugin): def process(self, instance): project_name = instance.context.data["projectName"] - asset_name = instance.data["asset"] + asset_name = instance.data["folderPath"] subset = instance.data["subset"] # Assume shading variation starts after a dot separator diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py index b19eb36168..67eb3d425c 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py @@ -170,7 +170,7 @@ class CollectShotInstance(pyblish.api.InstancePlugin): parents = instance.data.get('parents', []) # Split by '/' for AYON where asset is a path - asset_name = instance.data["asset"].split("/")[-1] + asset_name = instance.data["folderPath"].split("/")[-1] actual = {asset_name: in_info} for parent in reversed(parents): diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_existing_version.py b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_existing_version.py index 6a85f92ce1..b75ae674e8 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_existing_version.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_existing_version.py @@ -40,7 +40,7 @@ class ValidateExistingVersion( formatting_data = { "subset_name": subset_name, - "asset_name": instance.data["asset"], + "asset_name": instance.data["folderPath"], "version": version } raise PublishXmlValidationError( From 43485cc64abc348e51321725de577daf5627df9f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 20 Feb 2024 11:29:20 +0100 Subject: [PATCH 189/573] fix extract usd layered --- .../hosts/houdini/plugins/publish/extract_usd_layered.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py index 7160e3d282..56c335f50e 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py @@ -285,7 +285,7 @@ class ExtractUSDLayered(publish.Extractor): # to detect whether we should make this into a new publish # version. If not, skip it. asset = get_asset_by_name( - project_name, dependency.data["asset"], fields=["_id"] + project_name, dependency.data["folderPath"], fields=["_id"] ) subset = get_subset_by_name( project_name, From b938fe4220a4cd878342183309ff0fff16afdae8 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 20 Feb 2024 10:45:44 +0000 Subject: [PATCH 190/573] Remove validate model plugin --- .../plugins/publish/validate_model_name.py | 153 ------------------ 1 file changed, 153 deletions(-) delete mode 100644 client/ayon_core/hosts/maya/plugins/publish/validate_model_name.py diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_model_name.py b/client/ayon_core/hosts/maya/plugins/publish/validate_model_name.py deleted file mode 100644 index 0d27965971..0000000000 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_model_name.py +++ /dev/null @@ -1,153 +0,0 @@ -# -*- coding: utf-8 -*- -"""Validate model nodes names.""" -import os -import platform -import re - -import pyblish.api -from maya import cmds - -import ayon_core.hosts.maya.api.action -from ayon_core.pipeline.publish import ( - OptionalPyblishPluginMixin, - PublishValidationError, - ValidateContentsOrder, -) - - -class ValidateModelName(pyblish.api.InstancePlugin, - OptionalPyblishPluginMixin): - """Validate name of model - - starts with (somename)_###_(materialID)_GEO - materialID must be present in list - padding number doesn't have limit - - """ - optional = True - order = ValidateContentsOrder - hosts = ["maya"] - families = ["model"] - label = "Model Name" - actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction] - - material_file = { - "windows": None, - "darwin": None, - "linux": None, - } - - @classmethod - def get_invalid(cls, instance): - """Get invalid nodes.""" - - def is_group(group_name): - """Find out if supplied transform is group or not.""" - try: - children = cmds.listRelatives(group_name, children=True) - for child in children: - if not cmds.ls(child, transforms=True): - return False - return True - except Exception: - return False - - invalid = [] - content_instance = instance.data.get("setMembers", None) - if not content_instance: - cls.log.error("Instance has no nodes!") - return True - pass - - # validate top level group name - assemblies = cmds.ls(content_instance, assemblies=True, long=True) - if len(assemblies) != 1: - cls.log.error("Must have exactly one top group") - return assemblies or True - top_group = assemblies[0] - regex = cls.top_level_regex - r = re.compile(regex) - m = r.match(top_group) - project_name = instance.context.data["projectName"] - current_asset_name = instance.context.data["asset"] - if m is None: - cls.log.error("invalid name on: {}".format(top_group)) - cls.log.error("name doesn't match regex {}".format(regex)) - invalid.append(top_group) - else: - if "asset" in r.groupindex: - if m.group("asset") != current_asset_name: - cls.log.error("Invalid asset name in top level group.") - return top_group - if "subset" in r.groupindex: - if m.group("subset") != instance.data.get("subset"): - cls.log.error("Invalid subset name in top level group.") - return top_group - if "project" in r.groupindex: - if m.group("project") != project_name: - cls.log.error("Invalid project name in top level group.") - return top_group - - descendants = cmds.listRelatives(content_instance, - allDescendents=True, - fullPath=True) or [] - - descendants = cmds.ls(descendants, noIntermediate=True, long=True) - trns = cmds.ls(descendants, long=False, type='transform') - - # filter out groups - filtered = [node for node in trns if not is_group(node)] - - # load shader list file as utf-8 - shaders = [] - material_file = cls.material_file[platform.system().lower()] - if material_file: - if os.path.isfile(material_file): - shader_file = open(material_file, "r") - shaders = shader_file.readlines() - shader_file.close() - else: - cls.log.error("Missing shader name definition file.") - return True - - # strip line endings from list - shaders = [s.rstrip() for s in shaders if s.rstrip()] - - # compile regex for testing names - regex = cls.regex - r = re.compile(regex) - - for obj in filtered: - cls.log.debug("testing: {}".format(obj)) - m = r.match(obj) - if m is None: - cls.log.error("invalid name on: {}".format(obj)) - invalid.append(obj) - else: - # if we have shader files and shader named group is in - # regex, test this group against names in shader file - if "shader" in r.groupindex and shaders: - try: - if not m.group('shader') in shaders: - cls.log.error( - "invalid materialID on: {0} ({1})".format( - obj, m.group('shader'))) - invalid.append(obj) - except IndexError: - # shader named group doesn't match - cls.log.error( - "shader group doesn't match: {}".format(obj)) - invalid.append(obj) - - return invalid - - def process(self, instance): - """Plugin entry point.""" - if not self.is_active(instance.data): - return - - invalid = self.get_invalid(instance) - - if invalid: - raise PublishValidationError( - "Model naming is invalid. See the log.") From 1b153953113c6eb8378da508ac38f9451d63556e Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 20 Feb 2024 18:54:38 +0800 Subject: [PATCH 191/573] change on the identifier in regards to the ayon and move the collector of current file to lower priority for collecting the data --- client/ayon_core/hosts/max/plugins/create/create_workfile.py | 2 +- .../ayon_core/hosts/max/plugins/publish/collect_current_file.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/create/create_workfile.py b/client/ayon_core/hosts/max/plugins/create/create_workfile.py index ce4d8b976d..27864c28d5 100644 --- a/client/ayon_core/hosts/max/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/max/plugins/create/create_workfile.py @@ -9,7 +9,7 @@ from pymxs import runtime as rt class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): """Workfile auto-creator.""" - identifier = "io.openpype.creators.max.workfile" + identifier = "io.ayon.creators.max.workfile" label = "Workfile" family = "workfile" icon = "fa5.file" diff --git a/client/ayon_core/hosts/max/plugins/publish/collect_current_file.py b/client/ayon_core/hosts/max/plugins/publish/collect_current_file.py index 689a357c53..6f8b8dda4b 100644 --- a/client/ayon_core/hosts/max/plugins/publish/collect_current_file.py +++ b/client/ayon_core/hosts/max/plugins/publish/collect_current_file.py @@ -7,7 +7,7 @@ from pymxs import runtime as rt class CollectCurrentFile(pyblish.api.ContextPlugin): """Inject the current working file.""" - order = pyblish.api.CollectorOrder - 0.4 + order = pyblish.api.CollectorOrder - 0.5 label = "Max Current File" hosts = ['max'] From 98a0f72f0363574f9141f175907b667b53c6065a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 20 Feb 2024 13:25:13 +0100 Subject: [PATCH 192/573] define new metadata ids --- client/ayon_core/pipeline/__init__.py | 4 ++++ client/ayon_core/pipeline/constants.py | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/__init__.py b/client/ayon_core/pipeline/__init__.py index c5507b0a7b..679e9a195e 100644 --- a/client/ayon_core/pipeline/__init__.py +++ b/client/ayon_core/pipeline/__init__.py @@ -1,6 +1,8 @@ from .constants import ( AVALON_CONTAINER_ID, + AVALON_INSTANCE_ID, AYON_CONTAINER_ID, + AYON_INSTANCE_ID, HOST_WORKFILE_EXTENSIONS, ) @@ -101,7 +103,9 @@ uninstall = uninstall_host __all__ = ( "AVALON_CONTAINER_ID", + "AVALON_INSTANCE_ID", "AYON_CONTAINER_ID", + "AYON_INSTANCE_ID", "HOST_WORKFILE_EXTENSIONS", # --- Anatomy --- diff --git a/client/ayon_core/pipeline/constants.py b/client/ayon_core/pipeline/constants.py index 755a5fb380..7a08cbb3aa 100644 --- a/client/ayon_core/pipeline/constants.py +++ b/client/ayon_core/pipeline/constants.py @@ -1,5 +1,9 @@ # Metadata ID of loaded container into scene -AVALON_CONTAINER_ID = AYON_CONTAINER_ID = "pyblish.avalon.container" +AYON_CONTAINER_ID = "ayon.load.container" +AYON_INSTANCE_ID = "ayon.create.instance" +# Backwards compatibility +AVALON_CONTAINER_ID = "pyblish.avalon.container" +AVALON_INSTANCE_ID = "pyblish.avalon.instance" # TODO get extensions from host implementations HOST_WORKFILE_EXTENSIONS = { From ce6fc93d486730da3391c750742c039324f301b3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 20 Feb 2024 14:04:26 +0100 Subject: [PATCH 193/573] fix collect usd bootstrap --- .../hosts/houdini/plugins/publish/collect_usd_bootstrap.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py index c43ff4f442..791b530eed 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py @@ -95,7 +95,7 @@ class CollectUsdBootstrap(pyblish.api.InstancePlugin): new.data["optional"] = False # Copy some data from the instance for which we bootstrap - for key in ["asset"]: + for key in ["folderPath"]: new.data[key] = instance.data[key] def _subset_exists(self, project_name, instance, subset_name, asset_doc): @@ -107,7 +107,7 @@ class CollectUsdBootstrap(pyblish.api.InstancePlugin): for inst in context: if ( inst.data["subset"] == subset_name - and inst.data["asset"] == asset_doc_name + and inst.data["folderPath"] == asset_doc_name ): return True From 522e1b1095d1f152a4db95de915be45d9eba0ee0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 20 Feb 2024 14:11:10 +0100 Subject: [PATCH 194/573] fix extract hierarchy to ayon --- client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py b/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py index 26e448cc6e..7ceaf7d2ad 100644 --- a/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py +++ b/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py @@ -189,7 +189,7 @@ class ExtractHierarchyToAYON(pyblish.api.ContextPlugin): active_folder_paths = set() for instance in context: if instance.data.get("publish") is not False: - active_folder_paths.add(instance.data.get("asset")) + active_folder_paths.add(instance.data.get("folderPath")) active_folder_paths.discard(None) From 229bd07396f8f59fa70d7285aae71c50377b4fe3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 20 Feb 2024 14:11:31 +0100 Subject: [PATCH 195/573] fix 'get_last_versions_for_instances' --- client/ayon_core/pipeline/create/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/create/utils.py b/client/ayon_core/pipeline/create/utils.py index 0547c20c0a..c2655f319f 100644 --- a/client/ayon_core/pipeline/create/utils.py +++ b/client/ayon_core/pipeline/create/utils.py @@ -32,7 +32,7 @@ def get_last_versions_for_instances( subset_names_by_asset_name = collections.defaultdict(set) instances_by_hierarchy = {} for instance in instances: - asset_name = instance.data.get("asset") + asset_name = instance.data.get("folderPath") subset_name = instance.subset_name if not asset_name or not subset_name: if use_value_for_missing: From d724a666331b042d9908a1c6e4a4271348174c76 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 20 Feb 2024 14:30:20 +0100 Subject: [PATCH 196/573] use constants instead of hardcoded values --- .../hosts/aftereffects/api/pipeline.py | 6 +++++- .../ayon_core/hosts/blender/api/pipeline.py | 6 ++++-- client/ayon_core/hosts/blender/api/plugin.py | 8 ++++++-- .../publish/collect_timeline_instances.py | 5 ++++- client/ayon_core/hosts/fusion/api/plugin.py | 18 ++++++++++-------- client/ayon_core/hosts/harmony/api/README.md | 8 ++++++-- .../plugins/publish/collect_instances.py | 2 +- client/ayon_core/hosts/hiero/api/lib.py | 11 +++++++++-- client/ayon_core/hosts/hiero/api/pipeline.py | 5 ++++- .../plugins/publish/precollect_instances.py | 5 ++++- .../ayon_core/hosts/houdini/api/pipeline.py | 8 ++++++-- client/ayon_core/hosts/houdini/api/plugin.py | 9 +++++++-- .../plugins/publish/collect_instances.py | 7 +++++-- .../publish/collect_instances_usd_layered.py | 2 +- client/ayon_core/hosts/max/api/pipeline.py | 5 ++++- client/ayon_core/hosts/max/api/plugin.py | 14 ++++++++++++-- client/ayon_core/hosts/maya/api/lib.py | 19 +++++++++++++++---- client/ayon_core/hosts/maya/api/pipeline.py | 10 +++++++--- client/ayon_core/hosts/maya/api/plugin.py | 11 +++++++++-- .../maya/api/workfile_template_builder.py | 11 +++++++++-- .../maya/plugins/publish/collect_instances.py | 2 +- .../plugins/publish/extract_maya_scene_raw.py | 10 ++++++++-- client/ayon_core/hosts/nuke/api/lib.py | 10 ++++++++-- client/ayon_core/hosts/nuke/api/pipeline.py | 6 +++++- client/ayon_core/hosts/nuke/api/plugin.py | 8 ++++++-- .../nuke/plugins/create/convert_legacy.py | 5 ++++- .../ayon_core/hosts/photoshop/api/README.md | 2 +- .../ayon_core/hosts/photoshop/api/pipeline.py | 6 +++++- .../ayon_core/hosts/photoshop/api/ws_stub.py | 6 +++--- .../publish/collect_color_coded_instances.py | 2 +- .../plugins/publish/precollect_instances.py | 5 ++++- .../ayon_core/hosts/tvpaint/api/pipeline.py | 2 +- client/ayon_core/pipeline/create/README.md | 4 ++-- client/ayon_core/pipeline/create/context.py | 12 ++++++++++-- .../pipeline/create/legacy_create.py | 4 +++- .../ayon_core/tools/subsetmanager/README.md | 2 +- 36 files changed, 191 insertions(+), 65 deletions(-) diff --git a/client/ayon_core/hosts/aftereffects/api/pipeline.py b/client/ayon_core/hosts/aftereffects/api/pipeline.py index 32e064d8cb..7ed244fd1d 100644 --- a/client/ayon_core/hosts/aftereffects/api/pipeline.py +++ b/client/ayon_core/hosts/aftereffects/api/pipeline.py @@ -9,6 +9,8 @@ from ayon_core.pipeline import ( register_loader_plugin_path, register_creator_plugin_path, AVALON_CONTAINER_ID, + AVALON_INSTANCE_ID, + AYON_INSTANCE_ID, ) from ayon_core.hosts.aftereffects.api.workfile_template_builder import ( AEPlaceholderLoadPlugin, @@ -142,7 +144,9 @@ class AfterEffectsHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): layers_meta = stub.get_metadata() for instance in layers_meta: - if instance.get("id") == "pyblish.avalon.instance": + if instance.get("id") in { + AYON_INSTANCE_ID, AVALON_INSTANCE_ID + }: instances.append(instance) return instances diff --git a/client/ayon_core/hosts/blender/api/pipeline.py b/client/ayon_core/hosts/blender/api/pipeline.py index a49afeea6b..fcac285f74 100644 --- a/client/ayon_core/hosts/blender/api/pipeline.py +++ b/client/ayon_core/hosts/blender/api/pipeline.py @@ -26,6 +26,7 @@ from ayon_core.pipeline import ( deregister_loader_plugin_path, deregister_creator_plugin_path, AVALON_CONTAINER_ID, + AYON_CONTAINER_ID, ) from ayon_core.lib import ( Logger, @@ -563,8 +564,9 @@ def ls() -> Iterator: called containers. """ - for container in lib.lsattr("id", AVALON_CONTAINER_ID): - yield parse_container(container) + for id_type in {AYON_CONTAINER_ID, AVALON_CONTAINER_ID}: + for container in lib.lsattr("id", id_type): + yield parse_container(container) def publish(): diff --git a/client/ayon_core/hosts/blender/api/plugin.py b/client/ayon_core/hosts/blender/api/plugin.py index 2cd8d1f291..d72754f148 100644 --- a/client/ayon_core/hosts/blender/api/plugin.py +++ b/client/ayon_core/hosts/blender/api/plugin.py @@ -10,6 +10,8 @@ from ayon_core.pipeline import ( Creator, CreatedInstance, LoaderPlugin, + AVALON_INSTANCE_ID, + AYON_INSTANCE_ID, ) from ayon_core.lib import BoolDef @@ -193,7 +195,9 @@ class BaseCreator(Creator): if not avalon_prop: continue - if avalon_prop.get('id') != 'pyblish.avalon.instance': + if avalon_prop.get('id') not in { + AYON_INSTANCE_ID, AVALON_INSTANCE_ID + }: continue creator_id = avalon_prop.get('creator_identifier') @@ -352,7 +356,7 @@ class BaseCreator(Creator): instance_data.update( { - "id": "pyblish.avalon.instance", + "id": AVALON_INSTANCE_ID, "creator_identifier": self.identifier, "subset": subset_name, } diff --git a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_instances.py b/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_instances.py index 636cbd8031..3819537010 100644 --- a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_instances.py +++ b/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_instances.py @@ -3,6 +3,7 @@ from types import NoneType import pyblish import ayon_core.hosts.flame.api as opfapi from ayon_core.hosts.flame.otio import flame_export +from ayon_core.pipeline import AYON_INSTANCE_ID, AVALON_INSTANCE_ID from ayon_core.pipeline.editorial import ( is_overlapping_otio_ranges, get_media_range_with_retimes @@ -47,7 +48,9 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): if not marker_data: continue - if marker_data.get("id") != "pyblish.avalon.instance": + if marker_data.get("id") not in { + AYON_INSTANCE_ID, AVALON_INSTANCE_ID + }: continue self.log.debug("__ segment.name: {}".format( diff --git a/client/ayon_core/hosts/fusion/api/plugin.py b/client/ayon_core/hosts/fusion/api/plugin.py index 70392c2f5b..3bf810ca23 100644 --- a/client/ayon_core/hosts/fusion/api/plugin.py +++ b/client/ayon_core/hosts/fusion/api/plugin.py @@ -12,7 +12,9 @@ from ayon_core.lib import ( ) from ayon_core.pipeline import ( Creator, - CreatedInstance + CreatedInstance, + AVALON_INSTANCE_ID, + AYON_INSTANCE_ID, ) @@ -172,13 +174,13 @@ class GenericCreateSaver(Creator): if not isinstance(data, dict): return - required = { - "id": "pyblish.avalon.instance", - "creator_identifier": self.identifier, - } - for key, value in required.items(): - if key not in data or data[key] != value: - return + if ( + data.get("creator_identifier") != self.identifier + or data.get("id") not in { + AYON_INSTANCE_ID, AVALON_INSTANCE_ID + } + ): + return # Get active state from the actual tool state attrs = tool.GetAttrs() diff --git a/client/ayon_core/hosts/harmony/api/README.md b/client/ayon_core/hosts/harmony/api/README.md index 680a88c423..457e22fb2e 100644 --- a/client/ayon_core/hosts/harmony/api/README.md +++ b/client/ayon_core/hosts/harmony/api/README.md @@ -212,6 +212,7 @@ class CreateComposite(harmony.Creator): The creator plugin can be configured to use other node types. For example here is a write node creator: ```python +from uuid import uuid4 import ayon_core.hosts.harmony.api as harmony @@ -242,6 +243,7 @@ class CreateRender(harmony.Creator): #### Collector Plugin ```python import pyblish.api +from ayon_core.pipeline import AYON_INSTANCE_ID, AVALON_INSTANCE_ID import ayon_core.hosts.harmony.api as harmony @@ -252,7 +254,7 @@ class CollectInstances(pyblish.api.ContextPlugin): a composite node and marked with a unique identifier; Identifier: - id (str): "pyblish.avalon.instance" + id (str): "ayon.create.instance" """ label = "Instances" @@ -272,7 +274,7 @@ class CollectInstances(pyblish.api.ContextPlugin): continue # Skip containers. - if "container" in data["id"]: + if data["id"] not in {AYON_INSTANCE_ID, AVALON_INSTANCE_ID}: continue instance = context.create_instance(node.split("/")[-1]) @@ -287,6 +289,7 @@ class CollectInstances(pyblish.api.ContextPlugin): #### Extractor Plugin ```python import os +from uuid import uuid4 import pyblish.api import ayon_core.hosts.harmony.api as harmony @@ -418,6 +421,7 @@ class ExtractImage(pyblish.api.InstancePlugin): #### Loader Plugin ```python import os +from uuid import uuid4 import ayon_core.hosts.harmony.api as harmony diff --git a/client/ayon_core/hosts/harmony/plugins/publish/collect_instances.py b/client/ayon_core/hosts/harmony/plugins/publish/collect_instances.py index 3eb689aff6..9ce99a3c3d 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/collect_instances.py +++ b/client/ayon_core/hosts/harmony/plugins/publish/collect_instances.py @@ -13,7 +13,7 @@ class CollectInstances(pyblish.api.ContextPlugin): a composite node and marked with a unique identifier. Identifier: - id (str): "pyblish.avalon.instance" + id (str): "ayon.create.instance" """ label = "Instances" diff --git a/client/ayon_core/hosts/hiero/api/lib.py b/client/ayon_core/hosts/hiero/api/lib.py index 24ff76d30b..d74ff13f05 100644 --- a/client/ayon_core/hosts/hiero/api/lib.py +++ b/client/ayon_core/hosts/hiero/api/lib.py @@ -22,7 +22,12 @@ except ImportError: from ayon_core.client import get_project from ayon_core.settings import get_project_settings -from ayon_core.pipeline import Anatomy, get_current_project_name +from ayon_core.pipeline import ( + Anatomy, + get_current_project_name, + AYON_INSTANCE_ID, + AVALON_INSTANCE_ID, +) from ayon_core.pipeline.load import filter_containers from ayon_core.lib import Logger from . import tags @@ -1217,7 +1222,9 @@ def sync_clip_name_to_data_asset(track_items_list): # ignore if no data on the clip or not publish instance if not data: continue - if data.get("id") != "pyblish.avalon.instance": + if data.get("id") not in { + AYON_INSTANCE_ID, AVALON_INSTANCE_ID + }: continue # fix data if wrong name diff --git a/client/ayon_core/hosts/hiero/api/pipeline.py b/client/ayon_core/hosts/hiero/api/pipeline.py index 1897628678..a9ba2e4df3 100644 --- a/client/ayon_core/hosts/hiero/api/pipeline.py +++ b/client/ayon_core/hosts/hiero/api/pipeline.py @@ -15,6 +15,7 @@ from ayon_core.pipeline import ( deregister_creator_plugin_path, deregister_loader_plugin_path, AVALON_CONTAINER_ID, + AYON_CONTAINER_ID, ) from ayon_core.tools.utils import host_tools from . import lib, menu, events @@ -158,7 +159,9 @@ def parse_container(item, validate=True): def data_to_container(item, data): if ( not data - or data.get("id") != "pyblish.avalon.container" + or data.get("id") not in { + AYON_CONTAINER_ID, AVALON_CONTAINER_ID + } ): return diff --git a/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py b/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py index e41ca74320..6344d76cd0 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py @@ -1,5 +1,6 @@ import pyblish +from ayon_core.pipeline import AYON_INSTANCE_ID, AVALON_INSTANCE_ID from ayon_core.pipeline.editorial import is_overlapping_otio_ranges from ayon_core.hosts.hiero import api as phiero @@ -56,7 +57,9 @@ class PrecollectInstances(pyblish.api.ContextPlugin): if not tag_data: continue - if tag_data.get("id") != "pyblish.avalon.instance": + if tag_data.get("id") not in { + AYON_INSTANCE_ID, AVALON_INSTANCE_ID + }: continue # get clips subtracks and anotations diff --git a/client/ayon_core/hosts/houdini/api/pipeline.py b/client/ayon_core/hosts/houdini/api/pipeline.py index d93ea9acec..cbc94a2408 100644 --- a/client/ayon_core/hosts/houdini/api/pipeline.py +++ b/client/ayon_core/hosts/houdini/api/pipeline.py @@ -15,6 +15,7 @@ from ayon_core.pipeline import ( register_loader_plugin_path, register_inventory_action_path, AVALON_CONTAINER_ID, + AYON_CONTAINER_ID, ) from ayon_core.pipeline.load import any_outdated_containers from ayon_core.hosts.houdini import HOUDINI_HOST_DIR @@ -271,8 +272,11 @@ def parse_container(container): def ls(): containers = [] - for identifier in (AVALON_CONTAINER_ID, - "pyblish.mindbender.container"): + for identifier in ( + AYON_CONTAINER_ID, + AVALON_CONTAINER_ID, + "pyblish.mindbender.container" + ): containers += lib.lsattr("id", identifier) for container in sorted(containers, diff --git a/client/ayon_core/hosts/houdini/api/plugin.py b/client/ayon_core/hosts/houdini/api/plugin.py index e8f89bfbb4..2538766f5f 100644 --- a/client/ayon_core/hosts/houdini/api/plugin.py +++ b/client/ayon_core/hosts/houdini/api/plugin.py @@ -11,7 +11,9 @@ from ayon_core.pipeline import ( CreatorError, LegacyCreator, Creator as NewCreator, - CreatedInstance + CreatedInstance, + AYON_INSTANCE_ID, + AVALON_INSTANCE_ID, ) from ayon_core.lib import BoolDef from .lib import imprint, read, lsattr, add_self_publish_button @@ -118,7 +120,10 @@ class HoudiniCreatorBase(object): cache = dict() cache_legacy = dict() - for node in lsattr("id", "pyblish.avalon.instance"): + nodes = [] + for id_type in [AYON_INSTANCE_ID, AVALON_INSTANCE_ID]: + nodes.extend(lsattr("id", AYON_INSTANCE_ID)) + for node in nodes: creator_identifier_parm = node.parm("creator_identifier") if creator_identifier_parm: diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_instances.py index 2780da95d9..5869d201c0 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_instances.py @@ -2,6 +2,7 @@ import hou import pyblish.api +from ayon_core.pipeline import AYON_INSTANCE_ID, AVALON_INSTANCE_ID from ayon_core.hosts.houdini.api import lib @@ -12,7 +13,7 @@ class CollectInstances(pyblish.api.ContextPlugin): an specific node and marked with a unique identifier; Identifier: - id (str): "pyblish.avalon.instance + id (str): "ayon.create.instance" Specific node: The specific node is important because it dictates in which way the @@ -44,7 +45,9 @@ class CollectInstances(pyblish.api.ContextPlugin): if not node.parm("id"): continue - if node.evalParm("id") != "pyblish.avalon.instance": + if node.evalParm("id") not in { + AYON_INSTANCE_ID, AVALON_INSTANCE_ID + }: continue # instance was created by new creator code, skip it as diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_instances_usd_layered.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_instances_usd_layered.py index ee259fb70d..7a3087c268 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_instances_usd_layered.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_instances_usd_layered.py @@ -12,7 +12,7 @@ class CollectInstancesUsdLayered(pyblish.api.ContextPlugin): layers remain set to 'publish' by the user. This works differently from most of our Avalon instances in the pipeline. - As opposed to storing `pyblish.avalon.instance` as id on the node we store + As opposed to storing `ayon.create.instance` as id on the node we store `pyblish.avalon.usdlayered`. Additionally this instance has no need for storing family, asset, subset diff --git a/client/ayon_core/hosts/max/api/pipeline.py b/client/ayon_core/hosts/max/api/pipeline.py index c26e697429..46c5aeec11 100644 --- a/client/ayon_core/hosts/max/api/pipeline.py +++ b/client/ayon_core/hosts/max/api/pipeline.py @@ -12,6 +12,7 @@ from ayon_core.pipeline import ( register_creator_plugin_path, register_loader_plugin_path, AVALON_CONTAINER_ID, + AYON_CONTAINER_ID, ) from ayon_core.hosts.max.api.menu import OpenPypeMenu from ayon_core.hosts.max.api import lib @@ -151,7 +152,9 @@ def ls() -> list: objs = rt.objects containers = [ obj for obj in objs - if rt.getUserProp(obj, "id") == AVALON_CONTAINER_ID + if rt.getUserProp(obj, "id") in { + AYON_CONTAINER_ID, AVALON_CONTAINER_ID + } ] for container in sorted(containers, key=attrgetter("name")): diff --git a/client/ayon_core/hosts/max/api/plugin.py b/client/ayon_core/hosts/max/api/plugin.py index 3551450c24..18f752631d 100644 --- a/client/ayon_core/hosts/max/api/plugin.py +++ b/client/ayon_core/hosts/max/api/plugin.py @@ -6,7 +6,13 @@ import six from pymxs import runtime as rt from ayon_core.lib import BoolDef -from ayon_core.pipeline import CreatedInstance, Creator, CreatorError +from ayon_core.pipeline import ( + CreatedInstance, + Creator, + CreatorError, + AYON_INSTANCE_ID, + AVALON_INSTANCE_ID, +) from .lib import imprint, lsattr, read @@ -162,7 +168,11 @@ class MaxCreatorBase(object): return shared_data shared_data["max_cached_subsets"] = {} - cached_instances = lsattr("id", "pyblish.avalon.instance") + + cached_instances = [] + for id_type in [AYON_INSTANCE_ID, AVALON_INSTANCE_ID]: + cached_instances.extend(lsattr("id", id_type)) + for i in cached_instances: creator_id = rt.GetUserProp(i, "creator_identifier") if creator_id not in shared_data["max_cached_subsets"]: diff --git a/client/ayon_core/hosts/maya/api/lib.py b/client/ayon_core/hosts/maya/api/lib.py index 3a29fe433b..b38439144e 100644 --- a/client/ayon_core/hosts/maya/api/lib.py +++ b/client/ayon_core/hosts/maya/api/lib.py @@ -35,7 +35,11 @@ from ayon_core.pipeline import ( loaders_from_representation, get_representation_path, load_container, - registered_host + registered_host, + AVALON_CONTAINER_ID, + AVALON_INSTANCE_ID, + AYON_INSTANCE_ID, + AYON_CONTAINER_ID, ) from ayon_core.lib import NumberDef from ayon_core.pipeline.context_tools import get_current_project_asset @@ -2100,7 +2104,7 @@ def get_related_sets(node): """Return objectSets that are relationships for a look for `node`. Filters out based on: - - id attribute is NOT `pyblish.avalon.container` + - id attribute is NOT `AVALON_CONTAINER_ID` - shapes and deformer shapes (alembic creates meshShapeDeformed) - set name ends with any from a predefined list - set in not in viewport set (isolate selected for example) @@ -2120,7 +2124,12 @@ def get_related_sets(node): defaults = {"defaultLightSet", "defaultObjectSet"} # Ids to ignore - ignored = {"pyblish.avalon.instance", "pyblish.avalon.container"} + ignored = { + AVALON_INSTANCE_ID, + AVALON_CONTAINER_ID, + AYON_INSTANCE_ID, + AYON_CONTAINER_ID, + } view_sets = get_isolate_view_sets() @@ -3151,7 +3160,9 @@ def update_content_on_context_change(): new_data = asset_doc["data"] for s in scene_sets: try: - if cmds.getAttr("{}.id".format(s)) == "pyblish.avalon.instance": + if cmds.getAttr("{}.id".format(s)) in { + AYON_INSTANCE_ID, AVALON_INSTANCE_ID + }: attr = cmds.listAttr(s) print(s) if "asset" in attr: diff --git a/client/ayon_core/hosts/maya/api/pipeline.py b/client/ayon_core/hosts/maya/api/pipeline.py index e58316030e..bba9943889 100644 --- a/client/ayon_core/hosts/maya/api/pipeline.py +++ b/client/ayon_core/hosts/maya/api/pipeline.py @@ -33,6 +33,7 @@ from ayon_core.pipeline import ( deregister_loader_plugin_path, deregister_inventory_action_path, deregister_creator_plugin_path, + AYON_CONTAINER_ID, AVALON_CONTAINER_ID, ) from ayon_core.pipeline.load import any_outdated_containers @@ -376,9 +377,12 @@ def _ls(): yield iterator.thisNode() iterator.next() - ids = {AVALON_CONTAINER_ID, - # Backwards compatibility - "pyblish.mindbender.container"} + ids = { + AYON_CONTAINER_ID, + # Backwards compatibility + AVALON_CONTAINER_ID, + "pyblish.mindbender.container" + } # Iterate over all 'set' nodes in the scene to detect whether # they have the avalon container ".id" attribute. diff --git a/client/ayon_core/hosts/maya/api/plugin.py b/client/ayon_core/hosts/maya/api/plugin.py index 42c0c7051e..7a01f1a174 100644 --- a/client/ayon_core/hosts/maya/api/plugin.py +++ b/client/ayon_core/hosts/maya/api/plugin.py @@ -10,6 +10,9 @@ from maya.app.renderSetup.model import renderSetup from ayon_core.lib import BoolDef, Logger from ayon_core.settings import get_project_settings from ayon_core.pipeline import ( + AYON_INSTANCE_ID, + AYON_CONTAINER_ID, + AVALON_INSTANCE_ID, AVALON_CONTAINER_ID, Anatomy, @@ -110,7 +113,9 @@ class MayaCreatorBase(object): for node in cmds.ls(type="objectSet"): - if _get_attr(node, attr="id") != "pyblish.avalon.instance": + if _get_attr(node, attr="id") not in { + AYON_INSTANCE_ID, AVALON_INSTANCE_ID + }: continue creator_id = _get_attr(node, attr="creator_identifier") @@ -992,5 +997,7 @@ class ReferenceLoader(Loader): id_attr = "{}.id".format(node) if not cmds.attributeQuery("id", node=node, exists=True): continue - if cmds.getAttr(id_attr) == AVALON_CONTAINER_ID: + if cmds.getAttr(id_attr) not in { + AYON_CONTAINER_ID, AVALON_CONTAINER_ID + }: cmds.sets(node, forceElement=container) diff --git a/client/ayon_core/hosts/maya/api/workfile_template_builder.py b/client/ayon_core/hosts/maya/api/workfile_template_builder.py index c3c61e5444..e9db8ffe79 100644 --- a/client/ayon_core/hosts/maya/api/workfile_template_builder.py +++ b/client/ayon_core/hosts/maya/api/workfile_template_builder.py @@ -2,7 +2,12 @@ import json from maya import cmds -from ayon_core.pipeline import registered_host, get_current_asset_name +from ayon_core.pipeline import ( + registered_host, + get_current_asset_name, + AYON_INSTANCE_ID, + AVALON_INSTANCE_ID, +) from ayon_core.pipeline.workfile.workfile_template_builder import ( TemplateAlreadyImported, AbstractTemplateBuilder, @@ -73,7 +78,9 @@ class MayaTemplateBuilder(AbstractTemplateBuilder): for node in imported_sets: if not cmds.attributeQuery("id", node=node, exists=True): continue - if cmds.getAttr("{}.id".format(node)) != "pyblish.avalon.instance": + if cmds.getAttr("{}.id".format(node)) not in { + AYON_INSTANCE_ID, AVALON_INSTANCE_ID + }: continue if not cmds.attributeQuery("asset", node=node, exists=True): continue diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_instances.py b/client/ayon_core/hosts/maya/plugins/publish/collect_instances.py index 0b29851db0..2d745d0ca8 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/collect_instances.py +++ b/client/ayon_core/hosts/maya/plugins/publish/collect_instances.py @@ -11,7 +11,7 @@ class CollectNewInstances(pyblish.api.InstancePlugin): an objectSet and marked with a unique identifier; Identifier: - id (str): "pyblish.avalon.instance" + id (str): "ayon.create.instance" Limitations: - Does not take into account nodes connected to those diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_maya_scene_raw.py b/client/ayon_core/hosts/maya/plugins/publish/extract_maya_scene_raw.py index cd6f3bab6a..135185fe5c 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_maya_scene_raw.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_maya_scene_raw.py @@ -5,7 +5,11 @@ import os from maya import cmds from ayon_core.hosts.maya.api.lib import maintained_selection -from ayon_core.pipeline import AVALON_CONTAINER_ID, publish +from ayon_core.pipeline import ( + AYON_CONTAINER_ID, + AVALON_CONTAINER_ID, + publish, +) from ayon_core.pipeline.publish import AYONPyblishPluginMixin from ayon_core.lib import BoolDef @@ -136,7 +140,9 @@ class ExtractMayaSceneRaw(publish.Extractor, AYONPyblishPluginMixin): continue id_attr = "{}.id".format(obj_set) - if cmds.getAttr(id_attr) != AVALON_CONTAINER_ID: + if cmds.getAttr(id_attr) not in { + AYON_CONTAINER_ID, AVALON_CONTAINER_ID + }: continue set_content = set(cmds.sets(obj_set, query=True)) diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index c320df9361..a2ec8fdb98 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -45,6 +45,8 @@ from ayon_core.pipeline import ( get_current_host_name, get_current_project_name, get_current_asset_name, + AYON_INSTANCE_ID, + AVALON_INSTANCE_ID, ) from ayon_core.pipeline.context_tools import ( get_custom_workfile_template_from_session @@ -2300,12 +2302,16 @@ Reopening Nuke should synchronize these paths and resolve any discrepancies. # backward compatibility # TODO: remove this once old avalon data api will be removed avalon_knob_data - and avalon_knob_data.get("id") != "pyblish.avalon.instance" + and avalon_knob_data.get("id") not in { + AYON_INSTANCE_ID, AVALON_INSTANCE_ID + } ): continue elif ( node_data - and node_data.get("id") != "pyblish.avalon.instance" + and node_data.get("id") not in { + AYON_INSTANCE_ID, AVALON_INSTANCE_ID + } ): continue diff --git a/client/ayon_core/hosts/nuke/api/pipeline.py b/client/ayon_core/hosts/nuke/api/pipeline.py index 735426240e..c747996d9d 100644 --- a/client/ayon_core/hosts/nuke/api/pipeline.py +++ b/client/ayon_core/hosts/nuke/api/pipeline.py @@ -18,6 +18,8 @@ from ayon_core.pipeline import ( register_loader_plugin_path, register_creator_plugin_path, register_inventory_action_path, + AYON_INSTANCE_ID, + AVALON_INSTANCE_ID, AVALON_CONTAINER_ID, get_current_asset_name, get_current_task_name, @@ -550,7 +552,9 @@ def list_instances(creator_id=None): if not instance_data: continue - if instance_data["id"] != "pyblish.avalon.instance": + if instance_data["id"] not in { + AYON_INSTANCE_ID, AVALON_INSTANCE_ID + }: continue if creator_id and instance_data["creator_identifier"] != creator_id: diff --git a/client/ayon_core/hosts/nuke/api/plugin.py b/client/ayon_core/hosts/nuke/api/plugin.py index dc14515f2a..59042acee1 100644 --- a/client/ayon_core/hosts/nuke/api/plugin.py +++ b/client/ayon_core/hosts/nuke/api/plugin.py @@ -19,7 +19,9 @@ from ayon_core.pipeline import ( CreatorError, Creator as NewCreator, CreatedInstance, - get_current_task_name + get_current_task_name, + AYON_INSTANCE_ID, + AVALON_INSTANCE_ID, ) from ayon_core.pipeline.colorspace import ( get_display_view_colorspace_name, @@ -1265,7 +1267,9 @@ def convert_to_valid_instaces(): if not avalon_knob_data: continue - if avalon_knob_data["id"] != "pyblish.avalon.instance": + if avalon_knob_data["id"] not in { + AYON_INSTANCE_ID, AVALON_INSTANCE_ID + }: continue transfer_data.update({ diff --git a/client/ayon_core/hosts/nuke/plugins/create/convert_legacy.py b/client/ayon_core/hosts/nuke/plugins/create/convert_legacy.py index 815170ac8b..f113bec887 100644 --- a/client/ayon_core/hosts/nuke/plugins/create/convert_legacy.py +++ b/client/ayon_core/hosts/nuke/plugins/create/convert_legacy.py @@ -1,3 +1,4 @@ +from ayon_core.pipeline import AYON_INSTANCE_ID, AVALON_INSTANCE_ID from ayon_core.pipeline.create.creator_plugins import SubsetConvertorPlugin from ayon_core.hosts.nuke.api.lib import ( INSTANCE_DATA_KNOB, @@ -34,7 +35,9 @@ class LegacyConverted(SubsetConvertorPlugin): if not avalon_knob_data: continue - if avalon_knob_data["id"] != "pyblish.avalon.instance": + if avalon_knob_data["id"] not in { + AYON_INSTANCE_ID, AVALON_INSTANCE_ID + }: continue # catch and break diff --git a/client/ayon_core/hosts/photoshop/api/README.md b/client/ayon_core/hosts/photoshop/api/README.md index 72e2217829..02868753d1 100644 --- a/client/ayon_core/hosts/photoshop/api/README.md +++ b/client/ayon_core/hosts/photoshop/api/README.md @@ -75,7 +75,7 @@ class CollectInstances(pyblish.api.ContextPlugin): an LayerSet and marked with a unique identifier; Identifier: - id (str): "pyblish.avalon.instance" + id (str): "ayon.create.instance" """ label = "Instances" diff --git a/client/ayon_core/hosts/photoshop/api/pipeline.py b/client/ayon_core/hosts/photoshop/api/pipeline.py index ebde175053..4e9a861220 100644 --- a/client/ayon_core/hosts/photoshop/api/pipeline.py +++ b/client/ayon_core/hosts/photoshop/api/pipeline.py @@ -9,6 +9,8 @@ from ayon_core.pipeline import ( register_loader_plugin_path, register_creator_plugin_path, AVALON_CONTAINER_ID, + AYON_INSTANCE_ID, + AVALON_INSTANCE_ID, ) from ayon_core.host import ( @@ -121,7 +123,9 @@ class PhotoshopHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): layers_meta = stub.get_layers_metadata() if layers_meta: for instance in layers_meta: - if instance.get("id") == "pyblish.avalon.instance": + if instance.get("id") in { + AYON_INSTANCE_ID, AVALON_INSTANCE_ID + }: instances.append(instance) return instances diff --git a/client/ayon_core/hosts/photoshop/api/ws_stub.py b/client/ayon_core/hosts/photoshop/api/ws_stub.py index 42bad05f26..f53f7e04d9 100644 --- a/client/ayon_core/hosts/photoshop/api/ws_stub.py +++ b/client/ayon_core/hosts/photoshop/api/ws_stub.py @@ -119,14 +119,14 @@ class PhotoshopServerStub: "active":true, "subset":"imageBG", "family":"image", - "id":"pyblish.avalon.instance", + "id":"ayon.create.instance", "asset":"Town", "uuid": "8" }] - for created instances OR [{ "schema": "openpype:container-2.0", - "id": "pyblish.avalon.instance", + "id": "ayon.create.instance", "name": "imageMG", "namespace": "Jungle_imageMG_001", "loader": "ImageLoader", @@ -420,7 +420,7 @@ class PhotoshopServerStub: (list) example: {"8":{"active":true,"subset":"imageBG", - "family":"image","id":"pyblish.avalon.instance", + "family":"image","id":"ayon.create.instance", "asset":"Town"}} 8 is layer(group) id - used for deletion, update etc. """ diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_color_coded_instances.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_color_coded_instances.py index 6a09cff3c7..fa0ad4aac5 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_color_coded_instances.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_color_coded_instances.py @@ -26,7 +26,7 @@ class CollectColorCodedInstances(pyblish.api.ContextPlugin): only separate subsets per marked layer. Identifier: - id (str): "pyblish.avalon.instance" + id (str): "ayon.create.instance" """ label = "Collect Color-coded Instances" diff --git a/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py b/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py index 0ae6206496..89546373ad 100644 --- a/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py +++ b/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py @@ -2,6 +2,7 @@ from pprint import pformat import pyblish +from ayon_core.pipeline import AYON_INSTANCE_ID, AVALON_INSTANCE_ID from ayon_core.hosts.resolve.api.lib import ( get_current_timeline_items, get_timeline_item_pype_tag, @@ -39,7 +40,9 @@ class PrecollectInstances(pyblish.api.ContextPlugin): if not tag_data: continue - if tag_data.get("id") != "pyblish.avalon.instance": + if tag_data.get("id") not in { + AYON_INSTANCE_ID, AVALON_INSTANCE_ID + }: continue media_pool_item = timeline_item.GetMediaPoolItem() diff --git a/client/ayon_core/hosts/tvpaint/api/pipeline.py b/client/ayon_core/hosts/tvpaint/api/pipeline.py index 78a0c5270d..6194851cfb 100644 --- a/client/ayon_core/hosts/tvpaint/api/pipeline.py +++ b/client/ayon_core/hosts/tvpaint/api/pipeline.py @@ -52,7 +52,7 @@ expected that there are also keys `["instances0", "instances1"]`. Workfile data looks like: ``` [avalon] -instances0=[{{__dq__}id{__dq__}: {__dq__}pyblish.avalon.instance{__dq__... +instances0=[{{__dq__}id{__dq__}: {__dq__}ayon.create.instance{__dq__... instances1=...more data... instances=2 ``` diff --git a/client/ayon_core/pipeline/create/README.md b/client/ayon_core/pipeline/create/README.md index 012572a776..074cd24364 100644 --- a/client/ayon_core/pipeline/create/README.md +++ b/client/ayon_core/pipeline/create/README.md @@ -14,7 +14,7 @@ Except creating and removing instances are all changes not automatically propaga ## CreatedInstance -Product of creation is "instance" which holds basic data defying it. Core data are `creator_identifier`, `family` and `subset`. Other data can be keys used to fill subset name or metadata modifying publishing process of the instance (more described later). All instances have `id` which holds constant `pyblish.avalon.instance` and `instance_id` which is identifier of the instance. +Product of creation is "instance" which holds basic data defying it. Core data are `creator_identifier`, `family` and `subset`. Other data can be keys used to fill subset name or metadata modifying publishing process of the instance (more described later). All instances have `id` which holds constant `ayon.create.instance` or `pyblish.avalon.instance` (for backwards compatibility) and `instance_id` which is identifier of the instance. Family tells how should be instance processed and subset what name will published item have. - There are cases when subset is not fully filled during creation and may change during publishing. That is in most of cases caused because instance is related to other instance or instance data do not represent final product. @@ -24,7 +24,7 @@ Family tells how should be instance processed and subset what name will publishe { # Immutable data after creation ## Identifier that this data represents instance for publishing (automatically assigned) - "id": "pyblish.avalon.instance", + "id": "ayon.create.instance", ## Identifier of this specific instance (automatically assigned) "instance_id": , ## Instance family (used from Creator) diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index b973c45097..c50170f070 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -27,7 +27,11 @@ from ayon_core.lib.attribute_definitions import ( get_default_values, ) from ayon_core.host import IPublishHost, IWorkfileHost -from ayon_core.pipeline import Anatomy +from ayon_core.pipeline import ( + Anatomy, + AYON_INSTANCE_ID, + AVALON_INSTANCE_ID, +) from ayon_core.pipeline.plugin_discover import DiscoverResult from .creator_plugins import ( @@ -937,7 +941,11 @@ class CreatedInstance: # QUESTION Does it make sense to have data stored as ordered dict? self._data = collections.OrderedDict() # QUESTION Do we need this "id" information on instance? - self._data["id"] = "pyblish.avalon.instance" + item_id = data.get("id") + # TODO use only 'AYON_INSTANCE_ID' when all hosts support it + if item_id not in {AYON_INSTANCE_ID, AVALON_INSTANCE_ID}: + item_id = AVALON_INSTANCE_ID + self._data["id"] = item_id self._data["family"] = family self._data["subset"] = subset_name self._data["active"] = data.get("active", True) diff --git a/client/ayon_core/pipeline/create/legacy_create.py b/client/ayon_core/pipeline/create/legacy_create.py index 2ab8b8e3e5..b0e94ca225 100644 --- a/client/ayon_core/pipeline/create/legacy_create.py +++ b/client/ayon_core/pipeline/create/legacy_create.py @@ -10,6 +10,7 @@ import logging import collections from ayon_core.client import get_asset_by_id +from ayon_core.pipeline.constants import AVALON_INSTANCE_ID from .subset_name import get_subset_name @@ -33,7 +34,8 @@ class LegacyCreator(object): # Default data self.data = collections.OrderedDict() - self.data["id"] = "pyblish.avalon.instance" + # TODO use 'AYON_INSTANCE_ID' when all hosts support it + self.data["id"] = AVALON_INSTANCE_ID self.data["family"] = self.family self.data["asset"] = asset self.data["subset"] = name diff --git a/client/ayon_core/tools/subsetmanager/README.md b/client/ayon_core/tools/subsetmanager/README.md index 062214834a..35b80ea114 100644 --- a/client/ayon_core/tools/subsetmanager/README.md +++ b/client/ayon_core/tools/subsetmanager/README.md @@ -14,6 +14,6 @@ Host is expected to implemented: - `list_instances` - returning list of dictionaries (instances), must contain unique uuid field example: - ```[{"uuid":"15","active":true,"subset":"imageBG","family":"image","id":"pyblish.avalon.instance","asset":"Town"}]``` + ```[{"uuid":"15","active":true,"subset":"imageBG","family":"image","id":"ayon.create.instance","asset":"Town"}]``` - `remove_instance(instance)` - removes instance from file's metadata instance is a dictionary, with uuid field \ No newline at end of file From e145b3347666bb4815147d135118a335e5837727 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 20 Feb 2024 15:04:20 +0100 Subject: [PATCH 197/573] Fix typo in data key and update variable name. - Corrected a typo in a data key from "folferPath" to "folderPath". - Updated the variable name from "asset" to "asset_name". --- .../hosts/hiero/plugins/publish/precollect_instances.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py b/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py index 4142d2f403..e97059edf6 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py @@ -248,7 +248,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin): if not self.test_any_audio(item): return - asset = data["folferPath"] + asset = data["folderPath"] asset_name = data["asset_name"] # insert family into families From 72e615169b198580394a0f7a0314d545b861c901 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 20 Feb 2024 16:42:30 +0100 Subject: [PATCH 198/573] fix houdini instance collection --- client/ayon_core/hosts/houdini/api/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/api/plugin.py b/client/ayon_core/hosts/houdini/api/plugin.py index 2538766f5f..57fd194000 100644 --- a/client/ayon_core/hosts/houdini/api/plugin.py +++ b/client/ayon_core/hosts/houdini/api/plugin.py @@ -122,7 +122,7 @@ class HoudiniCreatorBase(object): nodes = [] for id_type in [AYON_INSTANCE_ID, AVALON_INSTANCE_ID]: - nodes.extend(lsattr("id", AYON_INSTANCE_ID)) + nodes.extend(lsattr("id", id_type)) for node in nodes: creator_identifier_parm = node.parm("creator_identifier") From 69882f5d796fd8816615ad9975addae17acb35d7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 20 Feb 2024 16:43:01 +0100 Subject: [PATCH 199/573] Remove 'pyblish.mindbender.container' Co-authored-by: Roy Nieterau --- client/ayon_core/hosts/maya/api/pipeline.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/maya/api/pipeline.py b/client/ayon_core/hosts/maya/api/pipeline.py index bba9943889..90fb2e5888 100644 --- a/client/ayon_core/hosts/maya/api/pipeline.py +++ b/client/ayon_core/hosts/maya/api/pipeline.py @@ -380,8 +380,7 @@ def _ls(): ids = { AYON_CONTAINER_ID, # Backwards compatibility - AVALON_CONTAINER_ID, - "pyblish.mindbender.container" + AVALON_CONTAINER_ID } # Iterate over all 'set' nodes in the scene to detect whether From ebfa5a2f3b79f367fe6c8079043e322dfd2f841b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 20 Feb 2024 17:02:42 +0100 Subject: [PATCH 200/573] fix traypublisher editorial --- .../hosts/traypublisher/plugins/create/create_online.py | 2 +- .../traypublisher/plugins/publish/collect_shot_instances.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_online.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_online.py index db11d30afe..36d2fba976 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_online.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_online.py @@ -53,7 +53,7 @@ class OnlineCreator(TrayPublishCreator): # disable check for existing subset with the same name """ asset = get_asset_by_name( - self.project_name, instance_data["asset"], fields=["_id"]) + self.project_name, instance_data["folderPath"], fields=["_id"]) if get_subset_by_name( self.project_name, origin_basename, asset["_id"], diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py index 67eb3d425c..d489528c57 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py @@ -17,7 +17,7 @@ class CollectShotInstance(pyblish.api.InstancePlugin): families = ["shot"] SHARED_KEYS = [ - "asset", + "folderPath", "fps", "handleStart", "handleEnd", @@ -132,7 +132,7 @@ class CollectShotInstance(pyblish.api.InstancePlugin): "sourceIn": _cr_attrs["sourceIn"], "sourceOut": _cr_attrs["sourceOut"], "workfileFrameStart": workfile_start_frame, - "asset": _cr_attrs["folderPath"], + "folderPath": _cr_attrs["folderPath"], } def _solve_hierarchy_context(self, instance): From 7e78937e31b39c9d690d7b6573d8aa0860986ee1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Feb 2024 10:38:21 +0100 Subject: [PATCH 201/573] fix 'update_content_on_context_change' in maya --- client/ayon_core/hosts/maya/api/lib.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/hosts/maya/api/lib.py b/client/ayon_core/hosts/maya/api/lib.py index 3a29fe433b..e2e985396b 100644 --- a/client/ayon_core/hosts/maya/api/lib.py +++ b/client/ayon_core/hosts/maya/api/lib.py @@ -24,7 +24,8 @@ from ayon_core.client import ( get_asset_by_name, get_subsets, get_last_versions, - get_representation_by_name + get_representation_by_name, + get_asset_name_identifier, ) from ayon_core.settings import get_project_settings from ayon_core.pipeline import ( @@ -3143,21 +3144,25 @@ def fix_incompatible_containers(): def update_content_on_context_change(): """ - This will update scene content to match new asset on context change + This will update scene content to match new folder on context change """ scene_sets = cmds.listSets(allSets=True) asset_doc = get_current_project_asset() - new_asset = asset_doc["name"] + new_folder_path = get_asset_name_identifier(asset_doc) new_data = asset_doc["data"] for s in scene_sets: try: if cmds.getAttr("{}.id".format(s)) == "pyblish.avalon.instance": attr = cmds.listAttr(s) print(s) - if "asset" in attr: - print(" - setting asset to: [ {} ]".format(new_asset)) - cmds.setAttr("{}.asset".format(s), - new_asset, type="string") + if "folderPath" in attr: + print( + " - setting folder to: [ {} ]".format(new_folder_path) + ) + cmds.setAttr( + "{}.folderPath".format(s), + new_folder_path, type="string" + ) if "frameStart" in attr: cmds.setAttr("{}.frameStart".format(s), new_data["frameStart"]) From 59a3d87297aca1c7d564d1641ff2458e7390ca43 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 21 Feb 2024 18:48:39 +0800 Subject: [PATCH 202/573] make sure all the objects clean up when removing items with different alembic imports --- client/ayon_core/hosts/max/api/pipeline.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/ayon_core/hosts/max/api/pipeline.py b/client/ayon_core/hosts/max/api/pipeline.py index 98cb95eb9a..68e8a3a020 100644 --- a/client/ayon_core/hosts/max/api/pipeline.py +++ b/client/ayon_core/hosts/max/api/pipeline.py @@ -254,7 +254,12 @@ def remove_container_data(container_node: str): all_set_members_names = [ member.node for member in container_node.modifiers[0].openPypeData.all_handles] + # clean up the children of alembic dummy objects for current_set_member in all_set_members_names: + shape_list = [members for members in current_set_member.Children + if rt.ClassOf(members) == rt.AlembicObject] + if shape_list: + rt.Delete(shape_list) rt.Delete(current_set_member) rt.deleteModifier(container_node, container_node.modifiers[0]) From c1e528e134c85667c075ef3a211d6bb07b3f9cc2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 21 Feb 2024 12:06:41 +0100 Subject: [PATCH 203/573] Remove pushing OPENPYPE_VERSION That env var doesn't make sense in Ayon. --- .../publish/create_publish_royalrender_job.py | 25 +------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/client/ayon_core/modules/royalrender/plugins/publish/create_publish_royalrender_job.py b/client/ayon_core/modules/royalrender/plugins/publish/create_publish_royalrender_job.py index 910abfcb15..90eab2e675 100644 --- a/client/ayon_core/modules/royalrender/plugins/publish/create_publish_royalrender_job.py +++ b/client/ayon_core/modules/royalrender/plugins/publish/create_publish_royalrender_job.py @@ -216,7 +216,7 @@ class CreatePublishRoyalRenderJob(pyblish.api.InstancePlugin, SeqEnd=1, SeqStep=1, SeqFileOffset=0, - Version=self._sanitize_version(os.environ.get("OPENPYPE_VERSION")), + Version=os.environ["AYON_BUNDLE_NAME"], SceneName=abs_metadata_path, # command line arguments CustomAddCmdFlags=" ".join(args), @@ -243,26 +243,3 @@ class CreatePublishRoyalRenderJob(pyblish.api.InstancePlugin, job.WaitForPreIDs += jobs_pre_ids return job - - def _sanitize_version(self, version): - """Returns version in format MAJOR.MINORPATCH - - 3.15.7-nightly.2 >> 3.157 - """ - VERSION_REGEX = re.compile( - r"(?P0|[1-9]\d*)" - r"\.(?P0|[1-9]\d*)" - r"\.(?P0|[1-9]\d*)" - r"(?:-(?P[a-zA-Z\d\-.]*))?" - r"(?:\+(?P[a-zA-Z\d\-.]*))?" - ) - - valid_parts = VERSION_REGEX.findall(version) - if len(valid_parts) != 1: - # Return invalid version with filled 'origin' attribute - return version - - # Unpack found version - major, minor, patch, pre, post = valid_parts[0] - - return "{}.{}{}".format(major, minor, patch) From 528f161472c0ca1e68244b99334b72f777e3dff6 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 21 Feb 2024 12:09:51 +0100 Subject: [PATCH 204/573] Cleaned up how selected RR server is queried It should be only queried only once in Collector phase, not multiple times. There is no real possibility to select RR server on instance, eg. artist cannot set that in Publisher, but thats on different PR. --- client/ayon_core/modules/royalrender/lib.py | 18 +------------ .../publish/collect_rr_path_from_instance.py | 3 +++ .../publish/submit_jobs_to_royalrender.py | 26 +------------------ 3 files changed, 5 insertions(+), 42 deletions(-) diff --git a/client/ayon_core/modules/royalrender/lib.py b/client/ayon_core/modules/royalrender/lib.py index 966eb82ab1..73383d482c 100644 --- a/client/ayon_core/modules/royalrender/lib.py +++ b/client/ayon_core/modules/royalrender/lib.py @@ -108,9 +108,7 @@ class BaseCreateRoyalRenderJob(pyblish.api.InstancePlugin, context = instance.context - self._rr_root = self._resolve_rr_path(context, instance.data.get( - "rrPathName")) # noqa - self.log.debug(self._rr_root) + self._rr_root = instance.data.get("rrPathName") if not self._rr_root: raise KnownPublishError( ("Missing RoyalRender root. " @@ -210,20 +208,6 @@ class BaseCreateRoyalRenderJob(pyblish.api.InstancePlugin, """Host specific mapping for RRJob""" raise NotImplementedError - @staticmethod - def _resolve_rr_path(context, rr_path_name): - # type: (pyblish.api.Context, str) -> str - rr_settings = context.data["project_settings"]["royalrender"] - rr_paths = rr_settings["rr_paths"] - selected_paths = rr_settings["selected_rr_paths"] - - rr_servers = { - path_key: rr_paths[path_key] - for path_key in selected_paths - if path_key in rr_paths - } - return rr_servers[rr_path_name][platform.system().lower()] - def expected_files(self, instance, path, start_frame, end_frame): """Get expected files. diff --git a/client/ayon_core/modules/royalrender/plugins/publish/collect_rr_path_from_instance.py b/client/ayon_core/modules/royalrender/plugins/publish/collect_rr_path_from_instance.py index f43ed1ca49..d860df4684 100644 --- a/client/ayon_core/modules/royalrender/plugins/publish/collect_rr_path_from_instance.py +++ b/client/ayon_core/modules/royalrender/plugins/publish/collect_rr_path_from_instance.py @@ -19,6 +19,9 @@ class CollectRRPathFromInstance(pyblish.api.InstancePlugin): # type: (pyblish.api.Instance) -> str """Get Royal Render pat name from render instance.""" + # TODO there are no "rrPaths" on instance, if Publisher should expose + # this (eg. artist could select specific server) it must be added + # to publisher instance_rr_paths = instance.data.get("rrPaths") if instance_rr_paths is None: return "default" diff --git a/client/ayon_core/modules/royalrender/plugins/publish/submit_jobs_to_royalrender.py b/client/ayon_core/modules/royalrender/plugins/publish/submit_jobs_to_royalrender.py index 5e25464186..dcec2ac810 100644 --- a/client/ayon_core/modules/royalrender/plugins/publish/submit_jobs_to_royalrender.py +++ b/client/ayon_core/modules/royalrender/plugins/publish/submit_jobs_to_royalrender.py @@ -25,16 +25,6 @@ class SubmitJobsToRoyalRender(pyblish.api.ContextPlugin): self._submission_parameters = [] def process(self, context): - rr_settings = ( - context.data - ["system_settings"] - ["modules"] - ["royalrender"] - ) - - if rr_settings["enabled"] is not True: - self.log.warning("RoyalRender modules is disabled.") - return # iterate over all instances and try to find RRJobs jobs = [] @@ -51,7 +41,7 @@ class SubmitJobsToRoyalRender(pyblish.api.ContextPlugin): instance_rr_path = instance.data["rrPathName"] if jobs: - self._rr_root = self._resolve_rr_path(context, instance_rr_path) + self._rr_root = instance_rr_path if not self._rr_root: raise KnownPublishError( ("Missing RoyalRender root. " @@ -100,17 +90,3 @@ class SubmitJobsToRoyalRender(pyblish.api.ContextPlugin): def get_submission_parameters(self): return [SubmitterParameter("RequiredMemory", "0")] - - @staticmethod - def _resolve_rr_path(context, rr_path_name): - # type: (pyblish.api.Context, str) -> str - rr_settings = context.data["project_settings"]["royalrender"] - rr_paths = rr_settings["rr_paths"] - selected_paths = rr_settings["selected_rr_paths"] - - rr_servers = { - path_key: rr_paths[path_key] - for path_key in selected_paths - if path_key in rr_paths - } - return rr_servers[rr_path_name][platform.system().lower()] From 27eef746553a647ca36e87ce4faed62d5bfcd284 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 21 Feb 2024 12:22:43 +0100 Subject: [PATCH 205/573] OP-7700 - adding OpenRV to Applications Must be here to be exposed in Applications in Anatomy --- .../hooks/pre_add_last_workfile_arg.py | 3 ++- client/ayon_core/hooks/pre_ocio_hook.py | 1 + .../ayon_core/resources/app_icons/openrv.png | Bin 0 -> 7840 bytes .../applications/server/applications.json | 24 ++++++++++++++++++ server_addon/applications/server/settings.py | 2 ++ server_addon/applications/server/version.py | 2 +- 6 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 client/ayon_core/resources/app_icons/openrv.png diff --git a/client/ayon_core/hooks/pre_add_last_workfile_arg.py b/client/ayon_core/hooks/pre_add_last_workfile_arg.py index 8144afd401..d11bb106d6 100644 --- a/client/ayon_core/hooks/pre_add_last_workfile_arg.py +++ b/client/ayon_core/hooks/pre_add_last_workfile_arg.py @@ -27,7 +27,8 @@ class AddLastWorkfileToLaunchArgs(PreLaunchHook): "tvpaint", "substancepainter", "aftereffects", - "wrap" + "wrap", + "openrv" } launch_types = {LaunchTypes.local} diff --git a/client/ayon_core/hooks/pre_ocio_hook.py b/client/ayon_core/hooks/pre_ocio_hook.py index 00ba9a3bcb..11f026f812 100644 --- a/client/ayon_core/hooks/pre_ocio_hook.py +++ b/client/ayon_core/hooks/pre_ocio_hook.py @@ -19,6 +19,7 @@ class OCIOEnvHook(PreLaunchHook): "nuke", "hiero", "resolve", + "openrv" } launch_types = set() diff --git a/client/ayon_core/resources/app_icons/openrv.png b/client/ayon_core/resources/app_icons/openrv.png new file mode 100644 index 0000000000000000000000000000000000000000..30077b38e806279aa2fce13213be0372e6cac8af GIT binary patch literal 7840 zcmV;R9$(>!P)Px#IAvH#W=%~1DgXcg2mk?xX#fNO00031000^Q000000-yo_1ONa40RR91fS>~a z1ONa40RR91fB*mh0Bc4jO#lEM7D+@wRCodHT?u@Y)wMsF?0X;(LI?>EAP@vm7C{Pv zB3cVRQ9;q7zS zXbkXCRXwWMprD|Z`1ttT`uh6g2?+@&Jng*KiSp7*F9j`GvgDD7h=>*3xFj^Nva-_C zc53-87zhXmARbT@92|@{JIcz+o@Yil=`Mo2$^fOMr8jb8R&ir)-n@A;rKP1&d3ia_ zojcdv_G$SP7ytvr#>Uc&88c}4^5ryS$dJmas;awt_3E`(k+kC;FJ8QusMqV)noOqF z3l}b!=FOXD;@V9+cI+_i-o0Dg<5~OjE)ICGnGPK~#Ar7aV9Ire;z90YlJYR21J|!# z|8R74^a^HxT4)` zz+eiDjNz(`KBx#q25`}>)R3P|WHeGl>Tn7Of_U~(-?-%FvLb4!DyJ(~uF%4T3n?=* zGr52N{*Uvg7x}MU(3V0jW-loznbfaeKZyI8+qP|^=bwKb4RM$|I4+)s{pJmd9)2w` z*E&q=$wysq2%UTN*HroKK1zLfDaDPQLdF)oCq>%N%gol=SVx&l=22@?Jq=&FmO|qC zFjM%3;V=cXR98^Wd&{Xh^BZD`OIx;Vq5JQ@|DHX2_N?To!C=3!ZNKQ+zHaBucz}fm9ev@yeQI9R7_%Rd39BXK7 z5Q>nyjnVRTJO=DBfUdf_`lsy0Tgmf%s#3d$xX<|c5=!~;JfY3EHVF~L3bFf~*zsN0 zY6J$93=mLMR5Xs~5{UcT?TY(_Pf~b7e`;-RB9pJQYmGh~f&m)_XlQ5{5FQ@>B|8=3 zmM&fDP~2NbeTOtttHf6ruweiuekfNI_uO;OS&1JxfW`eUma@3NmmM5IZ0lOYy{}rN zmD`~huwekM8EQJ4n&y?e#L2hNgca{mSYL?yMl!L6uR-_1fRX`3?-}Q*Y1S@#|+TD`l|It1p^ub zs366UU$uJ)QdJ4`V+8c%rU&xM?BAwLnbIsN@p5G7bUG}-SH#7|{c7vhtyZmGV*ojp z{P@1LwKd3KFw6p>@uvhZ1*fT~t*!kHf72L1iJEk=N>ZN%EK;F~qqJ<6WZSYz zcff?!2bVFRF@Vdc>YfQT2I!u2b-7m>1GtQ;?wL@t0y=@NM%K~|fozt^0%)L_A)H3X z0keS|H>v9&FKmxx0PIUJ$M4(Y0Fzh;F$b~QOXK=`5W6KPkO^$0eihX;sQ3ycT`8wt zb@dcvXr<4Fwmd`qbAP%h=5LRfe+h zDC7v{xa+4;SYirCGPf(#z74(f!9f(>tfwibzNedyou>4>%M@GRz$4u#YL-Snu3(b# z*cduBY!L0bVJu~)r3y(AB%-O?6z%bg9>)M6ep7KiWxetkZ?GtLh`^FBHd4m#KBACb zeZ}q{hly1_N~3_s*c|MTE{iC1nqPzEe zO&Gx~maTG_-$M)>+t2g8&;wWiLEIx8N74^4B|t7sJ$05w=j79ecg>|D76VnrL{ZkDWXeuSqSD@R z;#+ugGbNQ(&`6dN!!8w31V8IJPcv@=k7{h9r#E~~y{l_!_1A=c?y7@(8?AAB*Y`zc2iSG2yK})&1)^z7{F_&G#3PgZ{FT-cn<&8O8gKe>X{)a z^!EIp(1n3Xf*CL+*i-Dzvt;qR@NJSwsjX>N=c@@mg&UP z=6h3`c-j`f&u?S`4ZU1U3%=M#En@pSZ`fnvAIlg@zgx6`F0vvQ#!~}y14jU}5c$3y zGlE`R^Z*t0?IoDS$fhLT|NQfvVp?!ihO6H+25=QqE~H~YPWSFQNHJ{U8Fc}IXcq_c zcYdt$cMnk_H|uIsNFw=-vh$*EtHd-DDu~bg$W%3X5w(x5oWTLK!Hm0)rCeX zlJv|e02;Te;9^<>(_(0Yk$#k0M9oYjVeYX+_!DdIBpgo6sHTO1`~^LR{9Q%&7k8W(yaD7d=Ld2-=?B1>m8P+3kjpN zU#+6}$+N_!+Yn}g9XF1nTMwV4q^lLw!dAwJz-SsD5>NY^uQ)8r*~cC?1sK_xf=2nm>2DV33j-PV%T08Ht)H- zAU-oFeaTvCxSYfOeswo5=4o-35zhN@qO=*ARPnkHN(+c010)PPc;MI1PEE4W^l_FD zyA*AjWA8z@pJn|H#r9DU!3pt^!^ni{qYI-rQwUE4CJN#_aPU+|MF?iF zEwybwZNFQdB*|fUgym4ur#IC`hEsF{&-|=p^bh3ei9yCEmej6aI*kFK>6UE&go?NR zMeI9NYcd!ha!3XZd3+TgycCa3i)zW-d&gMw)~+YCDoPi`Zd&t{0;dg}KJfQ@Yc&sL zHim{!1OLIwz#&_a0YMb#Uhl2B>`#K$B;LOX&;^pdtb{IadXMy#Wz^bGr}}5$IkP(R zFqQ4u%p(>Xm%Ap2F|CxsE)Mke_Q>1Cb_xUFBoQ2~W^!?L%TUv`kD3Kh>P!Y|;Ryf$ z#V@9=k=OaXC$Uf_mXz^_d;F{CIz2N2S-})7?u`Gq6D}VqgS1^3blkqH#%8KA>KUQT z+^pF4tEl8P|9asNvTeFjRM>vCKCy_G1IkP+uN3n?Jc5DzSKMkotHBfObRGl9d*|o( zAbL47K#rk~f|xKc0poe)4jwz`0TN*9b7Xo->BJSTWG+jBRxWewxm>ZDh;SGdYnSAeK6no7?Hh-^j_kS4SR$MT#!}_P1UBQ+Exbz$xaxVv!q?DEk zS1>|FzHhCiV=ay&nyh6yxX~EE0dm^&2^+31Lj7Bq{l%7@Vim9| zD$?Q<;^W>&PjWC7uzAXU3duo*fRJl0s z4`DhoEj05@9Pj}j0Nl5j|A!Qo(leiI7R$HhkT8?*@59}z{@45i@prP1)mJ9EdnQjV|F=y=5EB=@IJ zIbsK)UjPJ%ASiUj2ODT`QK^XNfky8L5Hzaikv}Q(9IafpL4=BdnZQ&i@58%p70b(x z%5e2t=S%_Ik-+ds8n*Z~D&74V8S1K4+lzIAn2hn%`-Yj8@NLy(UFs!5zqU-DM5%?v zbUUJgID`vgJR|oKz4rE>Y1`BZ^d*OcmBiZ*2^(5a%pqY%XvQ&fNEl)O;0eTrkq`gl zRi{VCFSBo<&CFbg3i8%z zjUF)oMyUAV2XxvE027D>7dP^DBRDmhE3Z;;R1EtozzCje zq~7XOS11Qe0Htj`6AlqQ(#8<9%{<53M?Lallw-a>aBYUz7=q9+LZ$#!O1>5wZgjpJBV!%^2n%R%mk@IsS{hn@(aa^p1rMM z71GwQiPzF;W*+$deL;Lsz-0!2cM8!o7q|SCzW>u3R9}=YD0t~#n<)LErzm!41{uA) zYo)ue2$%p`{GRcnY2WpuC;^*8*&-NF#+yUg!v}u>rUvWg(EbVH9&v?GDt!qqFaSn< z7?qzsN@v!srqXZr*tAFI*3G4jBM(apflu(|7Nz^lmhc(`IQ2Itl|0T~dg} zyL##yjrbi!jZUb?Ap@X2KKsGNPUx&wWm^n5WdM0lLfh>8MmR8_!f4=+7;w%2PI`qO zgA~6jpgDq^V5)0=_Lu=uXWvheNvZ78^)x)OYa*z-x5}Xv;HdL(0S|ldSCq~_A=e%K z`HuSDoxV9`0JWfv0W)S(#-isaE`1Cc@!NrH*J|LO7;wx0Jg-OeOSbkbW=J|^{PHZ9@t-KCDaMoSaQd`A4WDPWk=ljt31oj9vafda{-*2fdYq&!O zfQbihAm(}i0yDH9j+9}q8{9f(0CbR#Cm7Hez)uL}t7djA z1MvE{242E|mvHHQ20+8?G!8xk#1m@Z83uqulb0f;ohyOuXe)N9!dx`e*Qw^J`gR}H zoH@ovvs`0dL{d%V`AhRIT(h)6d3)K-dX_L^r1xbRKi%uL->T0r{pQdJzc_oc@S0FDpjbM2~gv*_%f zUZ=}n{4ZOM_6cijYNXoilkIJ%+vywfv)#_?PR6?IQ|@Hzv^*X$0D?tZ>S`$alXvOd z-`*yDRfY48fQdsqs@8cO&5D4^AXasFhmB7F%&zz?fpFd)jzc4S1vrXE?#j7cQ=$q3$h!|%OaC1{LtPZksXUzYd*h{e$(=z)mA;;LU3bm+!3!&7lN%478)sq6jweH3X+~IcuDHQBL1N(x6{0Mrvm6Z|2b%!6$f1X9haW z??wVBnbjumY!p>z0867zy#6eY^k8dfxLn*r0$|j)0mg^Po`QF}glKCPb5Nx3rU4Kn zpN~(=vm7q@FE#@uCa@=&+H|5FFThx*SP87tQ$1&UuIdEMi%9X>2vxQO)ioFmw65Ol z=TLrc@3TV&z);1$05x&1rs)TT@lK+wBC0P{Kk-8-e>={#w~d~qFR3Nk*m1V`SIs4a zKYsxy3_#SEwfk#WyYDkfA?{^8)L9NJDA=DxnSb6(hDH{N3`U+Z965z#MQFDkV4#_Q zV$NM~$N+<8j;3)>%o4V}nz)DEuj4}us`JX|?579m^1&ZQ_wBdePGiQ5AvA8~%9ZZ6 zPs`^70~lZvCQJ~6%}G+#&kh+N?T+i%0N`J`P_dz5^6BfE=)(UTrt_a46ZfbgEiH{! ztXM%0KKLMAy?T{(uK-%Sc(H2b+Dq?Xz`zS|d3kwmF@ZV*ShRFu*DDk{9kh5>aS9Ke zr!yN4h|!O3g!8XrJ@Ld7^xNP5mI8UXa?P4Gv~%Z9w;JUgwDi1EAZ>N*p=}SSGk{cr zOly|JeN8^Z{nu1*fVFuG%$hZeUVZgdy6(E`Xxp}J^wCEj(dEmRDTI$m^W-37seJ9~ z8V20KwL=Dw3KZhLzKL=^J3`r8j*`9>Vc|rB2M-qFe&ND}bne_adh*F9>Bx~I6vRut zVf;IH8gzXOs51cDngBf$9VVb8?yGXmKT!taKK}UQw0!w;G0NY1>n+-{WeYX&eu2=? zuKUX#=3Z(4%Q2wN0G1R-*Y2RmgcvG4nQzTAbLLEX{q@&{xZl2gJFQ>8o(c*I1mPj_ zHSjMCIAnmjqDsOaV9=mJ^wLW&(ZdfvjK@Q7yzxfn=FOXbiD^O;_dtn%f>V_dA{}Pj zi4!M4{PM=eM%Z*W{r&HM-;Zz5_%-lf45&%~*p+~rZ@yWG`*GvOQADih_XnO88oqafbQ(HzsQk6fJqT7- zRz~^x`NF9MW&$&S=qQf|fepbM8XD-}!GktLmI_3kDO09U@7}$oSA_Bp95^7Z_L$U~ z*he>N3}9d<4&GPP*Vn(yir%{U^XIFC_xPwqE))xbCM6{aOJv%#X~Ggw%Lb$$KYpCR z%wPoh=mhd-%$PwBJ@k-TR-1QMu3Vx0`}Y%1dsI|ZB0qW}Nswyff?qIj>!-fHL((c{6h5H$1sT`uCqeUL>lh6<3-<0=-g@u%plOx1F zq=XzGb)J3pSs_WF6F|CP)XQP)*s(Tx0lpy(7(*@<@E-ZqbPIgzD6p9V7*Sf5(3&7C`!-hKDoHiWPy z;+t1qd4*1&J}u_>l#~>D@x>Q~ej%mF$jA_4|Jbo(l$)DNmo8lr9#QE#+6HkCqD%8S zuF5t;x5&=U7D}p=w==Fb5}-30(4IOmTBc8*Zj%Kf6r#Djyj(~JNC>>cybmPHVd~VW z6d4)WW`xSqfN+v^keQiDn>KBde-`(U1ehvWVhTl5o&ZcUV88&YWPsE{olrRCaOTVz zs;sQ+bP3=$3?LyA>ko2(hAw#`rT5U*u@rsw>{;oRxMB)`5+*mrY!dJTMyvD(O`k0w zWfH3d0Ye@>eAwnYbP_0r($s(}o6dqM^0dCZ_bUc~7LU;+2i94HnJ0gT zdoT>7f*hp$_~r^A9hgZ85(D8Im<(-@1M~n1zyQOB4YN^JF_mGOQ7c=@*HKr$VE`aR z8kutBNW_r8lkSz@)bdMdr7N_1Od*nX4(mY)#l^)!bVC9_(qO8P10(=jP=X1;M59NK zmVXusCZv{9DYQw{K@V-&yMrq3s?{Kr4GJ5i1>zfSyzxexrox{9f=iAiFd3$Sn{K+v znhrX^z<~qB5;=4dSO9W>TOCfLuCRupF@T&hd=KZ2J%Gml```aw_y8pFjVlO>rErO$ zcn2l|qd;L~&#Fz4upm0y{F55wS5tu0=+3xelp_WLrk_N7v;+5ue!6hsf=#oa2S7R? zMpCY~R8>_`R#uix88#U^_`$Cj0Q0F*fpavH)V?Y6EAb6N@7S?};Qp0EY;3F$+j8Pd z-=Q}^tCs`BKJ)<}d-TMs#K!F3IF?Y%V?o~CSAbos%ygaEjKfc+6 z4?g&So_XdOTDNYUO_C8KM$nyi-YLqk`M?iQ<`4=eS2|&GVxdWC|3mz{>Vj6*F9%U2T$UjJ$u@S^#uzSsQMAWfS3Yc z9Y_GCfMO`0|6vkBU+4>L+L9=@GYOyXaMNjG($XzNwuD>|4S#qBk?>6oq`dNPU<|nwz&DI`Fp*RT@}XR*K6!q% zGVvXGl}sql=j(ga&;2OkHe~GDwadE1>6a2fjw}#DITGdH9o@T{S4liI!b|nITBek~ zv#)-q19UdRx?BC8Fo10^TzA7v59yC543LL;Rs;XWfZGhf9yqwYE@FPvz<)8|HUqG2 z43Ec~N{!inQPDG7?luDeT}=ExvT5-Vv|>#X_$LE-!~lEu?lmys7cmpOz}>&1Ndy05 y0*@E~-31e{M{P9|e;ofk%C6cAnl$hqM)-f;jp$7_6`^PV0000 Date: Wed, 21 Feb 2024 12:34:00 +0100 Subject: [PATCH 206/573] added container 3 schema --- .../pipeline/schema/container-3.0.json | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 client/ayon_core/pipeline/schema/container-3.0.json diff --git a/client/ayon_core/pipeline/schema/container-3.0.json b/client/ayon_core/pipeline/schema/container-3.0.json new file mode 100644 index 0000000000..c9227bab11 --- /dev/null +++ b/client/ayon_core/pipeline/schema/container-3.0.json @@ -0,0 +1,59 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + + "title": "ayon:container-3.0", + "description": "A loaded asset", + + "type": "object", + + "additionalProperties": true, + + "required": [ + "schema", + "id", + "objectName", + "name", + "namespace", + "loader", + "representation" + ], + "properties": { + "schema": { + "description": "Schema identifier for payload", + "type": "string", + "enum": ["ayon:container-3.0"], + "example": "ayon:container-3.0" + }, + "id": { + "description": "Identifier for finding object in host", + "type": "string", + "enum": ["ayon.load.container"], + "example": "ayon.load.container" + }, + "objectName": { + "description": "Name of internal object, such as the objectSet in Maya.", + "type": "string", + "example": "Bruce_:rigDefault_CON" + }, + "loader": { + "description": "Name of loader plug-in used to produce this container", + "type": "string", + "example": "ModelLoader" + }, + "name": { + "description": "Internal object name of container in application", + "type": "string", + "example": "modelDefault_01" + }, + "namespace": { + "description": "Internal namespace of container in application", + "type": "string", + "example": "Bruce_" + }, + "representation": { + "description": "Unique id of representation in database", + "type": "string", + "example": "59523f355f8c1b5f6c5e8348" + } + } +} From 332b23391a7512ebbb28a0d635288cd84a10c97f Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 21 Feb 2024 20:55:28 +0800 Subject: [PATCH 207/573] fix the bug encountered in action.py --- client/ayon_core/hosts/max/api/action.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/max/api/action.py b/client/ayon_core/hosts/max/api/action.py index 58834d0172..bed72bc493 100644 --- a/client/ayon_core/hosts/max/api/action.py +++ b/client/ayon_core/hosts/max/api/action.py @@ -31,7 +31,7 @@ class SelectInvalidAction(pyblish.api.Action): if not invalid: self.log.info("No invalid nodes found.") return - invalid_names = [obj.name for obj in invalid if isinstance(obj, str)] + invalid_names = [obj.name for obj in invalid if not isinstance(obj, tuple)] if not invalid_names: invalid_names = [obj.name for obj, _ in invalid] invalid = [obj for obj, _ in invalid] From 4a85b2ec45a05f066758b6be4e17588cdfd51598 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 21 Feb 2024 21:01:12 +0800 Subject: [PATCH 208/573] bug fix the action.py --- client/ayon_core/hosts/max/api/action.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/max/api/action.py b/client/ayon_core/hosts/max/api/action.py index 58834d0172..bed72bc493 100644 --- a/client/ayon_core/hosts/max/api/action.py +++ b/client/ayon_core/hosts/max/api/action.py @@ -31,7 +31,7 @@ class SelectInvalidAction(pyblish.api.Action): if not invalid: self.log.info("No invalid nodes found.") return - invalid_names = [obj.name for obj in invalid if isinstance(obj, str)] + invalid_names = [obj.name for obj in invalid if not isinstance(obj, tuple)] if not invalid_names: invalid_names = [obj.name for obj, _ in invalid] invalid = [obj for obj, _ in invalid] From 8d608f72981d1e7b440b948153c2eb65cdbbdda4 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 21 Feb 2024 21:01:58 +0800 Subject: [PATCH 209/573] fix the bug in action.py --- client/ayon_core/hosts/max/api/action.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/max/api/action.py b/client/ayon_core/hosts/max/api/action.py index 58834d0172..bed72bc493 100644 --- a/client/ayon_core/hosts/max/api/action.py +++ b/client/ayon_core/hosts/max/api/action.py @@ -31,7 +31,7 @@ class SelectInvalidAction(pyblish.api.Action): if not invalid: self.log.info("No invalid nodes found.") return - invalid_names = [obj.name for obj in invalid if isinstance(obj, str)] + invalid_names = [obj.name for obj in invalid if not isinstance(obj, tuple)] if not invalid_names: invalid_names = [obj.name for obj, _ in invalid] invalid = [obj for obj, _ in invalid] From 42e3e9de97485dc865780e266e1ed2a917f1c8fe Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 21 Feb 2024 14:10:43 +0100 Subject: [PATCH 210/573] Refactor knob writing logic in Nuke API lib function. Simplify knob writing process by refactoring code to use a helper function for setting knobs based on settings. --- client/ayon_core/hosts/nuke/api/lib.py | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index adfe753987..05666e9de4 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -2346,25 +2346,8 @@ Reopening Nuke should synchronize these paths and resolve any discrepancies. if not write_node: return - try: - # write all knobs to node - for knob in nuke_imageio_writes["knobs"]: - value = knob["value"] - if isinstance(value, six.text_type): - value = str(value) - if str(value).startswith("0x"): - value = int(value, 16) - - log.debug("knob: {}| value: {}".format( - knob["name"], value - )) - write_node[knob["name"]].setValue(value) - except TypeError: - log.warning( - "Legacy workflow didn't work, switching to current") - - set_node_knobs_from_settings( - write_node, nuke_imageio_writes["knobs"]) + set_node_knobs_from_settings( + write_node, nuke_imageio_writes["knobs"]) def set_reads_colorspace(self, read_clrs_inputs): """ Setting colorspace to Read nodes From 523db4b415e2ff654972a0148e501b5675e31885 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Feb 2024 14:37:15 +0100 Subject: [PATCH 211/573] fix knobs --- client/ayon_core/hosts/nuke/api/lib.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index 7ea188305b..b5702aef48 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -1298,7 +1298,8 @@ def create_write_node( for knob in imageio_writes["knobs"]: if knob["name"] == "file_type": - ext = knob["value"] + knot_type = knob["type"] + ext = knob[knot_type] data.update({ "imageio_writes": imageio_writes, @@ -1413,12 +1414,17 @@ def create_write_node( # set tile color tile_color = next( iter( - k["value"] for k in imageio_writes["knobs"] + k[k["type"]] for k in imageio_writes["knobs"] if "tile_color" in k["name"] ), [255, 0, 0, 255] ) + new_tile_color = [] + for c in tile_color: + if isinstance(c, float): + c = int(c * 255) + new_tile_color.append(c) GN["tile_color"].setValue( - color_gui_to_int(tile_color)) + color_gui_to_int(new_tile_color)) return GN From 77b606993c3ac4eefc3551689f8c6e78345172bb Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 21 Feb 2024 14:54:30 +0100 Subject: [PATCH 212/573] Add option to clear output directory before package creation Added a new flag to clear the output directory before creating the package. This allows for a clean start each time the package is generated. --- create_package.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/create_package.py b/create_package.py index 94b31a03f2..48952c43c5 100644 --- a/create_package.py +++ b/create_package.py @@ -279,7 +279,8 @@ def create_server_package( def main( output_dir: Optional[str]=None, skip_zip: bool=False, - keep_sources: bool=False + keep_sources: bool=False, + clear_output_dir: bool=False ): log = logging.getLogger("create_package") log.info("Start creating package") @@ -292,7 +293,8 @@ def main( new_created_version_dir = os.path.join( output_dir, ADDON_NAME, ADDON_VERSION ) - if os.path.isdir(new_created_version_dir): + + if os.path.isdir(new_created_version_dir) and clear_output_dir: log.info(f"Purging {new_created_version_dir}") shutil.rmtree(output_dir) @@ -339,6 +341,15 @@ if __name__ == "__main__": "Keep folder structure when server package is created." ) ) + parser.add_argument( + "-c", "--clear-output-dir", + dest="clear_output_dir", + action="store_true", + help=( + "Clear output directory before package creation." + ) + ) + parser.add_argument( "-o", "--output", dest="output_dir", @@ -350,4 +361,9 @@ if __name__ == "__main__": ) args = parser.parse_args(sys.argv[1:]) - main(args.output_dir, args.skip_zip, args.keep_sources) + main( + args.output_dir, + args.skip_zip, + args.keep_sources, + args.clear_output_dir + ) From 54b2fa9658a3cd03a727582580417b30ee6f3709 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Feb 2024 15:02:57 +0100 Subject: [PATCH 213/573] use 'folder_path' to define current context --- client/ayon_core/cli_commands.py | 2 +- .../ayon_core/hooks/pre_copy_template_workfile.py | 2 +- client/ayon_core/hooks/pre_global_host_data.py | 4 ++-- client/ayon_core/hooks/pre_ocio_hook.py | 2 +- client/ayon_core/host/host.py | 4 ++-- client/ayon_core/hosts/aftereffects/api/lib.py | 2 +- client/ayon_core/hosts/houdini/api/lib.py | 2 +- client/ayon_core/hosts/nuke/api/lib.py | 5 +---- client/ayon_core/hosts/tvpaint/api/pipeline.py | 6 ++++-- .../hosts/tvpaint/plugins/load/load_workfile.py | 2 +- .../plugins/publish/collect_workfile_data.py | 10 +++++----- .../plugins/publish/validate_workfile_metadata.py | 2 +- client/ayon_core/lib/applications.py | 2 +- .../launch_hooks/post_start_timer.py | 4 ++-- .../ayon_core/modules/timers_manager/rest_api.py | 2 +- .../modules/timers_manager/timers_manager.py | 4 ++-- client/ayon_core/pipeline/context_tools.py | 14 +++++++------- client/ayon_core/pipeline/create/context.py | 2 +- .../pipeline/workfile/workfile_template_builder.py | 4 ++-- .../plugins/publish/collect_current_context.py | 2 +- client/ayon_core/tools/adobe_webserver/app.py | 2 +- client/ayon_core/tools/loader/control.py | 2 +- client/ayon_core/tools/sceneinventory/control.py | 6 +++--- client/ayon_core/tools/workfiles/control.py | 11 ++++++----- 24 files changed, 49 insertions(+), 49 deletions(-) diff --git a/client/ayon_core/cli_commands.py b/client/ayon_core/cli_commands.py index 7e652950eb..a24710aef2 100644 --- a/client/ayon_core/cli_commands.py +++ b/client/ayon_core/cli_commands.py @@ -106,7 +106,7 @@ class Commands: context = get_global_context() env = get_app_environments_for_context( context["project_name"], - context["asset_name"], + context["folder_path"], context["task_name"], app_full_name, launch_type=LaunchTypes.farm_publish, diff --git a/client/ayon_core/hooks/pre_copy_template_workfile.py b/client/ayon_core/hooks/pre_copy_template_workfile.py index e5d2d4f640..df2a0386b2 100644 --- a/client/ayon_core/hooks/pre_copy_template_workfile.py +++ b/client/ayon_core/hooks/pre_copy_template_workfile.py @@ -54,7 +54,7 @@ class CopyTemplateWorkfile(PreLaunchHook): self.log.info("Last workfile does not exist.") project_name = self.data["project_name"] - asset_name = self.data["asset_name"] + asset_name = self.data["folder_path"] task_name = self.data["task_name"] host_name = self.application.host_name diff --git a/client/ayon_core/hooks/pre_global_host_data.py b/client/ayon_core/hooks/pre_global_host_data.py index 3422c87484..de6d4acc8b 100644 --- a/client/ayon_core/hooks/pre_global_host_data.py +++ b/client/ayon_core/hooks/pre_global_host_data.py @@ -22,7 +22,7 @@ class GlobalHostDataHook(PreLaunchHook): app = self.launch_context.application temp_data = EnvironmentPrepData({ "project_name": self.data["project_name"], - "asset_name": self.data["asset_name"], + "folder_path": self.data["folder_path"], "task_name": self.data["task_name"], "app": app, @@ -66,7 +66,7 @@ class GlobalHostDataHook(PreLaunchHook): project_doc = get_project(project_name) self.data["project_doc"] = project_doc - asset_name = self.data.get("asset_name") + asset_name = self.data.get("folder_path") if not asset_name: self.log.warning( "Asset name was not set. Skipping asset document query." diff --git a/client/ayon_core/hooks/pre_ocio_hook.py b/client/ayon_core/hooks/pre_ocio_hook.py index 00ba9a3bcb..a709fd9fa4 100644 --- a/client/ayon_core/hooks/pre_ocio_hook.py +++ b/client/ayon_core/hooks/pre_ocio_hook.py @@ -27,7 +27,7 @@ class OCIOEnvHook(PreLaunchHook): template_data = get_template_data_with_names( project_name=self.data["project_name"], - asset_name=self.data["asset_name"], + asset_name=self.data["folder_path"], task_name=self.data["task_name"], host_name=self.host_name, system_settings=self.data["system_settings"] diff --git a/client/ayon_core/host/host.py b/client/ayon_core/host/host.py index 51dff9d558..dc7c1f2bb6 100644 --- a/client/ayon_core/host/host.py +++ b/client/ayon_core/host/host.py @@ -139,7 +139,7 @@ class HostBase(object): return { "project_name": self.get_current_project_name(), - "asset_name": self.get_current_asset_name(), + "folder_path": self.get_current_asset_name(), "task_name": self.get_current_task_name() } @@ -161,7 +161,7 @@ class HostBase(object): # Use current context to fill the context title current_context = self.get_current_context() project_name = current_context["project_name"] - asset_name = current_context["asset_name"] + asset_name = current_context["folder_path"] task_name = current_context["task_name"] items = [] if project_name: diff --git a/client/ayon_core/hosts/aftereffects/api/lib.py b/client/ayon_core/hosts/aftereffects/api/lib.py index f5f2d98698..0a2ee7b7ac 100644 --- a/client/ayon_core/hosts/aftereffects/api/lib.py +++ b/client/ayon_core/hosts/aftereffects/api/lib.py @@ -128,7 +128,7 @@ def set_settings(frames, resolution, comp_ids=None, print_msg=True): current_context = get_current_context() asset_doc = get_asset_by_name(current_context["project_name"], - current_context["asset_name"]) + current_context["folder_path"]) settings = get_asset_settings(asset_doc) msg = '' diff --git a/client/ayon_core/hosts/houdini/api/lib.py b/client/ayon_core/hosts/houdini/api/lib.py index bf66890285..9db055779d 100644 --- a/client/ayon_core/hosts/houdini/api/lib.py +++ b/client/ayon_core/hosts/houdini/api/lib.py @@ -835,7 +835,7 @@ def get_current_context_template_data_with_asset_data(): context = get_current_context() project_name = context["project_name"] - asset_name = context["asset_name"] + asset_name = context["folder_path"] task_name = context["task_name"] host_name = get_current_host_name() diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index a2ec8fdb98..adf9baad4c 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -1924,10 +1924,7 @@ class WorkfileSettings(object): Context._project_doc = project_doc self._project_name = project_name - self._asset = ( - kwargs.get("asset_name") - or get_current_asset_name() - ) + self._asset = get_current_asset_name() self._asset_entity = get_asset_by_name(project_name, self._asset) self._root_node = root_node or nuke.root() self._nodes = self.get_nodes(nodes=nodes) diff --git a/client/ayon_core/hosts/tvpaint/api/pipeline.py b/client/ayon_core/hosts/tvpaint/api/pipeline.py index 6194851cfb..1b0227e89c 100644 --- a/client/ayon_core/hosts/tvpaint/api/pipeline.py +++ b/client/ayon_core/hosts/tvpaint/api/pipeline.py @@ -99,7 +99,7 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): Union[str, None]: Current asset name. """ - return self.get_current_context().get("asset_name") + return self.get_current_context().get("folder_path") def get_current_task_name(self): """ @@ -115,11 +115,13 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): return get_global_context() if "project_name" in context: + if "asset_name" in context: + context["folder_path"] = context["asset_name"] return context # This is legacy way how context was stored return { "project_name": context.get("project"), - "asset_name": context.get("asset"), + "folder_path": context.get("asset"), "task_name": context.get("task") } diff --git a/client/ayon_core/hosts/tvpaint/plugins/load/load_workfile.py b/client/ayon_core/hosts/tvpaint/plugins/load/load_workfile.py index e29ecfd442..c652b379ab 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/load/load_workfile.py +++ b/client/ayon_core/hosts/tvpaint/plugins/load/load_workfile.py @@ -58,7 +58,7 @@ class LoadWorkfile(plugin.Loader): if not asset_name: context = get_current_context() project_name = context["project_name"] - asset_name = context["asset_name"] + asset_name = context["folder_path"] task_name = context["task_name"] template_key = get_workfile_template_key_from_context( diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile_data.py b/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile_data.py index 1cf21a1fae..414b09c123 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile_data.py +++ b/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile_data.py @@ -65,7 +65,7 @@ class CollectWorkfileData(pyblish.api.ContextPlugin): # Collect and store current context to have reference current_context = { "project_name": context.data["projectName"], - "asset_name": context.data["folderPath"], + "folder_path": context.data["folderPath"], "task_name": context.data["task"] } self.log.debug("Current context is: {}".format(current_context)) @@ -77,7 +77,7 @@ class CollectWorkfileData(pyblish.api.ContextPlugin): if "project" in workfile_context: workfile_context = { "project_name": workfile_context.get("project"), - "asset_name": workfile_context.get("asset"), + "folder_path": workfile_context.get("asset"), "task_name": workfile_context.get("task"), } # Store workfile context to pyblish context @@ -85,18 +85,18 @@ class CollectWorkfileData(pyblish.api.ContextPlugin): if workfile_context: # Change current context with context from workfile key_map = ( - ("AYON_FOLDER_PATH", "asset_name"), + ("AYON_FOLDER_PATH", "folder_path"), ("AYON_TASK_NAME", "task_name") ) for env_key, key in key_map: os.environ[env_key] = workfile_context[key] self.log.info("Context changed to: {}".format(workfile_context)) - asset_name = workfile_context["asset_name"] + asset_name = workfile_context["folder_path"] task_name = workfile_context["task_name"] else: - asset_name = current_context["asset_name"] + asset_name = current_context["folder_path"] task_name = current_context["task_name"] # Handle older workfiles or workfiles without metadata self.log.warning(( diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_workfile_metadata.py b/client/ayon_core/hosts/tvpaint/plugins/publish/validate_workfile_metadata.py index bdc46d02cd..1d9954d051 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_workfile_metadata.py +++ b/client/ayon_core/hosts/tvpaint/plugins/publish/validate_workfile_metadata.py @@ -31,7 +31,7 @@ class ValidateWorkfileMetadata(pyblish.api.ContextPlugin): actions = [ValidateWorkfileMetadataRepair] - required_keys = {"project_name", "asset_name", "task_name"} + required_keys = {"project_name", "folder_path", "task_name"} def process(self, context): workfile_context = context.data["workfile_context"] diff --git a/client/ayon_core/lib/applications.py b/client/ayon_core/lib/applications.py index 373b82d82f..eaccdc2df6 100644 --- a/client/ayon_core/lib/applications.py +++ b/client/ayon_core/lib/applications.py @@ -493,7 +493,7 @@ class ApplicationManager: """Launch procedure. For host application it's expected to contain "project_name", - "asset_name" and "task_name". + "folder_path" and "task_name". Args: app_name (str): Name of application that should be launched. diff --git a/client/ayon_core/modules/timers_manager/launch_hooks/post_start_timer.py b/client/ayon_core/modules/timers_manager/launch_hooks/post_start_timer.py index d8710a9b26..4e94603aa9 100644 --- a/client/ayon_core/modules/timers_manager/launch_hooks/post_start_timer.py +++ b/client/ayon_core/modules/timers_manager/launch_hooks/post_start_timer.py @@ -11,14 +11,14 @@ class PostStartTimerHook(PostLaunchHook): def execute(self): project_name = self.data.get("project_name") - asset_name = self.data.get("asset_name") + asset_name = self.data.get("folder_path") task_name = self.data.get("task_name") missing_context_keys = set() if not project_name: missing_context_keys.add("project_name") if not asset_name: - missing_context_keys.add("asset_name") + missing_context_keys.add("folder_path") if not task_name: missing_context_keys.add("task_name") diff --git a/client/ayon_core/modules/timers_manager/rest_api.py b/client/ayon_core/modules/timers_manager/rest_api.py index b460719f80..1473868134 100644 --- a/client/ayon_core/modules/timers_manager/rest_api.py +++ b/client/ayon_core/modules/timers_manager/rest_api.py @@ -45,7 +45,7 @@ class TimersManagerModuleRestApi: data = await request.json() try: project_name = data["project_name"] - asset_name = data["asset_name"] + asset_name = data["folder_path"] task_name = data["task_name"] except KeyError: msg = ( diff --git a/client/ayon_core/modules/timers_manager/timers_manager.py b/client/ayon_core/modules/timers_manager/timers_manager.py index f8f0e4a526..e04200525a 100644 --- a/client/ayon_core/modules/timers_manager/timers_manager.py +++ b/client/ayon_core/modules/timers_manager/timers_manager.py @@ -430,7 +430,7 @@ class TimersManager( return data = { "project_name": project_name, - "asset_name": asset_name, + "folder_path": asset_name, "task_name": task_name } @@ -472,7 +472,7 @@ class TimersManager( def _on_host_task_change(self, event): project_name = event["project_name"] - asset_name = event["asset_name"] + asset_name = event["folder_path"] task_name = event["task_name"] self.log.debug(( "Sending message that timer should change to" diff --git a/client/ayon_core/pipeline/context_tools.py b/client/ayon_core/pipeline/context_tools.py index 7c0db0be27..e8aa480105 100644 --- a/client/ayon_core/pipeline/context_tools.py +++ b/client/ayon_core/pipeline/context_tools.py @@ -355,7 +355,7 @@ def get_global_context(): Example: { "project_name": "Commercial", - "asset_name": "Bunny", + "folder_path": "Bunny", "task_name": "Animation", } @@ -366,7 +366,7 @@ def get_global_context(): return { "project_name": os.environ.get("AYON_PROJECT_NAME"), - "asset_name": os.environ.get("AYON_FOLDER_PATH"), + "folder_path": os.environ.get("AYON_FOLDER_PATH"), "task_name": os.environ.get("AYON_TASK_NAME"), } @@ -389,7 +389,7 @@ def get_current_asset_name(): host = registered_host() if isinstance(host, HostBase): return host.get_current_asset_name() - return get_global_context()["asset_name"] + return get_global_context()["folder_path"] def get_current_task_name(): @@ -481,7 +481,7 @@ def get_template_data_from_session(session=None, system_settings=None): else: context = get_current_context() project_name = context["project_name"] - asset_name = context["asset_name"] + asset_name = context["folder_path"] task_name = context["task_name"] host_name = get_current_host_name() @@ -502,7 +502,7 @@ def get_current_context_template_data(system_settings=None): context = get_current_context() project_name = context["project_name"] - asset_name = context["asset_name"] + asset_name = context["folder_path"] task_name = context["task_name"] host_name = get_current_host_name() @@ -573,7 +573,7 @@ def get_custom_workfile_template_from_session( else: context = get_current_context() project_name = context["project_name"] - asset_name = context["asset_name"] + asset_name = context["folder_path"] task_name = context["task_name"] host_name = get_current_host_name() @@ -634,7 +634,7 @@ def change_current_context(asset_doc, task_name, template_key=None): # Convert env keys to human readable keys data["project_name"] = project_name - data["asset_name"] = get_asset_name_identifier(asset_doc) + data["folder_path"] = get_asset_name_identifier(asset_doc) data["task_name"] = task_name data["workdir_path"] = workdir diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index c50170f070..75b9f38219 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -1686,7 +1686,7 @@ class CreateContext: host_context = self.host.get_current_context() if host_context: project_name = host_context.get("project_name") - asset_name = host_context.get("asset_name") + asset_name = host_context.get("folder_path") task_name = host_context.get("task_name") if isinstance(self.host, IWorkfileHost): diff --git a/client/ayon_core/pipeline/workfile/workfile_template_builder.py b/client/ayon_core/pipeline/workfile/workfile_template_builder.py index 3004e55861..eb3bcb544f 100644 --- a/client/ayon_core/pipeline/workfile/workfile_template_builder.py +++ b/client/ayon_core/pipeline/workfile/workfile_template_builder.py @@ -148,7 +148,7 @@ class AbstractTemplateBuilder(object): return self._host.get_current_context() return { "project_name": self.project_name, - "asset_name": self.current_asset_name, + "folder_path": self.current_asset_name, "task_name": self.current_task_name } @@ -1795,7 +1795,7 @@ class PlaceholderCreateMixin(object): # create subset name context = self._builder.get_current_context() project_name = context["project_name"] - asset_name = context["asset_name"] + asset_name = context["folder_path"] task_name = context["task_name"] if legacy_create: diff --git a/client/ayon_core/plugins/publish/collect_current_context.py b/client/ayon_core/plugins/publish/collect_current_context.py index 418ca8eff6..76d30a913e 100644 --- a/client/ayon_core/plugins/publish/collect_current_context.py +++ b/client/ayon_core/plugins/publish/collect_current_context.py @@ -29,7 +29,7 @@ class CollectCurrentContext(pyblish.api.ContextPlugin): context.data["projectName"] = current_context["project_name"] if not asset_name: - context.data["folderPath"] = current_context["asset_name"] + context.data["folderPath"] = current_context["folder_path"] if not task_name: context.data["task"] = current_context["task_name"] diff --git a/client/ayon_core/tools/adobe_webserver/app.py b/client/ayon_core/tools/adobe_webserver/app.py index 893c076020..b10509f484 100644 --- a/client/ayon_core/tools/adobe_webserver/app.py +++ b/client/ayon_core/tools/adobe_webserver/app.py @@ -82,7 +82,7 @@ class WebServerTool: context = get_global_context() project = context["project_name"] - asset = context["asset_name"] + asset = context["folder_path"] task = context["task_name"] log.info("Sending context change to {}-{}-{}".format(project, asset, diff --git a/client/ayon_core/tools/loader/control.py b/client/ayon_core/tools/loader/control.py index 29ca06e3e2..d2ee1d890c 100644 --- a/client/ayon_core/tools/loader/control.py +++ b/client/ayon_core/tools/loader/control.py @@ -320,7 +320,7 @@ class LoaderController(BackendLoaderController, FrontendLoaderController): context = get_current_context() folder_id = None project_name = context.get("project_name") - asset_name = context.get("asset_name") + asset_name = context.get("folder_path") if project_name and asset_name: folder = ayon_api.get_folder_by_path( project_name, asset_name, fields=["id"] diff --git a/client/ayon_core/tools/sceneinventory/control.py b/client/ayon_core/tools/sceneinventory/control.py index 54e4e9941e..16b889e855 100644 --- a/client/ayon_core/tools/sceneinventory/control.py +++ b/client/ayon_core/tools/sceneinventory/control.py @@ -69,10 +69,10 @@ class SceneInventoryController: context = self.get_current_context() project_name = context["project_name"] - folder_name = context.get("asset_name") + folder_path = context.get("folder_path") folder_id = None - if folder_name: - folder = ayon_api.get_folder_by_path(project_name, folder_name) + if folder_path: + folder = ayon_api.get_folder_by_path(project_name, folder_path) if folder: folder_id = folder["id"] diff --git a/client/ayon_core/tools/workfiles/control.py b/client/ayon_core/tools/workfiles/control.py index c1e513d12c..86c6a62a11 100644 --- a/client/ayon_core/tools/workfiles/control.py +++ b/client/ayon_core/tools/workfiles/control.py @@ -156,7 +156,7 @@ class BaseWorkfileController( self._log = None self._current_project_name = None - self._current_folder_name = None + self._current_folder_path = None self._current_folder_id = None self._current_task_name = None self._save_is_enabled = True @@ -468,12 +468,12 @@ class BaseWorkfileController( context = self._get_host_current_context() project_name = context["project_name"] - folder_name = context["asset_name"] + folder_path = context["folder_path"] task_name = context["task_name"] current_file = self.get_current_workfile() folder_id = None - if folder_name: - folder = ayon_api.get_folder_by_path(project_name, folder_name) + if folder_path: + folder = ayon_api.get_folder_by_path(project_name, folder_path) if folder: folder_id = folder["id"] @@ -481,7 +481,7 @@ class BaseWorkfileController( self._project_anatomy = None self._current_project_name = project_name - self._current_folder_name = folder_name + self._current_folder_path = folder_path self._current_folder_id = folder_id self._current_task_name = task_name @@ -639,6 +639,7 @@ class BaseWorkfileController( return { "project_name": project_name, "folder_id": folder_id, + "folder_path": folder["path"], "asset_id": folder_id, "asset_name": folder["name"], "task_id": task_id, From fca318cac7bdfafa23407d654eda814818716950 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Feb 2024 15:13:37 +0100 Subject: [PATCH 214/573] fix app launch --- client/ayon_core/lib/applications.py | 6 +++--- client/ayon_core/tools/launcher/models/actions.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/lib/applications.py b/client/ayon_core/lib/applications.py index eaccdc2df6..12a17f0cd0 100644 --- a/client/ayon_core/lib/applications.py +++ b/client/ayon_core/lib/applications.py @@ -1410,7 +1410,7 @@ class EnvironmentPrepData(dict): def get_app_environments_for_context( project_name, - asset_name, + folder_path, task_name, app_name, env_group=None, @@ -1421,7 +1421,7 @@ def get_app_environments_for_context( """Prepare environment variables by context. Args: project_name (str): Name of project. - asset_name (str): Name of asset. + folder_path (str): Folder path. task_name (str): Name of task. app_name (str): Name of application that is launched and can be found by ApplicationManager. @@ -1443,7 +1443,7 @@ def get_app_environments_for_context( context = app_manager.create_launch_context( app_name, project_name=project_name, - asset_name=asset_name, + folder_path=folder_path, task_name=task_name, env_group=env_group, launch_type=launch_type, diff --git a/client/ayon_core/tools/launcher/models/actions.py b/client/ayon_core/tools/launcher/models/actions.py index 53d2b78dc3..8d7da2748b 100644 --- a/client/ayon_core/tools/launcher/models/actions.py +++ b/client/ayon_core/tools/launcher/models/actions.py @@ -120,12 +120,12 @@ class ApplicationAction(LauncherAction): ) project_name = session["AYON_PROJECT_NAME"] - asset_name = session["AYON_FOLDER_PATH"] + folder_path = session["AYON_FOLDER_PATH"] task_name = session["AYON_TASK_NAME"] try: self.application.launch( project_name=project_name, - asset_name=asset_name, + folder_path=folder_path, task_name=task_name, **self.data ) From fb28784c4567a7685e86d5fa578084bb56e5cd65 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Feb 2024 15:13:47 +0100 Subject: [PATCH 215/573] minor fixes --- client/ayon_core/host/host.py | 2 +- client/ayon_core/modules/timers_manager/rest_api.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/host/host.py b/client/ayon_core/host/host.py index dc7c1f2bb6..f79c22824b 100644 --- a/client/ayon_core/host/host.py +++ b/client/ayon_core/host/host.py @@ -134,7 +134,7 @@ class HostBase(object): Returns: Dict[str, Union[str, None]]: Context with 3 keys 'project_name', - 'asset_name' and 'task_name'. All of them can be 'None'. + 'folder_path' and 'task_name'. All of them can be 'None'. """ return { diff --git a/client/ayon_core/modules/timers_manager/rest_api.py b/client/ayon_core/modules/timers_manager/rest_api.py index 1473868134..c890d587de 100644 --- a/client/ayon_core/modules/timers_manager/rest_api.py +++ b/client/ayon_core/modules/timers_manager/rest_api.py @@ -50,7 +50,7 @@ class TimersManagerModuleRestApi: except KeyError: msg = ( "Payload must contain fields 'project_name," - " 'asset_name' and 'task_name'" + " 'folder_path' and 'task_name'" ) self.log.error(msg) return Response(status=400, message=msg) @@ -71,11 +71,11 @@ class TimersManagerModuleRestApi: data = await request.json() try: project_name = data['project_name'] - asset_name = data['asset_name'] + asset_name = data['folder_path'] task_name = data['task_name'] except KeyError: message = ( - "Payload must contain fields 'project_name, 'asset_name'," + "Payload must contain fields 'project_name, 'folder_path'," " 'task_name'" ) self.log.warning(message) From 139f0489c5d59efeae533329bddcdc5c7ae87dc6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Feb 2024 15:26:57 +0100 Subject: [PATCH 216/573] added new product_name.py into create pipeline --- client/ayon_core/pipeline/create/__init__.py | 12 +- .../ayon_core/pipeline/create/product_name.py | 178 ++++++++++++++++++ .../ayon_core/pipeline/create/subset_name.py | 15 +- 3 files changed, 189 insertions(+), 16 deletions(-) create mode 100644 client/ayon_core/pipeline/create/product_name.py diff --git a/client/ayon_core/pipeline/create/__init__.py b/client/ayon_core/pipeline/create/__init__.py index 94d575a776..d14fc48230 100644 --- a/client/ayon_core/pipeline/create/__init__.py +++ b/client/ayon_core/pipeline/create/__init__.py @@ -10,8 +10,13 @@ from .utils import ( get_next_versions_for_instances, ) -from .subset_name import ( +from .product_name import ( TaskNotSetError, + get_product_name, + get_product_name_template, +) + +from .subset_name import ( get_subset_name_template, get_subset_name, ) @@ -56,10 +61,13 @@ __all__ = ( "get_last_versions_for_instances", "get_next_versions_for_instances", - "TaskNotSetError", "get_subset_name_template", "get_subset_name", + "TaskNotSetError", + "get_product_name", + "get_product_name_template", + "CreatorError", "BaseCreator", diff --git a/client/ayon_core/pipeline/create/product_name.py b/client/ayon_core/pipeline/create/product_name.py new file mode 100644 index 0000000000..52ba742910 --- /dev/null +++ b/client/ayon_core/pipeline/create/product_name.py @@ -0,0 +1,178 @@ +import os + +from ayon_core.settings import get_project_settings +from ayon_core.lib import filter_profiles, prepare_template_data + +from .constants import DEFAULT_SUBSET_TEMPLATE + + +class TaskNotSetError(KeyError): + def __init__(self, msg=None): + if not msg: + msg = "Creator's subset name template requires task name." + super(TaskNotSetError, self).__init__(msg) + + +class TemplateFillError(Exception): + def __init__(self, msg=None): + if not msg: + msg = "Creator's subset name template is missing key value." + super(TemplateFillError, self).__init__(msg) + + +def get_product_name_template( + project_name, + family, + task_name, + task_type, + host_name, + default_template=None, + project_settings=None +): + """Get subset name template based on passed context. + + Args: + project_name (str): Project on which the context lives. + family (str): Family (subset type) for which the subset name is + calculated. + host_name (str): Name of host in which the subset name is calculated. + task_name (str): Name of task in which context the subset is created. + task_type (str): Type of task in which context the subset is created. + default_template (Union[str, None]): Default template which is used if + settings won't find any matching possitibility. Constant + 'DEFAULT_SUBSET_TEMPLATE' is used if not defined. + project_settings (Union[Dict[str, Any], None]): Prepared settings for + project. Settings are queried if not passed. + """ + + if project_settings is None: + project_settings = get_project_settings(project_name) + tools_settings = project_settings["core"]["tools"] + profiles = tools_settings["creator"]["product_name_profiles"] + filtering_criteria = { + "product_types": family, + "hosts": host_name, + "tasks": task_name, + "task_types": task_type + } + + matching_profile = filter_profiles(profiles, filtering_criteria) + template = None + if matching_profile: + # TODO remove formatting keys replacement + template = ( + matching_profile["template"] + .replace("{task[name]}", "{task}") + .replace("{Task[name]}", "{Task}") + .replace("{TASK[NAME]}", "{TASK}") + .replace("{product[type]}", "{family}") + .replace("{Product[type]}", "{Family}") + .replace("{PRODUCT[TYPE]}", "{FAMILY}") + .replace("{folder[name]}", "{asset}") + .replace("{Folder[name]}", "{Asset}") + .replace("{FOLDER[NAME]}", "{ASSET}") + ) + + # Make sure template is set (matching may have empty string) + if not template: + template = default_template or DEFAULT_SUBSET_TEMPLATE + return template + + +def get_product_name( + project_name, + asset_doc, + task_name, + host_name, + product_type, + variant, + default_template=None, + dynamic_data=None, + project_settings=None, + family_filter=None, +): + """Calculate product name based on passed context and AYON settings. + + Subst name templates are defined in `project_settings/global/tools/creator + /product_name_profiles` where are profiles with host name, family, + task name and task type filters. If context does not match any profile + then `DEFAULT_SUBSET_TEMPLATE` is used as default template. + + That's main reason why so many arguments are required to calculate product + name. + + Todos: + Find better filtering options to avoid requirement of + argument 'family_filter'. + + Args: + project_name (str): Project name. + host_name (str): Host name. + product_type (str): Product type. + variant (str): In most of the cases it is user input during creation. + task_name (str): Task name on which context is instance created. + asset_doc (dict): Queried asset document with its tasks in data. + Used to get task type. + default_template (Optional[str]): Default template if any profile does + not match passed context. Constant 'DEFAULT_SUBSET_TEMPLATE' + is used if is not passed. + dynamic_data (Optional[Dict[str, Any]]): Dynamic data specific for + a creator which creates instance. + project_settings (Optional[Union[Dict[str, Any]]]): Prepared settings + for project. Settings are queried if not passed. + family_filter (Optional[str]): Use different family for subset template + filtering. Value of 'family' is used when not passed. + + Raises: + TemplateFillError: If filled template contains placeholder key which is not + collected. + """ + + if not product_type: + return "" + + asset_tasks = asset_doc.get("data", {}).get("tasks") or {} + task_info = asset_tasks.get(task_name) or {} + task_type = task_info.get("type") + + template = get_product_name_template( + project_name, + family_filter or product_type, + task_name, + task_type, + host_name, + default_template=default_template, + project_settings=project_settings + ) + # Simple check of task name existence for template with {task} in + # - missing task should be possible only in Standalone publisher + if not task_name and "{task" in template.lower(): + raise TaskNotSetError() + + task_value = { + "name": task_name, + "type": task_type, + } + if "{task}" in template.lower(): + task_value = task_name + + fill_pairs = { + "variant": variant, + "family": product_type, + "task": task_value, + "product": { + "type": product_type + } + } + if dynamic_data: + # Dynamic data may override default values + for key, value in dynamic_data.items(): + fill_pairs[key] = value + + try: + return template.format(**prepare_template_data(fill_pairs)) + except KeyError as exp: + raise TemplateFillError( + "Value for {} key is missing in template '{}'." + " Available values are {}".format(str(exp), template, fill_pairs) + ) diff --git a/client/ayon_core/pipeline/create/subset_name.py b/client/ayon_core/pipeline/create/subset_name.py index 5925ec0f2b..20025faaaa 100644 --- a/client/ayon_core/pipeline/create/subset_name.py +++ b/client/ayon_core/pipeline/create/subset_name.py @@ -4,20 +4,7 @@ from ayon_core.settings import get_project_settings from ayon_core.lib import filter_profiles, prepare_template_data from .constants import DEFAULT_SUBSET_TEMPLATE - - -class TaskNotSetError(KeyError): - def __init__(self, msg=None): - if not msg: - msg = "Creator's subset name template requires task name." - super(TaskNotSetError, self).__init__(msg) - - -class TemplateFillError(Exception): - def __init__(self, msg=None): - if not msg: - msg = "Creator's subset name template is missing key value." - super(TemplateFillError, self).__init__(msg) +from .product_name import TaskNotSetError, TemplateFillError def get_subset_name_template( From 44b80357d687c269c516cc68009056d2e2e358ca Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Feb 2024 15:29:01 +0100 Subject: [PATCH 217/573] use 'get_product_name' in core functionality --- client/ayon_core/pipeline/create/context.py | 10 +++++----- .../ayon_core/pipeline/create/creator_plugins.py | 14 +++++++------- client/ayon_core/pipeline/create/legacy_create.py | 14 +++++++------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index c50170f070..76c770eb37 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -2003,12 +2003,12 @@ class CreateContext: # TODO validate types _pre_create_data.update(pre_create_data) - subset_name = creator.get_subset_name( - variant, - task_name, - asset_doc, + subset_name = creator.get_product_name( project_name, - self.host_name + asset_doc, + task_name, + self.host_name, + variant, ) asset_name = get_asset_name_identifier(asset_doc) diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index 6fa0d2ffa1..9e39409f0c 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -17,7 +17,7 @@ from ayon_core.pipeline.plugin_discover import ( ) from .constants import DEFAULT_VARIANT_VALUE -from .subset_name import get_subset_name +from .product_name import get_product_name from .utils import get_next_versions_for_instances from .legacy_create import LegacyCreator @@ -507,7 +507,7 @@ class BaseCreator: return {} - def get_subset_name( + def get_product_name( self, variant, task_name, @@ -546,13 +546,13 @@ class BaseCreator: variant, task_name, asset_doc, project_name, host_name, instance ) - return get_subset_name( + return get_product_name( + project_name, + asset_doc, + task_name, + host_name, self.family, variant, - task_name, - asset_doc, - project_name, - host_name, dynamic_data=dynamic_data, project_settings=self.project_settings ) diff --git a/client/ayon_core/pipeline/create/legacy_create.py b/client/ayon_core/pipeline/create/legacy_create.py index bc1528b9cd..df0c942ffd 100644 --- a/client/ayon_core/pipeline/create/legacy_create.py +++ b/client/ayon_core/pipeline/create/legacy_create.py @@ -12,7 +12,7 @@ import collections from ayon_core.client import get_asset_by_id from ayon_core.pipeline.constants import AVALON_INSTANCE_ID -from .subset_name import get_subset_name +from .product_name import get_product_name class LegacyCreator(object): @@ -123,7 +123,7 @@ class LegacyCreator(object): return dynamic_data @classmethod - def get_subset_name( + def get_product_name( cls, variant, task_name, asset_id, project_name, host_name=None ): """Return subset name created with entered arguments. @@ -155,13 +155,13 @@ class LegacyCreator(object): project_name, asset_id, fields=["data.tasks"] ) - return get_subset_name( + return get_product_name( + project_name, + asset_doc, + task_name, + host_name, cls.family, variant, - task_name, - asset_doc, - project_name, - host_name, dynamic_data=dynamic_data ) From 6cff87d7a6b8f4c84547967ca670672f99511f7b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Feb 2024 15:35:48 +0100 Subject: [PATCH 218/573] CreateContext is using only product name and type --- client/ayon_core/pipeline/create/README.md | 18 ++--- client/ayon_core/pipeline/create/context.py | 75 +++++++++++---------- 2 files changed, 49 insertions(+), 44 deletions(-) diff --git a/client/ayon_core/pipeline/create/README.md b/client/ayon_core/pipeline/create/README.md index 5e3bd7dc1a..a2993bc121 100644 --- a/client/ayon_core/pipeline/create/README.md +++ b/client/ayon_core/pipeline/create/README.md @@ -6,7 +6,7 @@ Entry point of creation. All data and metadata are handled through create contex Discovers Creator plugins to be able create new instances and convert existing instances. Creators may have defined attributes that are specific for their instances. Attributes definition can enhance behavior of instance during publishing. -Publish plugins are loaded because they can also define attributes definitions. These are less family specific To be able define attributes Publish plugin must inherit from `AYONPyblishPluginMixin` and must override `get_attribute_defs` class method which must return list of attribute definitions. Values of publish plugin definitions are stored per plugin name under `publish_attributes`. Also can override `convert_attribute_values` class method which gives ability to modify values on instance before are used in CreatedInstance. Method `convert_attribute_values` can be also used without `get_attribute_defs` to modify values when changing compatibility (remove metadata from instance because are irrelevant). +Publish plugins are loaded because they can also define attributes definitions. These are less product type specific To be able define attributes Publish plugin must inherit from `AYONPyblishPluginMixin` and must override `get_attribute_defs` class method which must return list of attribute definitions. Values of publish plugin definitions are stored per plugin name under `publish_attributes`. Also can override `convert_attribute_values` class method which gives ability to modify values on instance before are used in CreatedInstance. Method `convert_attribute_values` can be also used without `get_attribute_defs` to modify values when changing compatibility (remove metadata from instance because are irrelevant). Possible attribute definitions can be found in `openpype/pipeline/lib/attribute_definitions.py`. @@ -14,9 +14,9 @@ Except creating and removing instances are all changes not automatically propaga ## CreatedInstance -Product of creation is "instance" which holds basic data defying it. Core data are `creator_identifier`, `family` and `subset`. Other data can be keys used to fill subset name or metadata modifying publishing process of the instance (more described later). All instances have `id` which holds constant `ayon.create.instance` or `pyblish.avalon.instance` (for backwards compatibility) and `instance_id` which is identifier of the instance. -Family tells how should be instance processed and subset what name will published item have. -- There are cases when subset is not fully filled during creation and may change during publishing. That is in most of cases caused because instance is related to other instance or instance data do not represent final product. +Product of creation is "instance" which holds basic data defying it. Core data are `creator_identifier`, `productType` and `productName`. Other data can be keys used to fill subset name or metadata modifying publishing process of the instance (more described later). All instances have `id` which holds constant `ayon.create.instance` or `pyblish.avalon.instance` (for backwards compatibility) and `instance_id` which is identifier of the instance. +Product type tells how should be instance processed and product name what name will published item have. +- There are cases when product name is not fully filled during creation and may change during publishing. That is in most of cases caused because instance is related to other instance or instance data do not represent final product. `CreatedInstance` is entity holding the data which are stored and used. @@ -27,12 +27,12 @@ Family tells how should be instance processed and subset what name will publishe "id": "ayon.create.instance", ## Identifier of this specific instance (automatically assigned) "instance_id": , - ## Instance family (used from Creator) - "family": , + ## Instance product type (used from Creator) + "productType": , # Mutable data - ## Subset name based on subset name template - may change overtime (on context change) - "subset": , + ## Subset name based on product name template - may change overtime (on context change) + "productName": , ## Instance is active and will be published "active": True, ## Version of instance @@ -54,7 +54,7 @@ Family tells how should be instance processed and subset what name will publishe ``` ## Creator -To be able create, update, remove or collect existing instances there must be defined a creator. Creator must have unique identifier and can represents a family. There can be multiple Creators for single family. Identifier of creator should contain family (advise). +To be able create, update, remove or collect existing instances there must be defined a creator. Creator must have unique identifier and can represent a product type. There can be multiple Creators for single product type. Identifier of creator should contain product type (advise). Creator has abstract methods to handle instances. For new instance creation is used `create` which should create metadata in host context and add new instance object to `CreateContext`. To collect existing instances is used `collect_instances` which should find all existing instances related to creator and add them to `CreateContext`. To update data of instance is used `update_instances` which is called from `CreateContext` on `save_changes`. To remove instance use `remove_instances` which should remove metadata from host context and remove instance from `CreateContext`. diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index 76c770eb37..69f4908c53 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -94,7 +94,7 @@ class ConvertorsOperationFailed(Exception): class ConvertorsFindFailed(ConvertorsOperationFailed): def __init__(self, failed_info): - msg = "Failed to find incompatible subsets" + msg = "Failed to find incompatible products" super(ConvertorsFindFailed, self).__init__( msg, failed_info ) @@ -102,7 +102,7 @@ class ConvertorsFindFailed(ConvertorsOperationFailed): class ConvertorsConversionFailed(ConvertorsOperationFailed): def __init__(self, failed_info): - msg = "Failed to convert incompatible subsets" + msg = "Failed to convert incompatible products" super(ConvertorsConversionFailed, self).__init__( msg, failed_info ) @@ -857,8 +857,8 @@ class CreatedInstance: """Instance entity with data that will be stored to workfile. I think `data` must be required argument containing all minimum information - about instance like "asset" and "task" and all data used for filling subset - name as creators may have custom data for subset name filling. + about instance like "asset" and "task" and all data used for filling + product name as creators may have custom data for product name filling. Notes: Object have 2 possible initialization. One using 'creator' object which @@ -867,8 +867,8 @@ class CreatedInstance: Args: family (str): Name of family that will be created. - subset_name (str): Name of subset that will be created. - data (Dict[str, Any]): Data used for filling subset name or override + product_name (str): Name of product that will be created. + data (Dict[str, Any]): Data used for filling product name or override data from already existing instance. creator (Union[BaseCreator, None]): Creator responsible for instance. creator_identifier (str): Identifier of creator plugin. @@ -893,8 +893,8 @@ class CreatedInstance: def __init__( self, - family, - subset_name, + product_type, + product_name, data, creator=None, creator_identifier=None, @@ -929,8 +929,10 @@ class CreatedInstance: # Store original value of passed data self._orig_data = copy.deepcopy(data) - # Pop family and subset to prevent unexpected changes - # TODO change to 'productType' and 'productName' in AYON + # Pop 'productType' and 'productName' to prevent unexpected changes + data.pop("productType", None) + data.pop("productName", None) + # Backwards compatibility with OpenPype instances data.pop("family", None) data.pop("subset", None) @@ -946,8 +948,8 @@ class CreatedInstance: if item_id not in {AYON_INSTANCE_ID, AVALON_INSTANCE_ID}: item_id = AVALON_INSTANCE_ID self._data["id"] = item_id - self._data["family"] = family - self._data["subset"] = subset_name + self._data["productType"] = product_type + self._data["productName"] = product_name self._data["active"] = data.get("active", True) self._data["creator_identifier"] = creator_identifier @@ -988,12 +990,11 @@ class CreatedInstance: def __str__(self): return ( - "" - " {data}" + " {data}" ).format( - subset=str(self._data), creator_identifier=self.creator_identifier, - family=self.family, + product={"name": self.product_name, "type": self.product_type}, data=str(self._data) ) @@ -1034,18 +1035,18 @@ class CreatedInstance: # ------ @property - def family(self): - return self._data["family"] + def product_type(self): + return self._data["productType"] @property - def subset_name(self): - return self._data["subset"] + def product_name(self): + return self._data["productName"] @property def label(self): label = self._data.get("label") if not label: - label = self.subset_name + label = self.product_name return label @property @@ -1192,13 +1193,17 @@ class CreatedInstance: instance_data = copy.deepcopy(instance_data) - family = instance_data.get("family", None) - if family is None: - family = creator.family - subset_name = instance_data.get("subset", None) + product_type = instance_data.get("productType", None) + if not product_type: + product_type = instance_data.get("family", None) + if product_type is None: + product_type = creator.product_type + product_name = instance_data.get("productName", None) + if not product_name: + product_name = instance_data.get("subset", None) return cls( - family, subset_name, instance_data, creator + product_type, product_name, instance_data, creator ) def set_publish_plugins(self, attr_plugins): @@ -1255,8 +1260,8 @@ class CreatedInstance: instance_data = copy.deepcopy(serialized_data["data"]) creator_identifier = instance_data["creator_identifier"] - family = instance_data["family"] - subset_name = instance_data.get("subset", None) + product_type = instance_data["productType"] + product_name = instance_data.get("productName", None) creator_label = serialized_data["creator_label"] group_label = serialized_data["group_label"] @@ -1266,8 +1271,8 @@ class CreatedInstance: publish_attributes = serialized_data["publish_attributes"] obj = cls( - family, - subset_name, + product_type, + product_name, instance_data, creator_identifier=creator_identifier, creator_label=creator_label, @@ -1960,11 +1965,11 @@ class CreateContext: values. If only 'task_name' is provided it will be overriden by task name from current context. If 'task_name' is not provided when 'asset_doc' is, it is considered that task name is not specified, - which can lead to error if subset name template requires task name. + which can lead to error if product name template requires task name. Args: creator_identifier (str): Identifier of creator plugin. - variant (str): Variant used for subset name. + variant (str): Variant used for product name. asset_doc (Dict[str, Any]): Asset document which define context of creation (possible context of created instance/s). task_name (str): Name of task to which is context related. @@ -2003,7 +2008,7 @@ class CreateContext: # TODO validate types _pre_create_data.update(pre_create_data) - subset_name = creator.get_product_name( + product_name = creator.get_product_name( project_name, asset_doc, task_name, @@ -2015,11 +2020,11 @@ class CreateContext: instance_data = { "folderPath": asset_name, "task": task_name, - "family": creator.family, + "productType": creator.product_type, "variant": variant } return creator.create( - subset_name, + product_name, instance_data, _pre_create_data ) From 3b2dc131f9a3dd2bca014b24ce4ec94e614547a8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Feb 2024 15:44:18 +0100 Subject: [PATCH 219/573] modified remaining base of create pipeline api to use product name and type --- .../pipeline/create/creator_plugins.py | 54 ++++++++--------- .../pipeline/create/legacy_create.py | 44 +++++++------- client/ayon_core/pipeline/create/utils.py | 58 +++++++++---------- .../tools/push_to_project/control.py | 4 +- 4 files changed, 80 insertions(+), 80 deletions(-) diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index 9e39409f0c..21d4371dc3 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -179,7 +179,7 @@ class BaseCreator: # Creator is enabled (Probably does not have reason of existence?) enabled = True - # Creator (and family) icon + # Creator (and product type) icon # - may not be used if `get_icon` is reimplemented icon = None @@ -329,14 +329,14 @@ class BaseCreator: def identifier(self): """Identifier of creator (must be unique). - Default implementation returns plugin's family. + Default implementation returns plugin's product type. """ - return self.family + return self.product_type @property @abstractmethod - def family(self): + def product_type(self): """Family that plugin represents.""" pass @@ -370,7 +370,7 @@ class BaseCreator: Default implementation use attributes in this order: - 'group_label' -> 'label' -> 'identifier' - Keep in mind that 'identifier' use 'family' by default. + Keep in mind that 'identifier' use 'product_type' by default. Returns: str: Group label that can be used for grouping of instances in UI. @@ -489,7 +489,7 @@ class BaseCreator: pass def get_icon(self): - """Icon of creator (family). + """Icon of creator (product type). Can return path to image file or awesome icon name. """ @@ -499,7 +499,7 @@ class BaseCreator: def get_dynamic_data( self, variant, task_name, asset_doc, project_name, host_name, instance ): - """Dynamic data for subset name filling. + """Dynamic data for product name filling. These may be get dynamically created based on current context of workfile. @@ -516,7 +516,7 @@ class BaseCreator: host_name=None, instance=None ): - """Return subset name for passed context. + """Return product name for passed context. CHANGES: Argument `asset_id` was replaced with `asset_doc`. It is easier to @@ -526,19 +526,19 @@ class BaseCreator: NOTE: Asset document is not used yet but is required if would like to use - task type in subset templates. + task type in product templates. - Method is also called on subset name update. In that case origin + Method is also called on product name update. In that case origin instance is passed in. Args: variant(str): Subset name variant. In most of cases user input. - task_name(str): For which task subset is created. - asset_doc(dict): Asset document for which subset is created. + task_name(str): For which task product is created. + asset_doc(dict): Asset document for which product is created. project_name(str): Project name. - host_name(str): Which host creates subset. + host_name(str): Which host creates product. instance(CreatedInstance|None): Object of 'CreatedInstance' for - which is subset name updated. Passed only on subset name + which is product name updated. Passed only on product name update. """ @@ -551,7 +551,7 @@ class BaseCreator: asset_doc, task_name, host_name, - self.family, + self.product_type, variant, dynamic_data=dynamic_data, project_settings=self.project_settings @@ -599,8 +599,8 @@ class BaseCreator: """Prepare next versions for instances. This is helper method to receive next possible versions for instances. - It is using context information on instance to receive them, 'asset' - and 'subset'. + It is using context information on instance to receive them, + 'folderPath' and 'product'. Output will contain version by each instance id. @@ -620,7 +620,7 @@ class BaseCreator: class Creator(BaseCreator): """Creator that has more information for artist to show in UI. - Creation requires prepared subset name and instance data. + Creation requires prepared product name and instance data. """ # GUI Purposes @@ -630,11 +630,11 @@ class Creator(BaseCreator): # Default variant used in 'get_default_variant' _default_variant = None - # Short description of family + # Short description of product type # - may not be used if `get_description` is overriden description = None - # Detailed description of family for artists + # Detailed description of product type for artists # - may not be used if `get_detail_description` is overriden detailed_description = None @@ -681,39 +681,39 @@ class Creator(BaseCreator): return self.order @abstractmethod - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): """Create new instance and store it. Ideally should be stored to workfile using host implementation. Args: - subset_name(str): Subset name of created instance. + product_name(str): Subset name of created instance. instance_data(dict): Base data for instance. pre_create_data(dict): Data based on pre creation attributes. Those may affect how creator works. """ # instance = CreatedInstance( - # self.family, subset_name, instance_data + # self.product_type, product_name, instance_data # ) pass def get_description(self): - """Short description of family and plugin. + """Short description of product type and plugin. Returns: - str: Short description of family. + str: Short description of product type. """ return self.description def get_detail_description(self): - """Description of family and plugin. + """Description of product type and plugin. Can be detailed with markdown or html tags. Returns: - str: Detailed description of family for artist. + str: Detailed description of product type for artist. """ return self.detailed_description diff --git a/client/ayon_core/pipeline/create/legacy_create.py b/client/ayon_core/pipeline/create/legacy_create.py index df0c942ffd..cf78f5b061 100644 --- a/client/ayon_core/pipeline/create/legacy_create.py +++ b/client/ayon_core/pipeline/create/legacy_create.py @@ -18,12 +18,12 @@ from .product_name import get_product_name class LegacyCreator(object): """Determine how assets are created""" label = None - family = None + product_type = None defaults = None maintain_selection = True enabled = True - dynamic_subset_keys = [] + dynamic_product_name_keys = [] log = logging.getLogger("LegacyCreator") log.propagate = True @@ -36,9 +36,9 @@ class LegacyCreator(object): self.data = collections.OrderedDict() # TODO use 'AYON_INSTANCE_ID' when all hosts support it self.data["id"] = AVALON_INSTANCE_ID - self.data["family"] = self.family + self.data["productType"] = self.product_type self.data["folderPath"] = folder_path - self.data["subset"] = name + self.data["productName"] = name self.data["active"] = True self.data.update(data or {}) @@ -93,19 +93,19 @@ class LegacyCreator(object): ): """Return dynamic data for current Creator plugin. - By default return keys from `dynamic_subset_keys` attribute as mapping - to keep formatted template unchanged. + By default return keys from `dynamic_product_name_keys` attribute + as mapping to keep formatted template unchanged. ``` - dynamic_subset_keys = ["my_key"] + dynamic_product_name_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. + Dynamic keys may override default Creator keys (productType, task, + folderPath, ...) 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. @@ -114,10 +114,10 @@ class LegacyCreator(object): is class method. Returns: - dict: Fill data for subset name template. + dict: Fill data for product name template. """ dynamic_data = {} - for key in cls.dynamic_subset_keys: + for key in cls.dynamic_product_name_keys: key = key.lower() dynamic_data[key] = "{" + key + "}" return dynamic_data @@ -126,14 +126,14 @@ class LegacyCreator(object): def get_product_name( cls, variant, task_name, asset_id, project_name, host_name=None ): - """Return subset name created with entered arguments. + """Return product name created with entered arguments. Logic extracted from Creator tool. This method should give ability - to get subset name without the tool. + to get product name without the tool. TODO: Maybe change `variant` variable. - By default is output concatenated family with user text. + By default is output concatenated product type with variant. Args: variant (str): What is entered by user in creator tool. @@ -143,7 +143,7 @@ class LegacyCreator(object): host_name (str): Name of host. Returns: - str: Formatted subset name with entered arguments. Should match + str: Formatted product name with entered arguments. Should match config's logic. """ @@ -160,7 +160,7 @@ class LegacyCreator(object): asset_doc, task_name, host_name, - cls.family, + cls.product_type, variant, dynamic_data=dynamic_data ) @@ -169,23 +169,23 @@ class LegacyCreator(object): 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`. + Associate nodes with a product name and type. These nodes are later + validated, according to their `product type`, and integrated into the + shared environment, relative their `productName`. - Data relative each family, along with default data, are imprinted + Data relative each product type, 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 + name (str): Name of product 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 + NameError on `productName` already exists KeyError on invalid dynamic property RuntimeError on host error diff --git a/client/ayon_core/pipeline/create/utils.py b/client/ayon_core/pipeline/create/utils.py index c2655f319f..44063bd9ac 100644 --- a/client/ayon_core/pipeline/create/utils.py +++ b/client/ayon_core/pipeline/create/utils.py @@ -11,14 +11,14 @@ from ayon_core.client import ( def get_last_versions_for_instances( project_name, instances, use_value_for_missing=False ): - """Get last versions for instances by their asset and subset name. + """Get last versions for instances by their folder path and product name. Args: project_name (str): Project name. instances (list[CreatedInstance]): Instances to get next versions for. use_value_for_missing (Optional[bool]): Missing values are replaced with negative value if True. Otherwise None is used. -2 is used - for instances without filled asset or subset name. -1 is used + for instances without filled folder or product name. -1 is used for missing entities. Returns: @@ -29,72 +29,72 @@ def get_last_versions_for_instances( instance.id: -1 if use_value_for_missing else None for instance in instances } - subset_names_by_asset_name = collections.defaultdict(set) + product_names_by_folder_path = collections.defaultdict(set) instances_by_hierarchy = {} for instance in instances: - asset_name = instance.data.get("folderPath") - subset_name = instance.subset_name - if not asset_name or not subset_name: + folder_path = instance.data.get("folderPath") + product_name = instance.product_name + if not folder_path or not product_name: if use_value_for_missing: output[instance.id] = -2 continue ( instances_by_hierarchy - .setdefault(asset_name, {}) - .setdefault(subset_name, []) + .setdefault(folder_path, {}) + .setdefault(product_name, []) .append(instance) ) - subset_names_by_asset_name[asset_name].add(subset_name) + product_names_by_folder_path[folder_path].add(product_name) - subset_names = set() - for names in subset_names_by_asset_name.values(): - subset_names |= names + product_names = set() + for names in product_names_by_folder_path.values(): + product_names |= names - if not subset_names: + if not product_names: return output asset_docs = get_assets( project_name, - asset_names=subset_names_by_asset_name.keys(), + asset_names=product_names_by_folder_path.keys(), fields=["name", "_id", "data.parents"] ) - asset_names_by_id = { + folder_paths_by_id = { asset_doc["_id"]: get_asset_name_identifier(asset_doc) for asset_doc in asset_docs } - if not asset_names_by_id: + if not folder_paths_by_id: return output subset_docs = get_subsets( project_name, - asset_ids=asset_names_by_id.keys(), - subset_names=subset_names, + asset_ids=folder_paths_by_id.keys(), + subset_names=product_names, fields=["_id", "name", "parent"] ) subset_docs_by_id = {} for subset_doc in subset_docs: # Filter subset docs by subset names under parent - asset_id = subset_doc["parent"] - asset_name = asset_names_by_id[asset_id] - subset_name = subset_doc["name"] - if subset_name not in subset_names_by_asset_name[asset_name]: + folder_id = subset_doc["parent"] + folder_path = folder_paths_by_id[folder_id] + product_name = subset_doc["name"] + if product_name not in product_names_by_folder_path[folder_path]: continue subset_docs_by_id[subset_doc["_id"]] = subset_doc if not subset_docs_by_id: return output - last_versions_by_subset_id = get_last_versions( + last_versions_by_product_id = get_last_versions( project_name, subset_docs_by_id.keys(), fields=["name", "parent"] ) - for subset_id, version_doc in last_versions_by_subset_id.items(): + for subset_id, version_doc in last_versions_by_product_id.items(): subset_doc = subset_docs_by_id[subset_id] - asset_id = subset_doc["parent"] - asset_name = asset_names_by_id[asset_id] - _instances = instances_by_hierarchy[asset_name][subset_doc["name"]] + folder_id = subset_doc["parent"] + folder_path = folder_paths_by_id[folder_id] + _instances = instances_by_hierarchy[folder_path][subset_doc["name"]] for instance in _instances: output[instance.id] = version_doc["name"] @@ -102,7 +102,7 @@ def get_last_versions_for_instances( def get_next_versions_for_instances(project_name, instances): - """Get next versions for instances by their asset and subset name. + """Get next versions for instances by their folder path and product name. Args: project_name (str): Project name. @@ -110,7 +110,7 @@ def get_next_versions_for_instances(project_name, instances): Returns: dict[str, Union[int, None]]: Next versions by instance id. Version is - 'None' if instance has no asset or subset name. + 'None' if instance has no folder path or product name. """ last_versions = get_last_versions_for_instances( diff --git a/client/ayon_core/tools/push_to_project/control.py b/client/ayon_core/tools/push_to_project/control.py index 1336721e5a..b3a5382343 100644 --- a/client/ayon_core/tools/push_to_project/control.py +++ b/client/ayon_core/tools/push_to_project/control.py @@ -9,7 +9,7 @@ from ayon_core.client import ( from ayon_core.settings import get_project_settings from ayon_core.lib import prepare_template_data from ayon_core.lib.events import QueuedEventSystem -from ayon_core.pipeline.create import get_subset_name_template +from ayon_core.pipeline.create import get_product_name_template from ayon_core.tools.ayon_utils.models import ProjectsModel, HierarchyModel from .models import ( @@ -259,7 +259,7 @@ class PushToContextController: family = subset_doc["data"].get("family") if not family: family = subset_doc["data"]["families"][0] - template = get_subset_name_template( + template = get_product_name_template( self._src_project_name, family, task_name, From 88786d8160c3b02cdb871fd11daa0859fc09df3d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Feb 2024 17:31:33 +0100 Subject: [PATCH 220/573] more usage of product type in create api --- client/ayon_core/pipeline/create/README.md | 2 +- client/ayon_core/pipeline/create/context.py | 30 +++++++++---------- .../ayon_core/pipeline/create/product_name.py | 26 ++++++++-------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/client/ayon_core/pipeline/create/README.md b/client/ayon_core/pipeline/create/README.md index a2993bc121..353f28e88b 100644 --- a/client/ayon_core/pipeline/create/README.md +++ b/client/ayon_core/pipeline/create/README.md @@ -14,7 +14,7 @@ Except creating and removing instances are all changes not automatically propaga ## CreatedInstance -Product of creation is "instance" which holds basic data defying it. Core data are `creator_identifier`, `productType` and `productName`. Other data can be keys used to fill subset name or metadata modifying publishing process of the instance (more described later). All instances have `id` which holds constant `ayon.create.instance` or `pyblish.avalon.instance` (for backwards compatibility) and `instance_id` which is identifier of the instance. +Product of creation is "instance" which holds basic data defying it. Core data are `creator_identifier`, `productType` and `productName`. Other data can be keys used to fill product name or metadata modifying publishing process of the instance (more described later). All instances have `id` which holds constant `ayon.create.instance` or `pyblish.avalon.instance` (for backwards compatibility) and `instance_id` which is identifier of the instance. Product type tells how should be instance processed and product name what name will published item have. - There are cases when product name is not fully filled during creation and may change during publishing. That is in most of cases caused because instance is related to other instance or instance data do not represent final product. diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index 69f4908c53..1a551f04c3 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -866,7 +866,7 @@ class CreatedInstance: creator. Args: - family (str): Name of family that will be created. + product_type (str): Product type that will be created. product_name (str): Name of product that will be created. data (Dict[str, Any]): Data used for filling product name or override data from already existing instance. @@ -885,7 +885,7 @@ class CreatedInstance: __immutable_keys = ( "id", "instance_id", - "family", + "product_type", "creator_identifier", "creator_attributes", "publish_attributes" @@ -1436,7 +1436,7 @@ class CreateContext: self.publish_plugins_mismatch_targets = [] self.publish_plugins = [] self.plugins_with_defs = [] - self._attr_plugins_by_family = {} + self._attr_plugins_by_product_type = {} # Helpers for validating context of collected instances # - they can be validation for multiple instances at one time @@ -1745,7 +1745,7 @@ class CreateContext: ) # Reset publish plugins - self._attr_plugins_by_family = {} + self._attr_plugins_by_product_type = {} discover_result = DiscoverResult(pyblish.api.Plugin) plugins_with_defs = [] @@ -1917,8 +1917,8 @@ class CreateContext: self._instances_by_id[instance.id] = instance # Prepare publish plugin attributes and set it on instance - attr_plugins = self._get_publish_plugins_with_attr_for_family( - instance.family + attr_plugins = self._get_publish_plugins_with_attr_for_product_type( + instance.product_type ) instance.set_publish_plugins(attr_plugins) @@ -2431,29 +2431,29 @@ class CreateContext: if failed_info: raise CreatorsRemoveFailed(failed_info) - def _get_publish_plugins_with_attr_for_family(self, family): - """Publish plugin attributes for passed family. + def _get_publish_plugins_with_attr_for_product_type(self, product_type): + """Publish plugin attributes for passed product type. - Attribute definitions for specific family are cached. + Attribute definitions for specific product type are cached. Args: - family(str): Instance family for which should be attribute - definitions returned. + product_type(str): Instance product type for which should be + attribute definitions returned. """ - if family not in self._attr_plugins_by_family: + if product_type not in self._attr_plugins_by_product_type: import pyblish.logic filtered_plugins = pyblish.logic.plugins_by_families( - self.plugins_with_defs, [family] + self.plugins_with_defs, [product_type] ) plugins = [] for plugin in filtered_plugins: if plugin.__instanceEnabled__: plugins.append(plugin) - self._attr_plugins_by_family[family] = plugins + self._attr_plugins_by_product_type[product_type] = plugins - return self._attr_plugins_by_family[family] + return self._attr_plugins_by_product_type[product_type] def _get_publish_plugins_with_attr_for_context(self): """Publish plugins attributes for Context plugins. diff --git a/client/ayon_core/pipeline/create/product_name.py b/client/ayon_core/pipeline/create/product_name.py index 52ba742910..ece8a68b2a 100644 --- a/client/ayon_core/pipeline/create/product_name.py +++ b/client/ayon_core/pipeline/create/product_name.py @@ -9,35 +9,35 @@ from .constants import DEFAULT_SUBSET_TEMPLATE class TaskNotSetError(KeyError): def __init__(self, msg=None): if not msg: - msg = "Creator's subset name template requires task name." + msg = "Creator's product name template requires task name." super(TaskNotSetError, self).__init__(msg) class TemplateFillError(Exception): def __init__(self, msg=None): if not msg: - msg = "Creator's subset name template is missing key value." + msg = "Creator's product name template is missing key value." super(TemplateFillError, self).__init__(msg) def get_product_name_template( project_name, - family, + product_type, task_name, task_type, host_name, default_template=None, project_settings=None ): - """Get subset name template based on passed context. + """Get product name template based on passed context. Args: project_name (str): Project on which the context lives. - family (str): Family (subset type) for which the subset name is + product_type (str): Product type for which the subset name is calculated. - host_name (str): Name of host in which the subset name is calculated. - task_name (str): Name of task in which context the subset is created. - task_type (str): Type of task in which context the subset is created. + host_name (str): Name of host in which the product name is calculated. + task_name (str): Name of task in which context the product is created. + task_type (str): Type of task in which context the product is created. default_template (Union[str, None]): Default template which is used if settings won't find any matching possitibility. Constant 'DEFAULT_SUBSET_TEMPLATE' is used if not defined. @@ -50,7 +50,7 @@ def get_product_name_template( tools_settings = project_settings["core"]["tools"] profiles = tools_settings["creator"]["product_name_profiles"] filtering_criteria = { - "product_types": family, + "product_types": product_type, "hosts": host_name, "tasks": task_name, "task_types": task_type @@ -94,7 +94,7 @@ def get_product_name( """Calculate product name based on passed context and AYON settings. Subst name templates are defined in `project_settings/global/tools/creator - /product_name_profiles` where are profiles with host name, family, + /product_name_profiles` where are profiles with host name, product type, task name and task type filters. If context does not match any profile then `DEFAULT_SUBSET_TEMPLATE` is used as default template. @@ -120,12 +120,12 @@ def get_product_name( a creator which creates instance. project_settings (Optional[Union[Dict[str, Any]]]): Prepared settings for project. Settings are queried if not passed. - family_filter (Optional[str]): Use different family for subset template + family_filter (Optional[str]): Use different family for product template filtering. Value of 'family' is used when not passed. Raises: - TemplateFillError: If filled template contains placeholder key which is not - collected. + TemplateFillError: If filled template contains placeholder key which + is not collected. """ if not product_type: From 993b7619a7fbe089d558b5b5c4005abf8d825121 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Feb 2024 17:37:13 +0100 Subject: [PATCH 221/573] use product type and name in rest of pipeline functions --- .../pipeline/farm/pyblish_functions.py | 111 ++++--- .../pipeline/farm/pyblish_functions.pyi | 4 +- client/ayon_core/pipeline/farm/tools.py | 5 +- .../publish/abstract_collect_render.py | 15 +- client/ayon_core/pipeline/publish/lib.py | 57 ++-- client/ayon_core/pipeline/version_start.py | 10 +- .../pipeline/workfile/build_workfile.py | 278 +++++++++--------- .../pipeline/workfile/path_resolving.py | 2 +- 8 files changed, 250 insertions(+), 232 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 4652d34011..8d875ca56e 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -58,16 +58,16 @@ def remap_source(path, anatomy): return source -def extend_frames(asset, subset, start, end): +def extend_frames(folder_path, product_name, start, end): """Get latest version of asset nad update frame range. Based on minimum and maximum values. Arguments: - asset (str): asset name - subset (str): subset name - start (int): start frame - end (int): end frame + folder_path (str): Folder path. + product_name (str): Product name. + start (int): Start frame. + end (int): End frame. Returns: (int, int): update frame start/end @@ -80,8 +80,8 @@ def extend_frames(asset, subset, start, end): project_name = get_current_project_name() version = get_last_version_by_subset_name( project_name, - subset, - asset_name=asset + product_name, + asset_name=folder_path ) # Set prev start / end frames for comparison @@ -198,7 +198,7 @@ def create_skeleton_instance( if data.get("extendFrames", False): time_data.start, time_data.end = extend_frames( data["folderPath"], - data["subset"], + data["productName"], time_data.start, time_data.end, ) @@ -215,18 +215,18 @@ def create_skeleton_instance( log.warning(("Could not find root path for remapping \"{}\". " "This may cause issues.").format(source)) - family = ("render" + product_type = ("render" if "prerender.farm" not in instance.data["families"] else "prerender") - families = [family] + families = [product_type] # pass review to families if marked as review if data.get("review"): families.append("review") instance_skeleton_data = { - "family": family, - "subset": data["subset"], + "productType": product_type, + "productName": data["productName"], "families": families, "folderPath": data["folderPath"], "frameStart": time_data.start, @@ -472,8 +472,8 @@ def create_instances_for_aov(instance, skeleton, aov_filter, expected files. """ - # we cannot attach AOVs to other subsets as we consider every - # AOV subset of its own. + # we cannot attach AOVs to other products as we consider every + # AOV product of its own. log = Logger.get_logger("farm_publishing") additional_color_data = { @@ -493,7 +493,7 @@ def create_instances_for_aov(instance, skeleton, aov_filter, log.warning(e) additional_color_data["colorspaceTemplate"] = colorspace_template - # if there are subset to attach to and more than one AOV, + # if there are product to attach to and more than one AOV, # we cannot proceed. if ( len(instance.data.get("attachTo", [])) > 0 @@ -501,7 +501,7 @@ def create_instances_for_aov(instance, skeleton, aov_filter, ): raise KnownPublishError( "attaching multiple AOVs or renderable cameras to " - "subset is not supported yet.") + "product is not supported yet.") # create instances for every AOV we found in expected files. # NOTE: this is done for every AOV and every render camera (if @@ -544,7 +544,7 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, task = os.environ["AYON_TASK_NAME"] anatomy = instance.context.data["anatomy"] - subset = skeleton["subset"] + s_product_name = skeleton["productName"] cameras = instance.data.get("cameras", []) exp_files = instance.data["expectedFiles"] log = Logger.get_logger("farm_publishing") @@ -570,34 +570,33 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, ext = cols[0].tail.lstrip(".") col = list(cols[0]) - # create subset name `familyTaskSubset_AOV` + # create product name `` # TODO refactor/remove me - family = skeleton["family"] - if not subset.startswith(family): + product_type = skeleton["productType"] + if not s_product_name.startswith(product_type): group_name = '{}{}{}{}{}'.format( - family, + product_type, task[0].upper(), task[1:], - subset[0].upper(), subset[1:]) + s_product_name[0].upper(), s_product_name[1:]) else: - group_name = subset + group_name = s_product_name # if there are multiple cameras, we need to add camera name expected_filepath = col[0] if isinstance(col, (list, tuple)) else col cams = [cam for cam in cameras if cam in expected_filepath] if cams: for cam in cams: - if aov: - if not aov.startswith(cam): - subset_name = '{}_{}_{}'.format(group_name, cam, aov) - else: - subset_name = "{}_{}".format(group_name, aov) + if not aov: + product_name = '{}_{}'.format(group_name, cam) + elif not aov.startswith(cam): + product_name = '{}_{}_{}'.format(group_name, cam, aov) else: - subset_name = '{}_{}'.format(group_name, cam) + product_name = "{}_{}".format(group_name, aov) else: if aov: - subset_name = '{}_{}'.format(group_name, aov) + product_name = '{}_{}'.format(group_name, aov) else: - subset_name = '{}'.format(group_name) + product_name = '{}'.format(group_name) if isinstance(col, (list, tuple)): staging = os.path.dirname(col[0]) @@ -609,7 +608,7 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, except ValueError as e: log.warning(e) - log.info("Creating data for: {}".format(subset_name)) + log.info("Creating data for: {}".format(product_name)) app = os.environ.get("AYON_HOST_NAME", "") @@ -626,7 +625,7 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, preview = True new_instance = deepcopy(skeleton) - new_instance["subset"] = subset_name + new_instance["productName"] = product_name new_instance["subsetGroup"] = group_name # explicitly disable review by user @@ -778,7 +777,7 @@ def create_skeleton_instance_cache(instance): if data.get("extendFrames", False): time_data.start, time_data.end = extend_frames( data["folderPath"], - data["subset"], + data["productName"], time_data.start, time_data.end, ) @@ -795,15 +794,15 @@ def create_skeleton_instance_cache(instance): log.warning(("Could not find root path for remapping \"{}\". " "This may cause issues.").format(source)) - family = instance.data["family"] + product_type = instance.data["productType"] # Make sure "render" is in the families to go through # validating expected and rendered files # during publishing job. - families = ["render", family] + families = ["render", product_type] instance_skeleton_data = { - "family": family, - "subset": data["subset"], + "productType": product_type, + "productName": data["productName"], "families": families, "folderPath": data["folderPath"], "frameStart": time_data.start, @@ -910,8 +909,8 @@ def create_instances_for_cache(instance, skeleton): """ anatomy = instance.context.data["anatomy"] - subset = skeleton["subset"] - family = skeleton["family"] + product_name = skeleton["productName"] + product_type = skeleton["productType"] exp_files = instance.data["expectedFiles"] log = Logger.get_logger("farm_publishing") @@ -948,9 +947,9 @@ def create_instances_for_cache(instance, skeleton): new_instance = deepcopy(skeleton) - new_instance["subset"] = subset - log.info("Creating data for: {}".format(subset)) - new_instance["family"] = family + new_instance["productName"] = product_name + log.info("Creating data for: {}".format(product_name)) + new_instance["productType"] = product_type new_instance["families"] = skeleton["families"] # create representation if isinstance(col, (list, tuple)): @@ -984,7 +983,7 @@ def create_instances_for_cache(instance, skeleton): def copy_extend_frames(instance, representation): """Copy existing frames from latest version. - This will copy all existing frames from subset's latest version back + This will copy all existing frames from product's latest version back to render directory and rename them to what renderer is expecting. Arguments: @@ -1005,20 +1004,20 @@ def copy_extend_frames(instance, representation): project_name = instance.context.data["project"] anatomy = instance.context.data["anatomy"] # type: Anatomy - # get latest version of subset - # this will stop if subset wasn't published yet + # get latest version of product + # this will stop if product wasn't published yet version = get_last_version_by_subset_name( project_name, - instance.data.get("subset"), + instance.data.get("productName"), asset_name=instance.data.get("folderPath") ) # get its files based on extension - subset_resources = get_resources( + product_resources = get_resources( project_name, version, representation.get("ext") ) - r_col, _ = clique.assemble(subset_resources) + r_col, _ = clique.assemble(product_resources) # if override remove all frames we are expecting to be rendered, # so we'll copy only those missing from current render @@ -1064,11 +1063,11 @@ def copy_extend_frames(instance, representation): log.info("Finished copying %i files" % len(resource_files)) -def attach_instances_to_subset(attach_to, instances): - """Attach instance to subset. +def attach_instances_to_product(attach_to, instances): + """Attach instance to product. - If we are attaching to other subsets, create copy of existing - instances, change data to match its subset and replace + If we are attaching to other products, create copy of existing + instances, change data to match its product and replace existing instances with modified data. Args: @@ -1084,8 +1083,8 @@ def attach_instances_to_subset(attach_to, instances): for i in instances: new_inst = copy.deepcopy(i) new_inst["version"] = attach_instance.get("version") - new_inst["subset"] = attach_instance.get("subset") - new_inst["family"] = attach_instance.get("family") + new_inst["productName"] = attach_instance.get("productName") + new_inst["productType"] = attach_instance.get("productType") new_inst["append"] = True # don't set subsetGroup if we are attaching new_inst.pop("subsetGroup") @@ -1108,7 +1107,7 @@ def create_metadata_path(instance, anatomy): # directory is not available log.warning("Path is unreachable: `{}`".format(output_dir)) - metadata_filename = "{}_metadata.json".format(ins_data["subset"]) + metadata_filename = "{}_metadata.json".format(ins_data["productName"]) metadata_path = os.path.join(output_dir, metadata_filename) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.pyi b/client/ayon_core/pipeline/farm/pyblish_functions.pyi index d9d46a63be..16c11aa480 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.pyi +++ b/client/ayon_core/pipeline/farm/pyblish_functions.pyi @@ -16,9 +16,9 @@ class TimeData: ... def remap_source(source: str, anatomy: Anatomy): ... -def extend_frames(asset: str, subset: str, start: int, end: int) -> Tuple[int, int]: ... +def extend_frames(folder_path: str, product_name: str, start: int, end: int) -> Tuple[int, int]: ... def get_time_data_from_instance_or_context(instance: pyblish.api.Instance) -> TimeData: ... def get_transferable_representations(instance: pyblish.api.Instance) -> list: ... def create_skeleton_instance(instance: pyblish.api.Instance, families_transfer: list = ..., instance_transfer: dict = ...) -> dict: ... def create_instances_for_aov(instance: pyblish.api.Instance, skeleton: dict, aov_filter: dict) -> List[pyblish.api.Instance]: ... -def attach_instances_to_subset(attach_to: list, instances: list) -> list: ... +def attach_instances_to_product(attach_to: list, instances: list) -> list: ... diff --git a/client/ayon_core/pipeline/farm/tools.py b/client/ayon_core/pipeline/farm/tools.py index f3acac7a32..8ab3b87ff6 100644 --- a/client/ayon_core/pipeline/farm/tools.py +++ b/client/ayon_core/pipeline/farm/tools.py @@ -5,8 +5,9 @@ def get_published_workfile_instance(context): """Find workfile instance in context""" for i in context: is_workfile = ( - "workfile" in i.data.get("families", []) or - i.data["family"] == "workfile" + i.data["productType"] == "workfile" + or "workfile" in i.data.get("families", []) + ) if not is_workfile: continue diff --git a/client/ayon_core/pipeline/publish/abstract_collect_render.py b/client/ayon_core/pipeline/publish/abstract_collect_render.py index 6ca1d81bc7..4ec38bbdd5 100644 --- a/client/ayon_core/pipeline/publish/abstract_collect_render.py +++ b/client/ayon_core/pipeline/publish/abstract_collect_render.py @@ -28,10 +28,11 @@ class RenderInstance(object): 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 - task = attr.ib() # task name + productType = attr.ib() # product type + productName = attr.ib() # product name folderPath = attr.ib() # folder path - attachTo = attr.ib() # subset name to attach render to + task = attr.ib() # task name + attachTo = attr.ib() # product name to attach render to setMembers = attr.ib() # list of nodes/members producing render output publish = attr.ib() # bool, True to publish instance name = attr.ib() # instance name @@ -60,7 +61,7 @@ class RenderInstance(object): review = attr.ib(default=None) # False - explicitly skip review priority = attr.ib(default=50) # job priority on farm - family = attr.ib(default="renderlayer") + # family = attr.ib(default="renderlayer") families = attr.ib(default=["renderlayer"]) # list of families # True if should be rendered on farm, eg not integrate farm = attr.ib(default=False) @@ -153,13 +154,13 @@ class AbstractCollectRender(pyblish.api.ContextPlugin): exp_files = self.get_expected_files(render_instance) assert exp_files, "no file names were generated, this is bug" - # if we want to attach render to subset, check if we have AOV's + # if we want to attach render to product, check if we have AOV's # in expectedFiles. If so, raise error as we cannot attach AOV - # (considered to be subset on its own) to another subset + # (considered to be product on its own) to another product if render_instance.attachTo: assert isinstance(exp_files, list), ( "attaching multiple AOVs or renderable cameras to " - "subset is not supported" + "product is not supported" ) frame_start_render = int(render_instance.frameStart) diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 7d980b4bbe..c61bac7f9a 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -105,7 +105,7 @@ def get_hero_template_name_profiles( def get_publish_template_name( project_name, host_name, - family, + product_type, task_name, task_type, project_settings=None, @@ -123,7 +123,7 @@ def get_publish_template_name( Args: project_name (str): Name of project where to look for settings. host_name (str): Name of host integration. - family (str): Family for which should be found template. + product_type (str): Product type for which should be found template. task_name (str): Task name on which is instance working. task_type (str): Task type on which is instance working. project_settings (Dict[str, Any]): Prepared project settings. @@ -138,7 +138,7 @@ def get_publish_template_name( template = None filter_criteria = { "hosts": host_name, - "product_types": family, + "product_types": product_type, "task_names": task_name, "task_types": task_type, } @@ -701,19 +701,26 @@ def get_publish_repre_path(instance, repre, only_published=False): return None -def get_custom_staging_dir_info(project_name, host_name, family, task_name, - task_type, subset_name, - project_settings=None, - anatomy=None, log=None): +def get_custom_staging_dir_info( + project_name, + host_name, + product_type, + task_name, + task_type, + product_name, + project_settings=None, + anatomy=None, + log=None +): """Checks profiles if context should use special custom dir as staging. Args: project_name (str) host_name (str) - family (str) + product_type (str) task_name (str) task_type (str) - subset_name (str) + product_name (str) project_settings(Dict[str, Any]): Prepared project settings. anatomy (Dict[str, Any]) log (Logger) (optional) @@ -736,10 +743,10 @@ def get_custom_staging_dir_info(project_name, host_name, family, task_name, filtering_criteria = { "hosts": host_name, - "families": family, + "families": product_type, "task_names": task_name, "task_types": task_type, - "subsets": subset_name + "subsets": product_name } profile = filter_profiles(custom_staging_dir_profiles, filtering_criteria, @@ -780,18 +787,18 @@ def _validate_transient_template(project_name, template_name, anatomy): def get_published_workfile_instance(context): """Find workfile instance in context""" for i in context: - is_workfile = ( - "workfile" in i.data.get("families", []) or - i.data["family"] == "workfile" - ) - if not is_workfile: - continue - # test if there is instance of workfile waiting # to be published. if not i.data.get("publish", True): continue + if not ( + i.data["productType"] == "workfile" + # QUESTION Is check in 'families' valid? + or "workfile" in i.data.get("families", []) + ): + continue + return i @@ -917,7 +924,7 @@ def get_publish_instance_label(instance): is used string conversion of instance object -> 'instance._name'. Todos: - Maybe 'subset' key could be used too. + Maybe 'productName' key could be used too. Args: instance (pyblish.api.Instance): Pyblish instance. @@ -936,8 +943,8 @@ def get_publish_instance_label(instance): def get_publish_instance_families(instance): """Get all families of the instance. - Look for families under 'family' and 'families' keys in instance data. - Value of 'family' is used as first family and then all other families + Look for families under 'productType' and 'families' keys in instance data. + Value of 'productType' is used as first family and then all other families in random order. Args: @@ -947,11 +954,11 @@ def get_publish_instance_families(instance): list[str]: List of families. """ - family = instance.data.get("family") + product_type = instance.data.get("productType") families = set(instance.data.get("families") or []) output = [] - if family: - output.append(family) - families.discard(family) + if product_type: + output.append(product_type) + families.discard(product_type) output.extend(families) return output diff --git a/client/ayon_core/pipeline/version_start.py b/client/ayon_core/pipeline/version_start.py index 4813910bf3..7ee20a5dd4 100644 --- a/client/ayon_core/pipeline/version_start.py +++ b/client/ayon_core/pipeline/version_start.py @@ -7,8 +7,8 @@ def get_versioning_start( host_name, task_name=None, task_type=None, - family=None, - subset=None, + product_type=None, + product_name=None, project_settings=None, ): """Get anatomy versioning start""" @@ -22,12 +22,14 @@ def get_versioning_start( if not profiles: return version_start + # TODO use 'product_types' and 'product_name' instead of + # 'families' and 'subsets' filtering_criteria = { "host_names": host_name, - "families": family, + "families": product_type, "task_names": task_name, "task_types": task_type, - "subsets": subset + "subsets": product_name } profile = filter_profiles(profiles, filtering_criteria) diff --git a/client/ayon_core/pipeline/workfile/build_workfile.py b/client/ayon_core/pipeline/workfile/build_workfile.py index 3a0d9ba95e..34d8ef0c8f 100644 --- a/client/ayon_core/pipeline/workfile/build_workfile.py +++ b/client/ayon_core/pipeline/workfile/build_workfile.py @@ -48,18 +48,18 @@ class BuildWorkfile: return self._log @staticmethod - def map_subsets_by_family(subsets): - subsets_by_family = collections.defaultdict(list) - for subset in subsets: - family = subset["data"].get("family") - if not family: - families = subset["data"].get("families") + def map_products_by_type(subset_docs): + products_by_type = collections.defaultdict(list) + for subset_doc in subset_docs: + product_type = subset_doc["data"].get("family") + if not product_type: + families = subset_doc["data"].get("families") if not families: continue - family = families[0] + product_type = families[0] - subsets_by_family[family].append(subset) - return subsets_by_family + products_by_type[product_type].append(subset_doc) + return products_by_type def process(self): """Main method of this wrapper. @@ -80,17 +80,17 @@ class BuildWorkfile: stored in Workfile profiles from presets. Profiles are set by host, filtered by current task name and used by families. - Each family can specify representation names and loaders for + Each product type can specify representation names and loaders for representations and first available and successful loaded representation is returned as container. At the end you'll get list of loaded containers per each asset. loaded_containers [{ - "asset_entity": , + "asset_doc": , "containers": [, , ...] }, { - "asset_entity": , + "asset_doc": , "containers": [, ...] }, { ... @@ -110,14 +110,14 @@ class BuildWorkfile: # Get current asset name and entity project_name = get_current_project_name() - current_asset_name = get_current_asset_name() - current_asset_entity = get_asset_by_name( - project_name, current_asset_name + current_folder_path = get_current_asset_name() + current_asset_doc = get_asset_by_name( + project_name, current_folder_path ) # Skip if asset was not found - if not current_asset_entity: - print("Asset entity with name `{}` was not found".format( - current_asset_name + if not current_asset_doc: + print("Folder entity `{}` was not found".format( + current_folder_path )) return loaded_containers @@ -143,7 +143,7 @@ class BuildWorkfile: # Load workfile presets for task self.build_presets = self.get_build_presets( - current_task_name, current_asset_entity + current_task_name, current_asset_doc ) # Skip if there are any presets for task @@ -155,9 +155,9 @@ class BuildWorkfile: ) return loaded_containers - # Get presets for loading current asset + # Get presets for loading current folder current_context_profiles = self.build_presets.get("current_context") - # Get presets for loading linked assets + # Get presets for loading linked folders link_context_profiles = self.build_presets.get("linked_assets") # Skip if both are missing if not current_context_profiles and not link_context_profiles: @@ -177,38 +177,38 @@ class BuildWorkfile: elif not link_context_profiles: self.log.warning(( "Current task `{}` doesn't have any" - "loading preset for it's linked assets." + "loading preset for it's linked folders." ).format(current_task_name)) # Prepare assets to process by workfile presets - assets = [] - current_asset_id = None + asset_docs = [] + current_folder_id = None if current_context_profiles: # Add current asset entity if preset has current context set - assets.append(current_asset_entity) - current_asset_id = current_asset_entity["_id"] + asset_docs.append(current_asset_doc) + current_folder_id = current_asset_doc["_id"] if link_context_profiles: # Find and append linked assets if preset has set linked mapping - link_assets = get_linked_assets(project_name, current_asset_entity) + link_assets = get_linked_assets(project_name, current_asset_doc) if link_assets: - assets.extend(link_assets) + asset_docs.extend(link_assets) # Skip if there are no assets. This can happen if only linked mapping # is set and there are no links for his asset. - if not assets: + if not asset_docs: self.log.warning( "Asset does not have linked assets. Nothing to process." ) return loaded_containers # Prepare entities from database for assets - prepared_entities = self._collect_last_version_repres(assets) + prepared_entities = self._collect_last_version_repres(asset_docs) # Load containers by prepared entities and presets # - Current asset containers - if current_asset_id and current_asset_id in prepared_entities: - current_context_data = prepared_entities.pop(current_asset_id) + if current_folder_id and current_folder_id in prepared_entities: + current_context_data = prepared_entities.pop(current_folder_id) loaded_data = self.load_containers_by_asset_data( current_context_data, current_context_profiles, loaders_by_name ) @@ -281,7 +281,7 @@ class BuildWorkfile: with valid values. - "loaders" expects list of strings representing possible loaders. - "families" expects list of strings for filtering - by main subset family. + by product type. - "repre_names" expects list of strings for filtering by representation name. @@ -349,33 +349,33 @@ class BuildWorkfile: return valid_profiles - def _prepare_profile_for_subsets(self, subsets, profiles): - """Select profile for each subset by it's data. + def _prepare_profile_for_products(self, subset_docs, profiles): + """Select profile for each product by it's data. - Profiles are filtered for each subset individually. - Profile is filtered by subset's family, optionally by name regex and + Profiles are filtered for each product individually. + Profile is filtered by product type, optionally by name regex and representation names set in profile. - It is possible to not find matching profile for subset, in that case - subset is skipped and it is possible that none of subsets have + It is possible to not find matching profile for product, in that case + product is skipped and it is possible that none of products have matching profile. Args: - subsets (List[Dict[str, Any]]): Subset documents. + subset_docs (List[Dict[str, Any]]): Subset documents. profiles (List[Dict[str, Any]]): Build profiles. Returns: - Dict[str, Any]: Profile by subset's id. + Dict[str, Any]: Profile by product id. """ - # Prepare subsets - subsets_by_family = self.map_subsets_by_family(subsets) + # Prepare products + products_by_type = self.map_products_by_type(subset_docs) - profiles_per_subset_id = {} - for family, subsets in subsets_by_family.items(): - family_low = family.lower() + profiles_by_product_id = {} + for product_type, subset_docs in products_by_type.items(): + product_type_low = product_type.lower() for profile in profiles: - # Skip profile if does not contain family - if family_low not in profile["product_types_lowered"]: + # Skip profile if does not contain product type + if product_type_low not in profile["product_types_lowered"]: continue # Precompile name filters as regexes @@ -387,31 +387,31 @@ class BuildWorkfile: profile_regexes = _profile_regexes # TODO prepare regex compilation - for subset in subsets: + for subset_doc in subset_docs: # Verify regex filtering (optional) if profile_regexes: valid = False for pattern in profile_regexes: - if re.match(pattern, subset["name"]): + if re.match(pattern, subset_doc["name"]): valid = True break if not valid: continue - profiles_per_subset_id[subset["_id"]] = profile + profiles_by_product_id[subset_doc["_id"]] = profile # break profiles loop on finding the first matching profile break - return profiles_per_subset_id + return profiles_by_product_id def load_containers_by_asset_data( - self, asset_entity_data, build_profiles, loaders_by_name + self, asset_doc_data, build_profiles, loaders_by_name ): """Load containers for entered asset entity by Build profiles. Args: - asset_entity_data (Dict[str, Any]): Prepared data with subsets, + asset_doc_data (Dict[str, Any]): Prepared data with products, last versions and representations for specific asset. build_profiles (Dict[str, Any]): Build profiles. loaders_by_name (Dict[str, LoaderPlugin]): Available loaders @@ -423,10 +423,10 @@ class BuildWorkfile: """ # Make sure all data are not empty - if not asset_entity_data or not build_profiles or not loaders_by_name: + if not asset_doc_data or not build_profiles or not loaders_by_name: return - asset_entity = asset_entity_data["asset_entity"] + asset_doc = asset_doc_data["asset_doc"] valid_profiles = self._filter_build_profiles( build_profiles, loaders_by_name @@ -439,53 +439,53 @@ class BuildWorkfile: self.log.debug("Valid Workfile profiles: {}".format(valid_profiles)) - subsets_by_id = {} - version_by_subset_id = {} + products_by_id = {} + version_by_product_id = {} repres_by_version_id = {} - for subset_id, in_data in asset_entity_data["subsets"].items(): - subset_entity = in_data["subset_entity"] - subsets_by_id[subset_entity["_id"]] = subset_entity + for product_id, in_data in asset_doc_data["subsets"].items(): + subset_doc = in_data["subset_doc"] + products_by_id[subset_doc["_id"]] = subset_doc version_data = in_data["version"] - version_entity = version_data["version_entity"] - version_by_subset_id[subset_id] = version_entity - repres_by_version_id[version_entity["_id"]] = ( + version_doc = version_data["version_doc"] + version_by_product_id[product_id] = version_doc + repres_by_version_id[version_doc["_id"]] = ( version_data["repres"] ) - if not subsets_by_id: - self.log.warning("There are not subsets for asset {0}".format( - asset_entity["name"] + if not products_by_id: + self.log.warning("There are not products for folder {0}".format( + asset_doc["name"] )) return - profiles_per_subset_id = self._prepare_profile_for_subsets( - subsets_by_id.values(), valid_profiles + profiles_by_product_id = self._prepare_profile_for_products( + products_by_id.values(), valid_profiles ) - if not profiles_per_subset_id: - self.log.warning("There are not valid subsets.") + if not profiles_by_product_id: + self.log.warning("There are not valid products.") return - valid_repres_by_subset_id = collections.defaultdict(list) - for subset_id, profile in profiles_per_subset_id.items(): + valid_repres_by_product_id = collections.defaultdict(list) + for product_id, profile in profiles_by_product_id.items(): profile_repre_names = profile["repre_names_lowered"] - version_entity = version_by_subset_id[subset_id] - version_id = version_entity["_id"] + version_doc = version_by_product_id[product_id] + version_id = version_doc["_id"] repres = repres_by_version_id[version_id] for repre in repres: repre_name_low = repre["name"].lower() if repre_name_low in profile_repre_names: - valid_repres_by_subset_id[subset_id].append(repre) + valid_repres_by_product_id[product_id].append(repre) # DEBUG message - msg = "Valid representations for Asset: `{}`".format( - asset_entity["name"] + msg = "Valid representations for Folder: `{}`".format( + asset_doc["name"] ) - for subset_id, repres in valid_repres_by_subset_id.items(): - subset = subsets_by_id[subset_id] - msg += "\n# Subset Name/ID: `{}`/{}".format( - subset["name"], subset_id + for product_id, repres in valid_repres_by_product_id.items(): + subset_doc = products_by_id[product_id] + msg += "\n# Product Name/ID: `{}`/{}".format( + subset_doc["name"], product_id ) for repre in repres: msg += "\n## Repre name: `{}`".format(repre["name"]) @@ -493,37 +493,37 @@ class BuildWorkfile: self.log.debug(msg) containers = self._load_containers( - valid_repres_by_subset_id, subsets_by_id, - profiles_per_subset_id, loaders_by_name + valid_repres_by_product_id, products_by_id, + profiles_by_product_id, loaders_by_name ) return { - "asset_entity": asset_entity, + "asset_doc": asset_doc, "containers": containers } def _load_containers( - self, repres_by_subset_id, subsets_by_id, - profiles_per_subset_id, loaders_by_name + self, repres_by_product_id, products_by_id, + profiles_by_product_id, loaders_by_name ): """Real load by collected data happens here. - Loading of representations per subset happens here. Each subset can + Loading of representations per product happens here. Each product can loads one representation. Loading is tried in specific order. Representations are tried to load by names defined in configuration. - If subset has representation matching representation name each loader + If product has representation matching representation name each loader is tried to load it until any is successful. If none of them was successful then next representation name is tried. Subset process loop ends when any representation is loaded or all matching representations were already tried. Args: - repres_by_subset_id (Dict[str, Dict[str, Any]]): Available - representations mapped by their parent (subset) id. - subsets_by_id (Dict[str, Dict[str, Any]]): Subset documents + repres_by_product_id (Dict[str, Dict[str, Any]]): Available + representations mapped by their parent (product) id. + products_by_id (Dict[str, Dict[str, Any]]): Subset documents mapped by their id. - profiles_per_subset_id (Dict[str, Dict[str, Any]]): Build profiles - mapped by subset id. + profiles_by_product_id (Dict[str, Dict[str, Any]]): Build profiles + mapped by product id. loaders_by_name (Dict[str, LoaderPlugin]): Available loaders per name. @@ -533,38 +533,40 @@ class BuildWorkfile: loaded_containers = [] - # Get subset id order from build presets. + # Get product id order from build presets. build_presets = self.build_presets.get("current_context", []) build_presets += self.build_presets.get("linked_assets", []) - subset_ids_ordered = [] + product_ids_ordered = [] for preset in build_presets: - for preset_family in preset["product_types"]: - for id, subset in subsets_by_id.items(): - if preset_family not in subset["data"].get("families", []): + for product_type in preset["product_types"]: + for product_id, subset_doc in products_by_id.items(): + # TODO 'families' is not available on product + families = subset_doc["data"].get("families") or [] + if product_type not in families: continue - subset_ids_ordered.append(id) + product_ids_ordered.append(product_id) - # Order representations from subsets. - print("repres_by_subset_id", repres_by_subset_id) + # Order representations from products. + print("repres_by_product_id", repres_by_product_id) representations_ordered = [] representations = [] - for id in subset_ids_ordered: - for subset_id, repres in repres_by_subset_id.items(): + for ordered_product_id in product_ids_ordered: + for product_id, repres in repres_by_product_id.items(): if repres in representations: continue - if id == subset_id: - representations_ordered.append((subset_id, repres)) + if ordered_product_id == product_id: + representations_ordered.append((product_id, repres)) representations.append(repres) print("representations", representations) # Load ordered representations. - for subset_id, repres in representations_ordered: - subset_name = subsets_by_id[subset_id]["name"] + for product_id, repres in representations_ordered: + product_name = products_by_id[product_id]["name"] - profile = profiles_per_subset_id[subset_id] + profile = profiles_by_product_id[product_id] loaders_last_idx = len(profile["loaders"]) - 1 repre_names_last_idx = len(profile["repre_names_lowered"]) - 1 @@ -595,7 +597,7 @@ class BuildWorkfile: container = load_container( loader, repre["_id"], - name=subset_name + name=product_name ) loaded_containers.append(container) is_loaded = True @@ -618,8 +620,8 @@ class BuildWorkfile: msg += " Trying next loader." elif repre_name_idx < repre_names_last_idx: msg += ( - " Loading of subset `{}` was not successful." - ).format(subset_name) + " Loading of product `{}` was not successful." + ).format(product_name) else: msg += " Trying next representation." self.log.info(msg) @@ -627,7 +629,7 @@ class BuildWorkfile: return loaded_containers def _collect_last_version_repres(self, asset_docs): - """Collect subsets, versions and representations for asset_entities. + """Collect products, versions and representations for asset_entities. Args: asset_docs (List[Dict[str, Any]]): Asset entities for which @@ -640,12 +642,12 @@ class BuildWorkfile: ``` { {Asset ID}: { - "asset_entity": , + "asset_doc": , "subsets": { {Subset ID}: { - "subset_entity": , + "subset_doc": , "version": { - "version_entity": , + "version_doc": , "repres": [ , , ... ] @@ -656,7 +658,7 @@ class BuildWorkfile: }, ... } - output[asset_id]["subsets"][subset_id]["version"]["repres"] + output[folder_id]["subsets"][product_id]["version"]["repres"] ``` """ @@ -666,20 +668,26 @@ class BuildWorkfile: if not asset_docs: return output - asset_docs_by_ids = {asset["_id"]: asset for asset in asset_docs} + asset_docs_by_ids = { + asset_doc["_id"]: asset_doc + for asset_doc in asset_docs + } project_name = get_current_project_name() - subsets = list(get_subsets( + subset_docs = list(get_subsets( project_name, asset_ids=asset_docs_by_ids.keys() )) - subset_entity_by_ids = {subset["_id"]: subset for subset in subsets} + subset_docs_by_id = { + subset_doc["_id"]: subset_doc + for subset_doc in subset_docs + } - last_version_by_subset_id = get_last_versions( - project_name, subset_entity_by_ids.keys() + last_version_by_product_id = get_last_versions( + project_name, subset_docs_by_id.keys() ) last_version_docs_by_id = { version["_id"]: version - for version in last_version_by_subset_id.values() + for version in last_version_by_product_id.values() } repre_docs = get_representations( project_name, version_ids=last_version_docs_by_id.keys() @@ -689,28 +697,28 @@ class BuildWorkfile: version_id = repre_doc["parent"] version_doc = last_version_docs_by_id[version_id] - subset_id = version_doc["parent"] - subset_doc = subset_entity_by_ids[subset_id] + product_id = version_doc["parent"] + subset_doc = subset_docs_by_id[product_id] - asset_id = subset_doc["parent"] - asset_doc = asset_docs_by_ids[asset_id] + folder_id = subset_doc["parent"] + asset_doc = asset_docs_by_ids[folder_id] - if asset_id not in output: - output[asset_id] = { - "asset_entity": asset_doc, + if folder_id not in output: + output[folder_id] = { + "asset_doc": asset_doc, "subsets": {} } - if subset_id not in output[asset_id]["subsets"]: - output[asset_id]["subsets"][subset_id] = { - "subset_entity": subset_doc, + if product_id not in output[folder_id]["subsets"]: + output[folder_id]["subsets"][product_id] = { + "subset_doc": subset_doc, "version": { - "version_entity": version_doc, + "version_doc": version_doc, "repres": [] } } - output[asset_id]["subsets"][subset_id]["version"]["repres"].append( + output[folder_id]["subsets"][product_id]["version"]["repres"].append( repre_doc ) diff --git a/client/ayon_core/pipeline/workfile/path_resolving.py b/client/ayon_core/pipeline/workfile/path_resolving.py index 168e775475..7718e32317 100644 --- a/client/ayon_core/pipeline/workfile/path_resolving.py +++ b/client/ayon_core/pipeline/workfile/path_resolving.py @@ -321,7 +321,7 @@ def get_last_workfile( data["app"], task_name=data["task"]["name"], task_type=data["task"]["type"], - family="workfile" + product_type="workfile" ) data.pop("comment", None) if not data.get("ext"): From d7785ef2da71400969e7c218ac2d6b378fec80d0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Feb 2024 17:37:21 +0100 Subject: [PATCH 222/573] removed subset_name.py --- client/ayon_core/pipeline/create/__init__.py | 5 - .../ayon_core/pipeline/create/subset_name.py | 172 ------------------ 2 files changed, 177 deletions(-) delete mode 100644 client/ayon_core/pipeline/create/subset_name.py diff --git a/client/ayon_core/pipeline/create/__init__.py b/client/ayon_core/pipeline/create/__init__.py index d14fc48230..ca2c47f1ff 100644 --- a/client/ayon_core/pipeline/create/__init__.py +++ b/client/ayon_core/pipeline/create/__init__.py @@ -16,11 +16,6 @@ from .product_name import ( get_product_name_template, ) -from .subset_name import ( - get_subset_name_template, - get_subset_name, -) - from .creator_plugins import ( CreatorError, diff --git a/client/ayon_core/pipeline/create/subset_name.py b/client/ayon_core/pipeline/create/subset_name.py deleted file mode 100644 index 20025faaaa..0000000000 --- a/client/ayon_core/pipeline/create/subset_name.py +++ /dev/null @@ -1,172 +0,0 @@ -import os - -from ayon_core.settings import get_project_settings -from ayon_core.lib import filter_profiles, prepare_template_data - -from .constants import DEFAULT_SUBSET_TEMPLATE -from .product_name import TaskNotSetError, TemplateFillError - - -def get_subset_name_template( - project_name, - family, - task_name, - task_type, - host_name, - default_template=None, - project_settings=None -): - """Get subset name template based on passed context. - - Args: - project_name (str): Project on which the context lives. - family (str): Family (subset type) for which the subset name is - calculated. - host_name (str): Name of host in which the subset name is calculated. - task_name (str): Name of task in which context the subset is created. - task_type (str): Type of task in which context the subset is created. - default_template (Union[str, None]): Default template which is used if - settings won't find any matching possitibility. Constant - 'DEFAULT_SUBSET_TEMPLATE' is used if not defined. - project_settings (Union[Dict[str, Any], None]): Prepared settings for - project. Settings are queried if not passed. - """ - - if project_settings is None: - project_settings = get_project_settings(project_name) - tools_settings = project_settings["core"]["tools"] - profiles = tools_settings["creator"]["product_name_profiles"] - filtering_criteria = { - "product_types": family, - "hosts": host_name, - "tasks": task_name, - "task_types": task_type - } - - matching_profile = filter_profiles(profiles, filtering_criteria) - template = None - if matching_profile: - # TODO remove formatting keys replacement - template = ( - matching_profile["template"] - .replace("{task[name]}", "{task}") - .replace("{Task[name]}", "{Task}") - .replace("{TASK[NAME]}", "{TASK}") - .replace("{product[type]}", "{family}") - .replace("{Product[type]}", "{Family}") - .replace("{PRODUCT[TYPE]}", "{FAMILY}") - .replace("{folder[name]}", "{asset}") - .replace("{Folder[name]}", "{Asset}") - .replace("{FOLDER[NAME]}", "{ASSET}") - ) - - # Make sure template is set (matching may have empty string) - if not template: - template = default_template or DEFAULT_SUBSET_TEMPLATE - return template - - -def get_subset_name( - family, - variant, - task_name, - asset_doc, - project_name=None, - host_name=None, - default_template=None, - dynamic_data=None, - project_settings=None, - family_filter=None, -): - """Calculate subset name based on passed context and OpenPype settings. - - Subst name templates are defined in `project_settings/global/tools/creator - /product_name_profiles` where are profiles with host name, family, - task name and task type filters. If context does not match any profile - then `DEFAULT_SUBSET_TEMPLATE` is used as default template. - - That's main reason why so many arguments are required to calculate subset - name. - - Option to pass family filter was added for special cases when creator or - automated publishing require special subset name template which would be - hard to maintain using its family value. - Why not just pass the right family? -> Family is also used as fill - value and for filtering of publish plugins. - - Todos: - Find better filtering options to avoid requirement of - argument 'family_filter'. - - Args: - family (str): Instance family. - variant (str): In most of the cases it is user input during creation. - task_name (str): Task name on which context is instance created. - asset_doc (dict): Queried asset document with its tasks in data. - Used to get task type. - project_name (Optional[str]): Name of project on which is instance - created. Important for project settings that are loaded. - host_name (Optional[str]): One of filtering criteria for template - profile filters. - default_template (Optional[str]): Default template if any profile does - not match passed context. Constant 'DEFAULT_SUBSET_TEMPLATE' - is used if is not passed. - dynamic_data (Optional[Dict[str, Any]]): Dynamic data specific for - a creator which creates instance. - project_settings (Optional[Union[Dict[str, Any]]]): Prepared settings - for project. Settings are queried if not passed. - family_filter (Optional[str]): Use different family for subset template - filtering. Value of 'family' is used when not passed. - - Raises: - TemplateFillError: If filled template contains placeholder key which is not - collected. - """ - - if not family: - return "" - - if not host_name: - host_name = os.environ.get("AYON_HOST_NAME") - - # Use only last part of class family value split by dot (`.`) - family = family.rsplit(".", 1)[-1] - - if project_name is None: - project_name = os.environ.get("AYON_PROJECT_NAME") - - asset_tasks = asset_doc.get("data", {}).get("tasks") or {} - task_info = asset_tasks.get(task_name) or {} - task_type = task_info.get("type") - - template = get_subset_name_template( - project_name, - family_filter or family, - task_name, - task_type, - host_name, - default_template=default_template, - project_settings=project_settings - ) - # Simple check of task name existence for template with {task} in - # - missing task should be possible only in Standalone publisher - if not task_name and "{task" in template.lower(): - raise TaskNotSetError() - - fill_pairs = { - "variant": variant, - "family": family, - "task": task_name - } - if dynamic_data: - # Dynamic data may override default values - for key, value in dynamic_data.items(): - fill_pairs[key] = value - - try: - return template.format(**prepare_template_data(fill_pairs)) - except KeyError as exp: - raise TemplateFillError( - "Value for {} key is missing in template '{}'." - " Available values are {}".format(str(exp), template, fill_pairs) - ) From 464fc4c78314c2bb3c4999c97337e21118054589 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Feb 2024 18:37:52 +0100 Subject: [PATCH 223/573] modified global plugins --- client/ayon_core/plugins/publish/cleanup.py | 35 +++++----- .../publish/collect_anatomy_instance_data.py | 62 ++++++++++-------- .../plugins/publish/collect_audio.py | 64 +++++++++---------- .../publish/collect_custom_staging_dir.py | 18 ++++-- .../plugins/publish/collect_frames_fix.py | 6 +- .../publish/collect_from_create_context.py | 15 +++-- .../plugins/publish/collect_hierarchy.py | 6 +- .../plugins/publish/collect_otio_review.py | 2 +- .../publish/collect_otio_subset_resources.py | 8 +-- .../plugins/publish/collect_rendered_files.py | 4 +- .../plugins/publish/extract_burnin.py | 18 +++--- .../publish/extract_color_transcode.py | 22 ++++--- .../publish/extract_otio_audio_tracks.py | 8 ++- .../plugins/publish/extract_review.py | 33 ++++------ .../plugins/publish/extract_thumbnail.py | 22 +++---- .../publish/extract_thumbnail_from_source.py | 4 +- .../publish/help/validate_unique_subsets.xml | 6 +- client/ayon_core/plugins/publish/integrate.py | 47 +++++++++----- .../plugins/publish/integrate_hero_version.py | 35 +++++----- .../plugins/publish/integrate_inputlinks.py | 4 +- .../publish/integrate_product_group.py | 16 +++-- .../plugins/publish/integrate_thumbnail.py | 4 -- .../publish/integrate_version_attrs.py | 2 +- .../preintegrate_thumbnail_representation.py | 8 +-- .../plugins/publish/validate_publish_dir.py | 12 ++-- .../publish/validate_unique_subsets.py | 34 +++++----- 26 files changed, 266 insertions(+), 229 deletions(-) diff --git a/client/ayon_core/plugins/publish/cleanup.py b/client/ayon_core/plugins/publish/cleanup.py index db73d28e7a..57ef803352 100644 --- a/client/ayon_core/plugins/publish/cleanup.py +++ b/client/ayon_core/plugins/publish/cleanup.py @@ -72,7 +72,7 @@ class CleanUp(pyblish.api.InstancePlugin): self.clean_renders(instance, skip_cleanup_filepaths) if [ef for ef in self.exclude_families - if instance.data["family"] in ef]: + if instance.data["productType"] in ef]: return import tempfile @@ -105,8 +105,8 @@ class CleanUp(pyblish.api.InstancePlugin): def clean_renders(self, instance, skip_cleanup_filepaths): transfers = instance.data.get("transfers", list()) - current_families = instance.data.get("families", list()) - instance_family = instance.data.get("family", None) + instance_families = instance.data.get("families", list()) + instance_product_type = instance.data.get("productType") dirnames = [] transfers_dirs = [] @@ -127,19 +127,24 @@ class CleanUp(pyblish.api.InstancePlugin): ).format(src)) continue - if os.path.normpath(src) != os.path.normpath(dest): - if instance_family == 'render' or 'render' in current_families: - self.log.info("Removing src: `{}`...".format(src)) - try: - os.remove(src) - except PermissionError: - self.log.warning( - "Insufficient permission to delete {}".format(src) - ) - continue + if os.path.normpath(src) == os.path.normpath(dest): + continue - # add dir for cleanup - dirnames.append(os.path.dirname(src)) + if ( + instance_product_type == "render" + or "render" in instance_families + ): + self.log.info("Removing src: `{}`...".format(src)) + try: + os.remove(src) + except PermissionError: + self.log.warning( + "Insufficient permission to delete {}".format(src) + ) + continue + + # add dir for cleanup + dirnames.append(os.path.dirname(src)) # clean by regex patterns # make unique set diff --git a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py index f6326bb9e8..6e1108e733 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py @@ -4,8 +4,8 @@ Requires: context -> projectEntity context -> assetEntity instance -> folderPath - instance -> subset - instance -> family + instance -> productName + instance -> productType Optional: instance -> version @@ -120,7 +120,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): ).format(joined_asset_names)) def fill_latest_versions(self, context, project_name): - """Try to find latest version for each instance's subset. + """Try to find latest version for each instance's product name. Key "latestVersion" is always set to latest version or `None`. @@ -134,7 +134,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): self.log.debug("Querying latest versions for instances.") hierarchy = {} - names_by_asset_ids = collections.defaultdict(set) + names_by_folder_ids = collections.defaultdict(set) for instance in context: # Make sure `"latestVersion"` key is set latest_version = instance.data.get("latestVersion") @@ -145,41 +145,41 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): if not asset_doc: continue - # Store asset ids and subset names for queries - asset_id = asset_doc["_id"] - subset_name = instance.data["subset"] + # Store folder ids and product names for queries + folder_id = asset_doc["_id"] + product_name = instance.data["productName"] # Prepare instance hierarchy for faster filling latest versions - if asset_id not in hierarchy: - hierarchy[asset_id] = {} - if subset_name not in hierarchy[asset_id]: - hierarchy[asset_id][subset_name] = [] - hierarchy[asset_id][subset_name].append(instance) - names_by_asset_ids[asset_id].add(subset_name) + if folder_id not in hierarchy: + hierarchy[folder_id] = {} + if product_name not in hierarchy[folder_id]: + hierarchy[folder_id][product_name] = [] + hierarchy[folder_id][product_name].append(instance) + names_by_folder_ids[folder_id].add(product_name) subset_docs = [] - if names_by_asset_ids: + if names_by_folder_ids: subset_docs = list(get_subsets( - project_name, names_by_asset_ids=names_by_asset_ids + project_name, names_by_folder_ids=names_by_folder_ids )) - subset_ids = [ + product_ids = { subset_doc["_id"] for subset_doc in subset_docs - ] + } - last_version_docs_by_subset_id = get_last_versions( - project_name, subset_ids, fields=["name"] + last_version_docs_by_product_id = get_last_versions( + project_name, product_ids, fields=["name"] ) for subset_doc in subset_docs: - subset_id = subset_doc["_id"] - last_version_doc = last_version_docs_by_subset_id.get(subset_id) + product_id = subset_doc["_id"] + last_version_doc = last_version_docs_by_product_id.get(product_id) if last_version_doc is None: continue - asset_id = subset_doc["parent"] - subset_name = subset_doc["name"] - _instances = hierarchy[asset_id][subset_name] + folder_id = subset_doc["parent"] + product_name = subset_doc["name"] + _instances = hierarchy[folder_id][product_name] for _instance in _instances: _instance.data["latestVersion"] = last_version_doc["name"] @@ -191,9 +191,15 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): for instance in context: anatomy_data = copy.deepcopy(context.data["anatomyData"]) + product_name = instance.data["productName"] + product_type = instance.data["productType"] anatomy_data.update({ - "family": instance.data["family"], - "subset": instance.data["subset"], + "family": product_type, + "subset": product_name, + "product": { + "name": product_name, + "type": product_type, + } }) self._fill_asset_data(instance, project_doc, anatomy_data) @@ -227,8 +233,8 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): instance.context.data["hostName"], task_name=task_name, task_type=task_type, - family=instance.data["family"], - subset=instance.data["subset"] + product_type=instance.data["productType"], + product_name=instance.data["productName"] ) anatomy_data["version"] = version_number diff --git a/client/ayon_core/plugins/publish/collect_audio.py b/client/ayon_core/plugins/publish/collect_audio.py index f0efd546e7..357dad76d4 100644 --- a/client/ayon_core/plugins/publish/collect_audio.py +++ b/client/ayon_core/plugins/publish/collect_audio.py @@ -14,7 +14,7 @@ from ayon_core.pipeline.load import get_representation_path_with_anatomy class CollectAudio(pyblish.api.ContextPlugin): """Collect asset's last published audio. - The audio subset name searched for is defined in: + The audio product name searched for is defined in: project settings > Collect Audio Note: @@ -71,9 +71,9 @@ class CollectAudio(pyblish.api.ContextPlugin): asset_names = set(instances_by_asset_name.keys()) self.log.debug(( - "Searching for audio product '{subset}' in assets {assets}" + "Searching for audio product '{product}' in assets {assets}" ).format( - subset=self.audio_product_name, + product=self.audio_product_name, assets=", ".join([ '"{}"'.format(asset_name) for asset_name in asset_names @@ -102,64 +102,64 @@ class CollectAudio(pyblish.api.ContextPlugin): }] self.log.debug("Audio Data added to instance ...") - def query_representations(self, project_name, asset_names): - """Query representations related to audio subsets for passed assets. + def query_representations(self, project_name, folder_paths): + """Query representations related to audio products for passed assets. Args: project_name (str): Project in which we're looking for all entities. - asset_names (Iterable[str]): Asset names where to look for audio - subsets and their representations. + folder_paths (Iterable[str]): Folder paths where to look for audio + products and their representations. Returns: collections.defaultdict[str, List[Dict[Str, Any]]]: Representations - related to audio subsets by asset name. + related to audio products by asset name. """ output = collections.defaultdict(list) # Query asset documents asset_docs = get_assets( project_name, - asset_names=asset_names, + asset_names=folder_paths, fields=["_id", "name", "data.parents"] ) - asset_id_by_name = { + folder_id_by_path = { get_asset_name_identifier(asset_doc): asset_doc["_id"] for asset_doc in asset_docs } - asset_ids = set(asset_id_by_name.values()) + folder_ids = set(folder_id_by_path.values()) - # Query subsets with name define by 'audio_product_name' attr - # - one or none subsets with the name should be available on an asset + # Query products with name define by 'audio_product_name' attr + # - one or none products with the name should be available on an asset subset_docs = get_subsets( project_name, subset_names=[self.audio_product_name], - asset_ids=asset_ids, + asset_ids=folder_ids, fields=["_id", "parent"] ) - subset_id_by_asset_id = {} + product_id_by_folder_id = {} for subset_doc in subset_docs: - asset_id = subset_doc["parent"] - subset_id_by_asset_id[asset_id] = subset_doc["_id"] + folder_id = subset_doc["parent"] + product_id_by_folder_id[folder_id] = subset_doc["_id"] - subset_ids = set(subset_id_by_asset_id.values()) - if not subset_ids: + product_ids = set(product_id_by_folder_id.values()) + if not product_ids: return output - # Find all latest versions for the subsets - version_docs_by_subset_id = get_last_versions( - project_name, subset_ids=subset_ids, fields=["_id", "parent"] + # Find all latest versions for the products + version_docs_by_product_id = get_last_versions( + project_name, subset_ids=product_ids, fields=["_id", "parent"] ) - version_id_by_subset_id = { - subset_id: version_doc["_id"] - for subset_id, version_doc in version_docs_by_subset_id.items() + version_id_by_product_id = { + product_id: version_doc["_id"] + for product_id, version_doc in version_docs_by_product_id.items() } - version_ids = set(version_id_by_subset_id.values()) + version_ids = set(version_id_by_product_id.values()) if not version_ids: return output - # Find representations under latest versions of audio subsets + # Find representations under latest versions of audio products repre_docs = get_representations( project_name, version_ids=version_ids ) @@ -171,9 +171,9 @@ class CollectAudio(pyblish.api.ContextPlugin): if not repre_docs_by_version_id: return output - for asset_name in asset_names: - asset_id = asset_id_by_name.get(asset_name) - subset_id = subset_id_by_asset_id.get(asset_id) - version_id = version_id_by_subset_id.get(subset_id) - output[asset_name] = repre_docs_by_version_id[version_id] + for folder_path in folder_paths: + folder_id = folder_id_by_path.get(folder_path) + product_id = product_id_by_folder_id.get(folder_id) + version_id = version_id_by_product_id.get(product_id) + output[folder_path] = repre_docs_by_version_id[version_id] return output diff --git a/client/ayon_core/plugins/publish/collect_custom_staging_dir.py b/client/ayon_core/plugins/publish/collect_custom_staging_dir.py index 6840c8e416..e42f34b0ae 100644 --- a/client/ayon_core/plugins/publish/collect_custom_staging_dir.py +++ b/client/ayon_core/plugins/publish/collect_custom_staging_dir.py @@ -38,8 +38,8 @@ class CollectCustomStagingDir(pyblish.api.InstancePlugin): template_key = "transient" def process(self, instance): - family = instance.data["family"] - subset_name = instance.data["subset"] + product_type = instance.data["productType"] + product_name = instance.data["productName"] host_name = instance.context.data["hostName"] project_name = instance.context.data["projectName"] project_settings = instance.context.data["project_settings"] @@ -47,9 +47,15 @@ class CollectCustomStagingDir(pyblish.api.InstancePlugin): task = instance.data["anatomyData"].get("task", {}) transient_tml, is_persistent = get_custom_staging_dir_info( - project_name, host_name, family, task.get("name"), - task.get("type"), subset_name, project_settings=project_settings, - anatomy=anatomy, log=self.log) + project_name, + host_name, + product_type, + product_name, + task.get("name"), + task.get("type"), + project_settings=project_settings, + anatomy=anatomy, + log=self.log) if transient_tml: anatomy_data = copy.deepcopy(instance.data["anatomyData"]) @@ -66,5 +72,5 @@ class CollectCustomStagingDir(pyblish.api.InstancePlugin): result_str = "Not adding" self.log.debug("{} custom staging dir for instance with '{}'".format( - result_str, family + result_str, product_type )) diff --git a/client/ayon_core/plugins/publish/collect_frames_fix.py b/client/ayon_core/plugins/publish/collect_frames_fix.py index 6996844eda..0fe86b8d70 100644 --- a/client/ayon_core/plugins/publish/collect_frames_fix.py +++ b/client/ayon_core/plugins/publish/collect_frames_fix.py @@ -17,7 +17,7 @@ class CollectFramesFixDef( ): """Provides text field to insert frame(s) to be rerendered. - Published files of last version of an instance subset are collected into + Published files of last version of an instance product are collected into instance.data["last_version_published_files"]. All these but frames mentioned in text field will be reused for new version. """ @@ -40,7 +40,7 @@ class CollectFramesFixDef( instance.data["frames_to_fix"] = frames_to_fix - subset_name = instance.data["subset"] + product_name = instance.data["productName"] asset_name = instance.data["folderPath"] project_entity = instance.data["projectEntity"] @@ -48,7 +48,7 @@ class CollectFramesFixDef( version = get_last_version_by_subset_name( project_name, - subset_name, + product_name, asset_name=asset_name ) if not version: diff --git a/client/ayon_core/plugins/publish/collect_from_create_context.py b/client/ayon_core/plugins/publish/collect_from_create_context.py index 7152446de8..353f4f69cd 100644 --- a/client/ayon_core/plugins/publish/collect_from_create_context.py +++ b/client/ayon_core/plugins/publish/collect_from_create_context.py @@ -72,18 +72,21 @@ class CollectFromCreateContext(pyblish.api.ContextPlugin): transient_data, thumbnail_path ): - subset = in_data["subset"] + product_name = in_data["productName"] # If instance data already contain families then use it instance_families = in_data.get("families") or [] + # Add product type to families + instance_families.append(in_data["productType"]) - instance = context.create_instance(subset) + instance = context.create_instance(product_name) instance.data.update({ - "subset": subset, + "publish": True, + "label": in_data.get("label") or product_name, + "name": product_name, "folderPath": in_data["folderPath"], "task": in_data["task"], - "label": in_data.get("label") or subset, - "name": subset, - "family": in_data["family"], + "productName": product_name, + "productType": in_data["productType"], "families": instance_families, "representations": [], "thumbnailSource": thumbnail_path diff --git a/client/ayon_core/plugins/publish/collect_hierarchy.py b/client/ayon_core/plugins/publish/collect_hierarchy.py index d7077d030a..8ba83d582f 100644 --- a/client/ayon_core/plugins/publish/collect_hierarchy.py +++ b/client/ayon_core/plugins/publish/collect_hierarchy.py @@ -28,11 +28,13 @@ class CollectHierarchy(pyblish.api.ContextPlugin): # shot data dict shot_data = {} - family = instance.data["family"] + product_type = instance.data["productType"] families = instance.data["families"] # exclude other families then self.families with intersection - if not set(self.families).intersection(set(families + [family])): + if not set(self.families).intersection( + set(families + [product_type]) + ): continue # exclude if not masterLayer True diff --git a/client/ayon_core/plugins/publish/collect_otio_review.py b/client/ayon_core/plugins/publish/collect_otio_review.py index 0e4d596213..69cf9199e7 100644 --- a/client/ayon_core/plugins/publish/collect_otio_review.py +++ b/client/ayon_core/plugins/publish/collect_otio_review.py @@ -91,7 +91,7 @@ class CollectOtioReview(pyblish.api.InstancePlugin): if otio_review_clips: # add review track to instance and change label to reflect it - label = instance.data.get("label", instance.data["subset"]) + label = instance.data.get("label", instance.data["productName"]) instance.data["label"] = label + " (review)" instance.data["families"] += ["review", "ftrack"] instance.data["otioReviewClips"] = otio_review_clips diff --git a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py index e6817a4beb..3f47e6e3bf 100644 --- a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py +++ b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py @@ -16,7 +16,7 @@ from ayon_core.pipeline.publish import ( class CollectOtioSubsetResources(pyblish.api.InstancePlugin): - """Get Resources for a subset version""" + """Get Resources for a product version""" label = "Collect OTIO Subset Resources" order = pyblish.api.CollectorOrder + 0.491 @@ -32,7 +32,7 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): make_sequence_collection ) - if "audio" in instance.data["family"]: + if "audio" in instance.data["productType"]: return if not instance.data.get("representations"): @@ -250,14 +250,14 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): # Task can be optional in anatomy data host_name = context.data["hostName"] - family = instance.data["family"] + product_type = instance.data["productType"] anatomy_data = instance.data["anatomyData"] task_info = anatomy_data.get("task") or {} return get_publish_template_name( project_name, host_name, - family, + product_type, task_name=task_info.get("name"), task_type=task_info.get("type"), project_settings=context.data["project_settings"], diff --git a/client/ayon_core/plugins/publish/collect_rendered_files.py b/client/ayon_core/plugins/publish/collect_rendered_files.py index fcea773208..ca88a7aa82 100644 --- a/client/ayon_core/plugins/publish/collect_rendered_files.py +++ b/client/ayon_core/plugins/publish/collect_rendered_files.py @@ -103,9 +103,9 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin): for instance_data in data.get("instances"): self.log.debug(" - processing instance for {}".format( - instance_data.get("subset"))) + instance_data.get("productName"))) instance = self._context.create_instance( - instance_data.get("subset") + instance_data.get("productName") ) self._fill_staging_dir(instance_data, anatomy) diff --git a/client/ayon_core/plugins/publish/extract_burnin.py b/client/ayon_core/plugins/publish/extract_burnin.py index edba7d6d8a..ab6353a29f 100644 --- a/client/ayon_core/plugins/publish/extract_burnin.py +++ b/client/ayon_core/plugins/publish/extract_burnin.py @@ -156,16 +156,16 @@ class ExtractBurnin(publish.Extractor): def main_process(self, instance): host_name = instance.context.data["hostName"] - family = instance.data["family"] + product_type = instance.data["productType"] + product_name = instance.data["productName"] task_data = instance.data["anatomyData"].get("task", {}) task_name = task_data.get("name") task_type = task_data.get("type") - subset = instance.data["subset"] filtering_criteria = { "hosts": host_name, - "product_types": family, - "product_names": subset, + "product_types": product_type, + "product_names": product_name, "task_names": task_name, "task_types": task_type, } @@ -177,9 +177,11 @@ class ExtractBurnin(publish.Extractor): if not profile: self.log.debug(( "Skipped instance. None of profiles in presets are for" - " Host: \"{}\" | Product type: \"{}\" | Task name \"{}\"" - " | Task type \"{}\" | Product name \"{}\" " - ).format(host_name, family, task_name, task_type, subset)) + " Host: \"{}\" | Product type: \"{}\" | Product name \"{}\"" + " | Task name \"{}\" | Task type \"{}\"" + ).format( + host_name, product_type, product_name, task_name, task_type + )) return # Pre-filter burnin definitions by instance families @@ -189,7 +191,7 @@ class ExtractBurnin(publish.Extractor): "Skipped instance. Burnin definitions are not set for profile" " Host: \"{}\" | Product type: \"{}\" | Task name \"{}\"" " | Profile \"{}\"" - ).format(host_name, family, task_name, profile)) + ).format(host_name, product_type, task_name, profile)) return burnin_options = self._get_burnin_options() diff --git a/client/ayon_core/plugins/publish/extract_color_transcode.py b/client/ayon_core/plugins/publish/extract_color_transcode.py index 77c4673bca..b5ddebe05b 100644 --- a/client/ayon_core/plugins/publish/extract_color_transcode.py +++ b/client/ayon_core/plugins/publish/extract_color_transcode.py @@ -26,11 +26,11 @@ class ExtractOIIOTranscode(publish.Extractor): This dict contains source colorspace information, collected by hosts. Target colorspace is selected by profiles in the Settings, based on: - - families - - host + - host names + - product types + - product names - task types - task names - - subset names Can produce one or more representations (with different extensions) based on output definition in format: @@ -313,15 +313,15 @@ class ExtractOIIOTranscode(publish.Extractor): def _get_profile(self, instance): """Returns profile if and how repre should be color transcoded.""" host_name = instance.context.data["hostName"] - family = instance.data["family"] + product_type = instance.data["productType"] + product_name = instance.data["productName"] task_data = instance.data["anatomyData"].get("task", {}) task_name = task_data.get("name") task_type = task_data.get("type") - subset = instance.data["subset"] filtering_criteria = { "hosts": host_name, - "product_types": family, - "product_names": subset, + "product_types": product_type, + "product_names": product_name, "task_names": task_name, "task_types": task_type, } @@ -331,9 +331,11 @@ class ExtractOIIOTranscode(publish.Extractor): if not profile: self.log.debug(( "Skipped instance. None of profiles in presets are for" - " Host: \"{}\" | Product types: \"{}\" | Task \"{}\"" - " | Task type \"{}\" | Product names: \"{}\" " - ).format(host_name, family, task_name, task_type, subset)) + " Host: \"{}\" | Product types: \"{}\" | Product names: \"{}\"" + " | Task name \"{}\" | Task type \"{}\"" + ).format( + host_name, product_type, product_name, task_name, task_type + )) return profile diff --git a/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py b/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py index dd45f3fc1a..a19b5b9090 100644 --- a/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py +++ b/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py @@ -103,7 +103,9 @@ class ExtractOtioAudioTracks(pyblish.api.ContextPlugin): else: audio_fpath = recycling_file.pop() - if "audio" in (inst.data["families"] + [inst.data["family"]]): + if "audio" in ( + inst.data["families"] + [inst.data["productType"]] + ): # create empty representation attr if "representations" not in inst.data: inst.data["representations"] = [] @@ -140,10 +142,10 @@ class ExtractOtioAudioTracks(pyblish.api.ContextPlugin): """ return [ _i for _i in context - # filter only those with audio family + # filter only those with audio product type or family # and also with reviewAudio data key if bool("audio" in ( - _i.data.get("families", []) + [_i.data["family"]]) + _i.data.get("families", []) + [_i.data["productType"]]) ) or _i.data.get("reviewAudio") ] diff --git a/client/ayon_core/plugins/publish/extract_review.py b/client/ayon_core/plugins/publish/extract_review.py index 927c8a3538..905158c851 100644 --- a/client/ayon_core/plugins/publish/extract_review.py +++ b/client/ayon_core/plugins/publish/extract_review.py @@ -103,38 +103,38 @@ class ExtractReview(pyblish.api.InstancePlugin): def _get_outputs_for_instance(self, instance): host_name = instance.context.data["hostName"] - family = self.main_family_from_instance(instance) + product_type = instance.data["productType"] self.log.debug("Host: \"{}\"".format(host_name)) - self.log.debug("Family: \"{}\"".format(family)) + self.log.debug("Product type: \"{}\"".format(product_type)) profile = filter_profiles( self.profiles, { "hosts": host_name, - "product_types": family, + "product_types": product_type, }, logger=self.log) if not profile: self.log.info(( "Skipped instance. None of profiles in presets are for" - " Host: \"{}\" | Family: \"{}\"" - ).format(host_name, family)) + " Host: \"{}\" | Product type: \"{}\"" + ).format(host_name, product_type)) return self.log.debug("Matching profile: \"{}\"".format(json.dumps(profile))) - subset_name = instance.data.get("subset") + product_name = instance.data.get("productName") instance_families = self.families_from_instance(instance) filtered_outputs = self.filter_output_defs( - profile, subset_name, instance_families + profile, product_name, instance_families ) if not filtered_outputs: self.log.info(( "Skipped instance. All output definitions from selected" " profile do not match instance families \"{}\" or" - " subset name \"{}\"." - ).format(str(instance_families), subset_name)) + " product name \"{}\"." + ).format(str(instance_families), product_name)) # Store `filename_suffix` to save arguments profile_outputs = [] @@ -1463,13 +1463,6 @@ class ExtractReview(pyblish.api.InstancePlugin): return filters - def main_family_from_instance(self, instance): - """Returns main family of entered instance.""" - family = instance.data.get("family") - if not family: - family = instance.data["families"][0] - return family - def families_from_instance(self, instance): """Returns all families of entered instance.""" families = [] @@ -1497,7 +1490,7 @@ class ExtractReview(pyblish.api.InstancePlugin): return any(family.lower() in families_filter_lower for family in families) - def filter_output_defs(self, profile, subset_name, families): + def filter_output_defs(self, profile, product_name, families): """Return outputs matching input instance families. Output definitions without families filter are marked as valid. @@ -1505,7 +1498,7 @@ class ExtractReview(pyblish.api.InstancePlugin): Args: profile (dict): Profile from presets matching current context. families (list): All families of current instance. - subset_name (str): name of subset + product_name (str): Product name. Returns: dict[str, Any]: Containing all output definitions matching entered @@ -1536,11 +1529,11 @@ class ExtractReview(pyblish.api.InstancePlugin): # Skip empty strings if name_filter ] - if subset_name and product_name_filters: + if product_name and product_name_filters: match = False for product_name_filter in product_name_filters: compiled = re.compile(product_name_filter) - if compiled.search(subset_name): + if compiled.search(product_name): match = True break diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index e392ce630a..84c1000ba3 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -86,16 +86,16 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): instance.data["representations"].remove(repre) def _main_process(self, instance): - subset_name = instance.data["subset"] + product_name = instance.data["productName"] instance_repres = instance.data.get("representations") if not instance_repres: self.log.debug(( "Instance {} does not have representations. Skipping" - ).format(subset_name)) + ).format(product_name)) return self.log.debug( - "Processing instance with subset name {}".format(subset_name) + "Processing instance with product name {}".format(product_name) ) # Skip if instance have 'review' key in data set to 'False' @@ -110,15 +110,15 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): # 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. + # affecting every prouct 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 subset_name.lower(): + if "crypto" in product_name.lower(): self.log.debug("Skipping crypto passes.") return - # We only want to process the subsets needed from settings. + # We only want to process the produces needed from settings. def validate_string_against_patterns(input_str, patterns): for pattern in patterns: if re.match(pattern, input_str): @@ -128,14 +128,12 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): product_names = self.product_names if product_names: result = validate_string_against_patterns( - instance.data["subset"], product_names + product_name, product_names ) if not result: - self.log.debug( - "Product name \"{}\" did not match settings filters: {}".format( - instance.data["subset"], product_names - ) - ) + self.log.debug(( + "Product name \"{}\" did not match settings filters: {}" + ).format(product_name, product_names)) return # first check for any explicitly marked representations for thumbnail diff --git a/client/ayon_core/plugins/publish/extract_thumbnail_from_source.py b/client/ayon_core/plugins/publish/extract_thumbnail_from_source.py index 8d043d700d..7751d73335 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail_from_source.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail_from_source.py @@ -38,9 +38,9 @@ class ExtractThumbnailFromSource(pyblish.api.InstancePlugin): def process(self, instance): self._create_context_thumbnail(instance.context) - subset_name = instance.data["subset"] + product_name = instance.data["productName"] self.log.debug( - "Processing instance with subset name {}".format(subset_name) + "Processing instance with product name {}".format(product_name) ) thumbnail_source = instance.data.get("thumbnailSource") if not thumbnail_source: diff --git a/client/ayon_core/plugins/publish/help/validate_unique_subsets.xml b/client/ayon_core/plugins/publish/help/validate_unique_subsets.xml index b18f046f84..a4b289d848 100644 --- a/client/ayon_core/plugins/publish/help/validate_unique_subsets.xml +++ b/client/ayon_core/plugins/publish/help/validate_unique_subsets.xml @@ -3,11 +3,11 @@ Subset not unique -## Clashing subset names found +## Clashing product names found -Multiples instances from your scene are set to publish into the same asset > subset. +Multiples instances from your scene are set to publish into the same folder > product. - Non unique subset names: '{non_unique}' + Non unique product names: '{non_unique}' ### How to repair? diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index a502031595..12c702c93b 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -60,7 +60,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): """Register publish in the database and transfer files to destinations. Steps: - 1) Register the subset and version + 1) Register the product and version 2) Transfer the representation files to the destination 3) Register the representation @@ -148,8 +148,19 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # Representation context keys that should always be written to # the database even if not used by the destination template db_representation_context_keys = [ - "project", "asset", "task", "subset", "version", "representation", - "family", "hierarchy", "username", "user", "output" + "project", + "asset", + "hierarchy", + "folder", + "task", + "product", + "subset", + "family", + "version", + "representation", + "username", + "user", + "output" ] def process(self, instance): @@ -172,7 +183,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): self.log.warning(( "Skipping, there are no representations" " to integrate for instance {}" - ).format(instance.data["family"])) + ).format(instance.data["productType"])) return file_transactions = FileTransaction(log=self.log, @@ -205,7 +216,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): if not repres: raise KnownPublishError( "Instance {} has no representations to integrate".format( - instance.data["family"] + instance.data["productType"] ) ) @@ -307,9 +318,9 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # increase if the file transaction takes a long time. op_session.commit() - self.log.info("Subset '{subset[name]}' version {version[name]} " - "written to database..".format(subset=subset, - version=version)) + self.log.info(( + "Product '{}' version {} written to database.." + ).format(subset["name"], version["name"])) # Process all file transfers of all integrations now self.log.debug("Integrating source files to destination ...") @@ -403,13 +414,13 @@ class IntegrateAsset(pyblish.api.InstancePlugin): def prepare_subset(self, instance, op_session, project_name): asset_doc = instance.data["assetEntity"] - subset_name = instance.data["subset"] - family = instance.data["family"] - self.log.debug("Subset: {}".format(subset_name)) + product_name = instance.data["productName"] + product_type = instance.data["productType"] + self.log.debug("Product: {}".format(product_name)) # Get existing subset if it exists existing_subset_doc = get_subset_by_name( - project_name, subset_name, asset_doc["_id"] + project_name, product_name, asset_doc["_id"] ) # Define subset data @@ -430,12 +441,14 @@ class IntegrateAsset(pyblish.api.InstancePlugin): if existing_subset_doc: subset_id = existing_subset_doc["_id"] subset_doc = new_subset_document( - subset_name, family, asset_doc["_id"], data, subset_id + product_name, product_type, asset_doc["_id"], data, subset_id ) if existing_subset_doc is None: # Create a new subset - self.log.info("Subset '%s' not found, creating ..." % subset_name) + self.log.info( + "Product '%s' not found, creating ..." % product_name + ) op_session.create_entity( project_name, subset_doc["type"], subset_doc ) @@ -455,7 +468,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): update_data ) - self.log.debug("Prepared subset: {}".format(subset_name)) + self.log.debug("Prepared product: {}".format(product_name)) return subset_doc def prepare_version(self, instance, op_session, subset_doc, project_name): @@ -914,13 +927,13 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # Task can be optional in anatomy data host_name = context.data["hostName"] anatomy_data = instance.data["anatomyData"] - family = anatomy_data["family"] + product_type = instance.data["productType"] task_info = anatomy_data.get("task") or {} return get_publish_template_name( project_name, host_name, - family, + product_type, task_name=task_info.get("name"), task_type=task_info.get("type"), project_settings=context.data["project_settings"], diff --git a/client/ayon_core/plugins/publish/integrate_hero_version.py b/client/ayon_core/plugins/publish/integrate_hero_version.py index 6dec41b7b0..c275f75118 100644 --- a/client/ayon_core/plugins/publish/integrate_hero_version.py +++ b/client/ayon_core/plugins/publish/integrate_hero_version.py @@ -46,8 +46,18 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): # Can specify representation names that will be ignored (lower case) ignored_representation_names = [] db_representation_context_keys = [ - "project", "asset", "task", "subset", "representation", - "family", "hierarchy", "task", "username", "user" + "project", + "folder", + "asset", + "hierarchy", + "task", + "product", + "subset", + "family", + "representation", + "username", + "user", + "output" ] # QUESTION/TODO this process should happen on server if crashed due to # permissions error on files (files were used or user didn't have perms) @@ -57,8 +67,8 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): def process(self, instance): self.log.debug( - "--- Integration of Hero version for subset `{}` begins.".format( - instance.data.get("subset", str(instance)) + "--- Integration of Hero version for product `{}` begins.".format( + instance.data["productName"] ) ) published_repres = instance.data.get("published_representations") @@ -503,10 +513,10 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): raise self.log.debug(( - "--- hero version integration for subset `{}`" + "--- hero version integration for product `{}`" " seems to be successful." ).format( - instance.data.get("subset", str(instance)) + instance.data["productName"] )) def get_all_files_from_path(self, path): @@ -558,14 +568,12 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): anatomy_data = instance.data["anatomyData"] task_info = anatomy_data.get("task") or {} host_name = instance.context.data["hostName"] - - # TODO raise error if Hero not set? - family = self.main_family_from_instance(instance) + product_type = instance.data["productType"] return get_publish_template_name( project_name, host_name, - family, + product_type, task_info.get("name"), task_info.get("type"), project_settings=instance.context.data["project_settings"], @@ -573,13 +581,6 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): logger=self.log ) - def main_family_from_instance(self, instance): - """Returns main family of entered instance.""" - family = instance.data.get("family") - if not family: - family = instance.data["families"][0] - return family - def copy_file(self, src_path, dst_path): # TODO check drives if are the same to check if cas hardlink dirname = os.path.dirname(dst_path) diff --git a/client/ayon_core/plugins/publish/integrate_inputlinks.py b/client/ayon_core/plugins/publish/integrate_inputlinks.py index da8df53170..809e93f27e 100644 --- a/client/ayon_core/plugins/publish/integrate_inputlinks.py +++ b/client/ayon_core/plugins/publish/integrate_inputlinks.py @@ -61,8 +61,8 @@ class IntegrateInputLinksAYON(pyblish.api.ContextPlugin): "Instance {} doesn't have version.".format(instance)) continue - family = instance.data.get("family") - if family == "workfile": + product_type = instance.data["productType"] + if product_type == "workfile": workfile_instance = instance else: other_instances.append(instance) diff --git a/client/ayon_core/plugins/publish/integrate_product_group.py b/client/ayon_core/plugins/publish/integrate_product_group.py index 82646ed733..f69e7744d9 100644 --- a/client/ayon_core/plugins/publish/integrate_product_group.py +++ b/client/ayon_core/plugins/publish/integrate_product_group.py @@ -28,7 +28,7 @@ class IntegrateProductGroup(pyblish.api.InstancePlugin): product_grouping_profiles = None def process(self, instance): - """Look into subset group profiles set by settings. + """Look into product group profiles set by settings. Attribute 'product_grouping_profiles' is defined by settings. """ @@ -40,7 +40,7 @@ class IntegrateProductGroup(pyblish.api.InstancePlugin): if instance.data.get("subsetGroup"): # If subsetGroup is already set then allow that value to remain self.log.debug(( - "Skipping collect subset group due to existing value: {}" + "Skipping collect product group due to existing value: {}" ).format(instance.data["subsetGroup"])) return @@ -56,12 +56,18 @@ class IntegrateProductGroup(pyblish.api.InstancePlugin): return template = profile["template"] + product_name = instance.data["productName"] + product_type = instance.data["productType"] fill_pairs = prepare_template_data({ - "family": filter_criteria["product_types"], + "family": product_type, "task": filter_criteria["tasks"], "host": filter_criteria["hosts"], - "subset": instance.data["subset"], + "subset": product_name, + "product": { + "name": product_name, + "type": product_type, + }, "renderlayer": instance.data.get("renderlayer") }) @@ -91,7 +97,7 @@ class IntegrateProductGroup(pyblish.api.InstancePlugin): # Return filter criteria return { - "product_types": anatomy_data["family"], + "product_types": instance.data["productType"], "tasks": task.get("name"), "hosts": instance.context.data["hostName"], "task_types": task.get("type") diff --git a/client/ayon_core/plugins/publish/integrate_thumbnail.py b/client/ayon_core/plugins/publish/integrate_thumbnail.py index 03bc8d2d3d..9eb649d5a0 100644 --- a/client/ayon_core/plugins/publish/integrate_thumbnail.py +++ b/client/ayon_core/plugins/publish/integrate_thumbnail.py @@ -42,10 +42,6 @@ class IntegrateThumbnailsAYON(pyblish.api.ContextPlugin): label = "Integrate Thumbnails to AYON" order = pyblish.api.IntegratorOrder + 0.01 - required_context_keys = [ - "project", "asset", "task", "subset", "version" - ] - def process(self, context): # Filter instances which can be used for integration filtered_instance_items = self._prepare_instances(context) diff --git a/client/ayon_core/plugins/publish/integrate_version_attrs.py b/client/ayon_core/plugins/publish/integrate_version_attrs.py index 5b5ec9cf5b..bc09af9db0 100644 --- a/client/ayon_core/plugins/publish/integrate_version_attrs.py +++ b/client/ayon_core/plugins/publish/integrate_version_attrs.py @@ -82,6 +82,6 @@ class IntegrateVersionAttributes(pyblish.api.ContextPlugin): return ( instance.data.get("label") or instance.data.get("name") - or instance.data.get("subset") + or instance.data.get("productName") or str(instance) ) diff --git a/client/ayon_core/plugins/publish/preintegrate_thumbnail_representation.py b/client/ayon_core/plugins/publish/preintegrate_thumbnail_representation.py index fc60948139..8bd67c0183 100644 --- a/client/ayon_core/plugins/publish/preintegrate_thumbnail_representation.py +++ b/client/ayon_core/plugins/publish/preintegrate_thumbnail_representation.py @@ -37,8 +37,8 @@ class PreIntegrateThumbnails(pyblish.api.InstancePlugin): if not thumbnail_repres: return - family = instance.data["family"] - subset_name = instance.data["subset"] + product_type = instance.data["productType"] + product_name = instance.data["productName"] host_name = instance.context.data["hostName"] anatomy_data = instance.data["anatomyData"] @@ -50,8 +50,8 @@ class PreIntegrateThumbnails(pyblish.api.InstancePlugin): "hosts": host_name, "task_names": task.get("name"), "task_types": task.get("type"), - "families": family, - "subsets": subset_name, + "product_types": product_type, + "product_names": product_name, }, logger=self.log ) diff --git a/client/ayon_core/plugins/publish/validate_publish_dir.py b/client/ayon_core/plugins/publish/validate_publish_dir.py index 5827774cca..f89a7c6810 100644 --- a/client/ayon_core/plugins/publish/validate_publish_dir.py +++ b/client/ayon_core/plugins/publish/validate_publish_dir.py @@ -21,7 +21,7 @@ class ValidatePublishDir(pyblish.api.InstancePlugin): checked_template_names = ["source"] # validate instances might have interim family, needs to be mapped to final - family_mapping = { + product_type_mapping = { "renderLayer": "render", "renderLocal": "render" } @@ -39,7 +39,7 @@ class ValidatePublishDir(pyblish.api.InstancePlugin): self, "Instance meant for in place publishing." " Its 'originalDirname' must be collected." - " Contact OP developer to modify collector." + " Contact AYON developer to modify collector." ) anatomy = instance.context.data["anatomy"] @@ -62,15 +62,17 @@ class ValidatePublishDir(pyblish.api.InstancePlugin): """Find template which will be used during integration.""" project_name = instance.context.data["projectName"] host_name = instance.context.data["hostName"] + product_type = instance.data["productType"] + mapped_product_type = ( + self.product_type_mapping.get(product_type) or product_type + ) anatomy_data = instance.data["anatomyData"] - family = anatomy_data["family"] - family = self.family_mapping.get(family) or family task_info = anatomy_data.get("task") or {} return get_publish_template_name( project_name, host_name, - family, + mapped_product_type, task_name=task_info.get("name"), task_type=task_info.get("type"), project_settings=instance.context.data["project_settings"], diff --git a/client/ayon_core/plugins/publish/validate_unique_subsets.py b/client/ayon_core/plugins/publish/validate_unique_subsets.py index 5e0e90cff6..3144675c50 100644 --- a/client/ayon_core/plugins/publish/validate_unique_subsets.py +++ b/client/ayon_core/plugins/publish/validate_unique_subsets.py @@ -6,17 +6,17 @@ from ayon_core.pipeline.publish import ( class ValidateSubsetUniqueness(pyblish.api.ContextPlugin): - """Validate all subset names are unique. + """Validate all product names are unique. This only validates whether the instances currently set to publish from - the workfile overlap one another for the asset + subset they are publishing + the workfile overlap one another for the asset + product they are publishing to. This does not perform any check against existing publishes in the database - since it is allowed to publish into existing subsets resulting in + since it is allowed to publish into existing products resulting in versioning. - A subset may appear twice to publish from the workfile if one + A product may appear twice to publish from the workfile if one of them is set to publish to another asset than the other. """ @@ -27,8 +27,8 @@ class ValidateSubsetUniqueness(pyblish.api.ContextPlugin): def process(self, context): - # Find instance per (asset,subset) - instance_per_asset_subset = defaultdict(list) + # Find instance per (asset,product) + instance_per_asset_product = defaultdict(list) for instance in context: # Ignore disabled instances @@ -42,30 +42,30 @@ class ValidateSubsetUniqueness(pyblish.api.ContextPlugin): "{}".format(instance.name)) continue - # Ignore instance without subset data - subset = instance.data.get("subset") - if subset is None: - self.log.warning("Instance found without `subset` data: " - "{}".format(instance.name)) + # Ignore instance without product data + product_name = instance.data.get("productName") + if product_name is None: + self.log.warning(( + "Instance found without `productName` in data: {}" + ).format(instance.name)) continue - instance_per_asset_subset[(asset, subset)].append(instance) + instance_per_asset_product[(asset, product_name)].append(instance) non_unique = [] - for (asset, subset), instances in instance_per_asset_subset.items(): + for (asset, product_name), instances in instance_per_asset_product.items(): - # A single instance per asset, subset is fine + # A single instance per asset, product is fine if len(instances) < 2: continue - non_unique.append("{asset} > {subset}".format(asset=asset, - subset=subset)) + non_unique.append("{} > {}".format(asset, product_name)) if not non_unique: # All is ok return - msg = ("Instance subset names {} are not unique. ".format(non_unique) + + msg = ("Instance product names {} are not unique. ".format(non_unique) + "Please remove or rename duplicates.") formatting_data = { "non_unique": ",".join(non_unique) From 92d3efe10166f7f84c100bd76c05c313cbe59527 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Feb 2024 18:38:17 +0100 Subject: [PATCH 224/573] modified content of 'loadedVersions' --- .../plugins/publish/collect_scene_loaded_versions.py | 6 +++--- client/ayon_core/plugins/publish/integrate_inputlinks.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_scene_loaded_versions.py b/client/ayon_core/plugins/publish/collect_scene_loaded_versions.py index 397a3ce87c..c1326f164d 100644 --- a/client/ayon_core/plugins/publish/collect_scene_loaded_versions.py +++ b/client/ayon_core/plugins/publish/collect_scene_loaded_versions.py @@ -67,9 +67,9 @@ class CollectSceneLoadedVersions(pyblish.api.ContextPlugin): # NOTE: # may have more then one representation that are same version version = { - "subsetName": con["name"], - "representation": repre_doc["_id"], - "version": repre_doc["parent"], + "container_name": con["name"], + "representation_id": repre_doc["_id"], + "version_id": repre_doc["parent"], } loaded_versions.append(version) diff --git a/client/ayon_core/plugins/publish/integrate_inputlinks.py b/client/ayon_core/plugins/publish/integrate_inputlinks.py index 809e93f27e..f7e802f410 100644 --- a/client/ayon_core/plugins/publish/integrate_inputlinks.py +++ b/client/ayon_core/plugins/publish/integrate_inputlinks.py @@ -107,7 +107,7 @@ class IntegrateInputLinksAYON(pyblish.api.ContextPlugin): self.add_link( new_links_by_type, "reference", - version["version"], + version["version_id"], workfile_version_id, ) From 9a6db4df408b84b8b888134c65e7b2f29d032837 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 22 Feb 2024 09:31:38 +0100 Subject: [PATCH 225/573] unreal is using product type and name --- client/ayon_core/hosts/unreal/api/plugin.py | 66 ++++++++++--------- .../ayon_core/hosts/unreal/api/rendering.py | 2 +- .../unreal/plugins/create/create_camera.py | 6 +- .../unreal/plugins/create/create_layout.py | 2 +- .../unreal/plugins/create/create_look.py | 8 +-- .../unreal/plugins/create/create_render.py | 26 ++++---- .../plugins/create/create_staticmeshfbx.py | 2 +- .../unreal/plugins/create/create_uasset.py | 12 ++-- .../plugins/load/load_alembic_animation.py | 2 +- .../unreal/plugins/load/load_animation.py | 2 +- .../hosts/unreal/plugins/load/load_camera.py | 2 +- .../plugins/load/load_geometrycache_abc.py | 2 +- .../hosts/unreal/plugins/load/load_layout.py | 4 +- .../plugins/load/load_skeletalmesh_abc.py | 2 +- .../plugins/load/load_skeletalmesh_fbx.py | 2 +- .../plugins/load/load_staticmesh_abc.py | 2 +- .../plugins/load/load_staticmesh_fbx.py | 2 +- .../hosts/unreal/plugins/load/load_uasset.py | 2 +- .../unreal/plugins/load/load_yeticache.py | 2 +- .../publish/collect_render_instances.py | 9 +-- 20 files changed, 80 insertions(+), 77 deletions(-) diff --git a/client/ayon_core/hosts/unreal/api/plugin.py b/client/ayon_core/hosts/unreal/api/plugin.py index ddf54f6c79..f379291742 100644 --- a/client/ayon_core/hosts/unreal/api/plugin.py +++ b/client/ayon_core/hosts/unreal/api/plugin.py @@ -35,16 +35,16 @@ class UnrealBaseCreator(Creator): suffix = "_INS" @staticmethod - def cache_subsets(shared_data): + def cache_instance_data(shared_data): """Cache instances for Creators to shared data. - Create `unreal_cached_subsets` key when needed in shared data and + Create `unreal_cached_instances` key when needed in shared data and fill it with all collected instances from the scene under its respective creator identifiers. If legacy instances are detected in the scene, create - `unreal_cached_legacy_subsets` there and fill it with - all legacy subsets under family as a key. + `unreal_cached_legacy_instances` there and fill it with + all legacy products under family as a key. Args: Dict[str, Any]: Shared data. @@ -53,34 +53,36 @@ class UnrealBaseCreator(Creator): Dict[str, Any]: Shared data dictionary. """ - if shared_data.get("unreal_cached_subsets") is None: - unreal_cached_subsets = collections.defaultdict(list) - unreal_cached_legacy_subsets = collections.defaultdict(list) - for instance in ls_inst(): - creator_id = instance.get("creator_identifier") - if creator_id: - unreal_cached_subsets[creator_id].append(instance) - else: - family = instance.get("family") - unreal_cached_legacy_subsets[family].append(instance) - shared_data["unreal_cached_subsets"] = unreal_cached_subsets - shared_data["unreal_cached_legacy_subsets"] = ( - unreal_cached_legacy_subsets - ) - return shared_data + if "unreal_cached_instances" in shared_data: + return - def create(self, subset_name, instance_data, pre_create_data): + unreal_cached_instances = collections.defaultdict(list) + unreal_cached_legacy_instances = collections.defaultdict(list) + for instance in ls_inst(): + creator_id = instance.get("creator_identifier") + if creator_id: + unreal_cached_instances[creator_id].append(instance) + else: + family = instance.get("family") + unreal_cached_legacy_instances[family].append(instance) + + shared_data["unreal_cached_instances"] = unreal_cached_instances + shared_data["unreal_cached_legacy_instances"] = ( + unreal_cached_legacy_instances + ) + + def create(self, product_name, instance_data, pre_create_data): try: - instance_name = f"{subset_name}{self.suffix}" + instance_name = f"{product_name}{self.suffix}" pub_instance = create_publish_instance(instance_name, self.root) - instance_data["subset"] = subset_name + instance_data["productName"] = product_name instance_data["instance_path"] = f"{self.root}/{instance_name}" instance = CreatedInstance( - self.family, - subset_name, + self.product_type, + product_name, instance_data, self) self._add_instance_to_context(instance) @@ -106,9 +108,9 @@ class UnrealBaseCreator(Creator): def collect_instances(self): # cache instances if missing - self.cache_subsets(self.collection_shared_data) + self.cache_instance_data(self.collection_shared_data) for instance in self.collection_shared_data[ - "unreal_cached_subsets"].get(self.identifier, []): + "unreal_cached_instances"].get(self.identifier, []): # Unreal saves metadata as string, so we need to convert it back instance['creator_attributes'] = ast.literal_eval( instance.get('creator_attributes', '{}')) @@ -148,11 +150,11 @@ class UnrealBaseCreator(Creator): class UnrealAssetCreator(UnrealBaseCreator): """Base class for Unreal creator plugins based on assets.""" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): """Create instance of the asset. Args: - subset_name (str): Name of the subset. + product_name (str): Name of the product. instance_data (dict): Data for the instance. pre_create_data (dict): Data for the instance. @@ -172,7 +174,7 @@ class UnrealAssetCreator(UnrealBaseCreator): a.get_path_name() for a in sel_objects] super(UnrealAssetCreator, self).create( - subset_name, + product_name, instance_data, pre_create_data) @@ -192,11 +194,11 @@ class UnrealAssetCreator(UnrealBaseCreator): class UnrealActorCreator(UnrealBaseCreator): """Base class for Unreal creator plugins based on actors.""" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): """Create instance of the asset. Args: - subset_name (str): Name of the subset. + product_name (str): Name of the product. instance_data (dict): Data for the instance. pre_create_data (dict): Data for the instance. @@ -226,7 +228,7 @@ class UnrealActorCreator(UnrealBaseCreator): instance_data["level"] = world.get_path_name() super(UnrealActorCreator, self).create( - subset_name, + product_name, instance_data, pre_create_data) diff --git a/client/ayon_core/hosts/unreal/api/rendering.py b/client/ayon_core/hosts/unreal/api/rendering.py index 4a15ceb89a..395513aefa 100644 --- a/client/ayon_core/hosts/unreal/api/rendering.py +++ b/client/ayon_core/hosts/unreal/api/rendering.py @@ -56,7 +56,7 @@ def start_rendering(): for i in instances: data = pipeline.parse_container(i.get_path_name()) - if data["family"] == "render": + if data["productType"] == "render": inst_data.append(data) try: diff --git a/client/ayon_core/hosts/unreal/plugins/create/create_camera.py b/client/ayon_core/hosts/unreal/plugins/create/create_camera.py index f78de00f44..3ffb9dd70b 100644 --- a/client/ayon_core/hosts/unreal/plugins/create/create_camera.py +++ b/client/ayon_core/hosts/unreal/plugins/create/create_camera.py @@ -13,10 +13,10 @@ class CreateCamera(UnrealAssetCreator): identifier = "io.ayon.creators.unreal.camera" label = "Camera" - family = "camera" + product_type = "camera" icon = "fa.camera" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): if pre_create_data.get("use_selection"): sel_objects = unreal.EditorUtilityLibrary.get_selected_assets() selection = [a.get_path_name() for a in sel_objects] @@ -33,6 +33,6 @@ class CreateCamera(UnrealAssetCreator): instance_data["level"] = world.get_path_name() super(CreateCamera, self).create( - subset_name, + product_name, instance_data, pre_create_data) diff --git a/client/ayon_core/hosts/unreal/plugins/create/create_layout.py b/client/ayon_core/hosts/unreal/plugins/create/create_layout.py index 0ec8a8d445..9bcddfe507 100644 --- a/client/ayon_core/hosts/unreal/plugins/create/create_layout.py +++ b/client/ayon_core/hosts/unreal/plugins/create/create_layout.py @@ -9,5 +9,5 @@ class CreateLayout(UnrealActorCreator): identifier = "io.ayon.creators.unreal.layout" label = "Layout" - family = "layout" + product_type = "layout" icon = "cubes" diff --git a/client/ayon_core/hosts/unreal/plugins/create/create_look.py b/client/ayon_core/hosts/unreal/plugins/create/create_look.py index ecc0783c35..edc6d45f2f 100644 --- a/client/ayon_core/hosts/unreal/plugins/create/create_look.py +++ b/client/ayon_core/hosts/unreal/plugins/create/create_look.py @@ -16,10 +16,10 @@ class CreateLook(UnrealAssetCreator): identifier = "io.ayon.creators.unreal.look" label = "Look" - family = "look" + product_type = "look" icon = "paint-brush" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): # We need to set this to True for the parent class to work pre_create_data["use_selection"] = True sel_objects = unreal.EditorUtilityLibrary.get_selected_assets() @@ -33,7 +33,7 @@ class CreateLook(UnrealAssetCreator): look_directory = "/Game/Ayon/Looks" # Create the folder - folder_name = create_folder(look_directory, subset_name) + folder_name = create_folder(look_directory, product_name) path = f"{look_directory}/{folder_name}" instance_data["look"] = path @@ -67,7 +67,7 @@ class CreateLook(UnrealAssetCreator): unreal.EditorAssetLibrary.save_asset(object_path) super(CreateLook, self).create( - subset_name, + product_name, instance_data, pre_create_data) diff --git a/client/ayon_core/hosts/unreal/plugins/create/create_render.py b/client/ayon_core/hosts/unreal/plugins/create/create_render.py index 5bb782e7ea..cbec84c543 100644 --- a/client/ayon_core/hosts/unreal/plugins/create/create_render.py +++ b/client/ayon_core/hosts/unreal/plugins/create/create_render.py @@ -24,11 +24,11 @@ class CreateRender(UnrealAssetCreator): identifier = "io.ayon.creators.unreal.render" label = "Render" - family = "render" + product_type = "render" icon = "eye" def create_instance( - self, instance_data, subset_name, pre_create_data, + self, instance_data, product_name, pre_create_data, selected_asset_path, master_seq, master_lvl, seq_data ): instance_data["members"] = [selected_asset_path] @@ -40,12 +40,12 @@ class CreateRender(UnrealAssetCreator): instance_data["frameEnd"] = seq_data.get('frame_range')[1] super(CreateRender, self).create( - subset_name, + product_name, instance_data, pre_create_data) def create_with_new_sequence( - self, subset_name, instance_data, pre_create_data + self, product_name, instance_data, pre_create_data ): # If the option to create a new level sequence is selected, # create a new level sequence and a master level. @@ -53,7 +53,7 @@ class CreateRender(UnrealAssetCreator): root = f"/Game/Ayon/Sequences" # Create a new folder for the sequence in root - sequence_dir_name = create_folder(root, subset_name) + sequence_dir_name = create_folder(root, product_name) sequence_dir = f"{root}/{sequence_dir_name}" unreal.log_warning(f"sequence_dir: {sequence_dir}") @@ -61,7 +61,7 @@ class CreateRender(UnrealAssetCreator): # Create the level sequence asset_tools = unreal.AssetToolsHelpers.get_asset_tools() seq = asset_tools.create_asset( - asset_name=subset_name, + asset_name=product_name, package_path=sequence_dir, asset_class=unreal.LevelSequence, factory=unreal.LevelSequenceFactoryNew()) @@ -92,7 +92,7 @@ class CreateRender(UnrealAssetCreator): else: unreal.EditorLevelLibrary.save_current_level() - ml_path = f"{sequence_dir}/{subset_name}_MasterLevel" + ml_path = f"{sequence_dir}/{product_name}_MasterLevel" if UNREAL_VERSION.major >= 5: unreal.LevelEditorSubsystem().new_level(ml_path) @@ -107,11 +107,11 @@ class CreateRender(UnrealAssetCreator): seq.get_playback_end())} self.create_instance( - instance_data, subset_name, pre_create_data, + instance_data, product_name, pre_create_data, seq.get_path_name(), seq.get_path_name(), ml_path, seq_data) def create_from_existing_sequence( - self, subset_name, instance_data, pre_create_data + self, product_name, instance_data, pre_create_data ): ar = unreal.AssetRegistryHelpers.get_asset_registry() @@ -224,16 +224,16 @@ class CreateRender(UnrealAssetCreator): continue self.create_instance( - instance_data, subset_name, pre_create_data, + instance_data, product_name, pre_create_data, selected_asset_path, master_seq, master_lvl, seq_data) - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): if pre_create_data.get("create_seq"): self.create_with_new_sequence( - subset_name, instance_data, pre_create_data) + product_name, instance_data, pre_create_data) else: self.create_from_existing_sequence( - subset_name, instance_data, pre_create_data) + product_name, instance_data, pre_create_data) def get_pre_create_attr_defs(self): return [ diff --git a/client/ayon_core/hosts/unreal/plugins/create/create_staticmeshfbx.py b/client/ayon_core/hosts/unreal/plugins/create/create_staticmeshfbx.py index 7fcd6f165a..603b852873 100644 --- a/client/ayon_core/hosts/unreal/plugins/create/create_staticmeshfbx.py +++ b/client/ayon_core/hosts/unreal/plugins/create/create_staticmeshfbx.py @@ -9,5 +9,5 @@ class CreateStaticMeshFBX(UnrealAssetCreator): identifier = "io.ayon.creators.unreal.staticmeshfbx" label = "Static Mesh (FBX)" - family = "unrealStaticMesh" + product_type = "unrealStaticMesh" icon = "cube" diff --git a/client/ayon_core/hosts/unreal/plugins/create/create_uasset.py b/client/ayon_core/hosts/unreal/plugins/create/create_uasset.py index 500726497d..1cd532c63d 100644 --- a/client/ayon_core/hosts/unreal/plugins/create/create_uasset.py +++ b/client/ayon_core/hosts/unreal/plugins/create/create_uasset.py @@ -14,12 +14,12 @@ class CreateUAsset(UnrealAssetCreator): identifier = "io.ayon.creators.unreal.uasset" label = "UAsset" - family = "uasset" + product_type = "uasset" icon = "cube" extension = ".uasset" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): if pre_create_data.get("use_selection"): ar = unreal.AssetRegistryHelpers.get_asset_registry() @@ -44,7 +44,7 @@ class CreateUAsset(UnrealAssetCreator): f"{Path(sys_path).name} is not a {self.label}.") super(CreateUAsset, self).create( - subset_name, + product_name, instance_data, pre_create_data) @@ -54,13 +54,13 @@ class CreateUMap(CreateUAsset): identifier = "io.ayon.creators.unreal.umap" label = "Level" - family = "uasset" + product_type = "uasset" extension = ".umap" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): instance_data["families"] = ["umap"] super(CreateUMap, self).create( - subset_name, + product_name, instance_data, pre_create_data) diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_alembic_animation.py b/client/ayon_core/hosts/unreal/plugins/load/load_alembic_animation.py index 4d7760e385..fe13e5989b 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_alembic_animation.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_alembic_animation.py @@ -56,7 +56,7 @@ class AnimationAlembicLoader(plugin.Loader): Args: context (dict): application context - name (str): subset name + name (str): Product name namespace (str): in Unreal this is basically path to container. This is not passed here, so namespace is set by `containerise()` because only then we know diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_animation.py b/client/ayon_core/hosts/unreal/plugins/load/load_animation.py index 4d44b6c0c2..ee5b8d9244 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_animation.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_animation.py @@ -128,7 +128,7 @@ class AnimationFBXLoader(plugin.Loader): Args: context (dict): application context - name (str): subset name + name (str): Product name namespace (str): in Unreal this is basically path to container. This is not passed here, so namespace is set by `containerise()` because only then we know diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_camera.py b/client/ayon_core/hosts/unreal/plugins/load/load_camera.py index faba561085..257cc2c720 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_camera.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_camera.py @@ -70,7 +70,7 @@ class CameraLoader(plugin.Loader): Args: context (dict): application context - name (str): subset name + name (str): Product name namespace (str): in Unreal this is basically path to container. This is not passed here, so namespace is set by `containerise()` because only then we know diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py b/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py index 360737cbc5..d7a4df1b03 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py @@ -108,7 +108,7 @@ class PointCacheAlembicLoader(plugin.Loader): Args: context (dict): application context - name (str): subset name + name (str): Product name namespace (str): in Unreal this is basically path to container. This is not passed here, so namespace is set by `containerise()` because only then we know diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_layout.py b/client/ayon_core/hosts/unreal/plugins/load/load_layout.py index a1cc2e785a..2a05720d7a 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_layout.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_layout.py @@ -481,7 +481,7 @@ class LayoutLoader(plugin.Loader): for asset_container in asset_containers: package_path = asset_container.get_editor_property('package_path') family = EditorAssetLibrary.get_metadata_tag( - asset_container.get_asset(), 'family') + asset_container.get_asset(), "family") assets = EditorAssetLibrary.list_assets( str(package_path), recursive=False) if family == 'model': @@ -501,7 +501,7 @@ class LayoutLoader(plugin.Loader): Args: context (dict): application context - name (str): subset name + name (str): Product name namespace (str): in Unreal this is basically path to container. This is not passed here, so namespace is set by `containerise()` because only then we know diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py index 225df3b440..3ab6ea8ebd 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py @@ -94,7 +94,7 @@ class SkeletalMeshAlembicLoader(plugin.Loader): Args: context (dict): application context - name (str): subset name + name (str): Product name namespace (str): in Unreal this is basically path to container. This is not passed here, so namespace is set by `containerise()` because only then we know diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py index 1c45c58d02..cbdb4901f8 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py @@ -99,7 +99,7 @@ class SkeletalMeshFBXLoader(plugin.Loader): Args: context (dict): application context - name (str): subset name + name (str): Product name namespace (str): in Unreal this is basically path to container. This is not passed here, so namespace is set by `containerise()` because only then we know diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_abc.py b/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_abc.py index a0814b5b07..c60ad8814c 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_abc.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_abc.py @@ -95,7 +95,7 @@ class StaticMeshAlembicLoader(plugin.Loader): Args: context (dict): application context - name (str): subset name + name (str): Product name namespace (str): in Unreal this is basically path to container. This is not passed here, so namespace is set by `containerise()` because only then we know diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py b/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py index a78b1bc959..c9271159c4 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py @@ -87,7 +87,7 @@ class StaticMeshFBXLoader(plugin.Loader): Args: context (dict): application context - name (str): subset name + name (str): Product name namespace (str): in Unreal this is basically path to container. This is not passed here, so namespace is set by `containerise()` because only then we know diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_uasset.py b/client/ayon_core/hosts/unreal/plugins/load/load_uasset.py index 048ec8eaba..0898035985 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_uasset.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_uasset.py @@ -28,7 +28,7 @@ class UAssetLoader(plugin.Loader): Args: context (dict): application context - name (str): subset name + name (str): Product name namespace (str): in Unreal this is basically path to container. This is not passed here, so namespace is set by `containerise()` because only then we know diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_yeticache.py b/client/ayon_core/hosts/unreal/plugins/load/load_yeticache.py index b643f352b7..d21c6205fc 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_yeticache.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_yeticache.py @@ -69,7 +69,7 @@ class YetiLoader(plugin.Loader): Args: context (dict): application context - name (str): subset name + name (str): Product name namespace (str): in Unreal this is basically path to container. This is not passed here, so namespace is set by `containerise()` because only then we know diff --git a/client/ayon_core/hosts/unreal/plugins/publish/collect_render_instances.py b/client/ayon_core/hosts/unreal/plugins/publish/collect_render_instances.py index 8bbf5a5c62..0f22ea8085 100644 --- a/client/ayon_core/hosts/unreal/plugins/publish/collect_render_instances.py +++ b/client/ayon_core/hosts/unreal/plugins/publish/collect_render_instances.py @@ -57,19 +57,20 @@ class CollectRenderInstances(pyblish.api.InstancePlugin): seq = s.get('sequence') seq_name = seq.get_name() + new_product_name = f"{data.get('productName')}_{seq_name}" new_instance = context.create_instance( - f"{data.get('subset')}_" - f"{seq_name}") + new_product_name + ) new_instance[:] = seq_name new_data = new_instance.data new_data["folderPath"] = seq_name new_data["setMembers"] = seq_name - new_data["family"] = "render" + new_data["productType"] = "render" + new_data["productName"] = new_product_name new_data["families"] = ["render", "review"] new_data["parent"] = data.get("parent") - new_data["subset"] = f"{data.get('subset')}_{seq_name}" new_data["level"] = data.get("level") new_data["output"] = s.get('output') new_data["fps"] = seq.get_display_rate().numerator From b286e310b628b8c0e705b55f71d8c65fed2f527e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 22 Feb 2024 09:46:30 +0100 Subject: [PATCH 226/573] tvpaint is using product name and type --- client/ayon_core/hosts/tvpaint/api/plugin.py | 32 ++++---- .../tvpaint/plugins/create/convert_legacy.py | 4 +- .../tvpaint/plugins/create/create_render.py | 80 +++++++++---------- .../tvpaint/plugins/create/create_review.py | 10 +-- .../tvpaint/plugins/create/create_workfile.py | 10 +-- .../plugins/load/load_reference_image.py | 4 +- .../tvpaint/plugins/load/load_workfile.py | 2 +- .../publish/collect_instance_frames.py | 2 +- .../publish/collect_render_instances.py | 14 ++-- .../plugins/publish/collect_workfile.py | 2 +- .../plugins/publish/extract_sequence.py | 2 +- .../publish/help/validate_asset_name.xml | 6 +- .../help/validate_duplicated_layer_names.xml | 4 +- .../help/validate_layers_visibility.xml | 4 +- .../help/validate_missing_layer_names.xml | 4 +- .../publish/validate_render_layer_group.py | 16 ++-- .../ayon_core/pipeline/create/product_name.py | 9 ++- 17 files changed, 104 insertions(+), 101 deletions(-) diff --git a/client/ayon_core/hosts/tvpaint/api/plugin.py b/client/ayon_core/hosts/tvpaint/api/plugin.py index 88a0e74528..40bf440dc7 100644 --- a/client/ayon_core/hosts/tvpaint/api/plugin.py +++ b/client/ayon_core/hosts/tvpaint/api/plugin.py @@ -3,7 +3,7 @@ import re from ayon_core.pipeline import LoaderPlugin from ayon_core.pipeline.create import ( CreatedInstance, - get_subset_name, + get_product_name, AutoCreator, Creator, ) @@ -17,8 +17,8 @@ SHARED_DATA_KEY = "openpype.tvpaint.instances" class TVPaintCreatorCommon: @property - def subset_template_family_filter(self): - return self.family + def product_template_product_type(self): + return self.product_type def _cache_and_get_instances(self): return cache_and_get_instances( @@ -53,7 +53,7 @@ class TVPaintCreatorCommon: cur_instance_data.update(instance_data) self.host.write_instances(cur_instances) - def _custom_get_subset_name( + def _custom_get_product_name( self, variant, task_name, @@ -66,16 +66,16 @@ class TVPaintCreatorCommon: variant, task_name, asset_doc, project_name, host_name, instance ) - return get_subset_name( - self.family, - variant, - task_name, - asset_doc, + return get_product_name( project_name, + asset_doc, + task_name, host_name, + self.product_type, + variant, dynamic_data=dynamic_data, project_settings=self.project_settings, - family_filter=self.subset_template_family_filter + product_type_filter=self.product_template_product_type ) @@ -118,8 +118,8 @@ class TVPaintCreator(Creator, TVPaintCreatorCommon): output["task"] = task_name return output - def get_subset_name(self, *args, **kwargs): - return self._custom_get_subset_name(*args, **kwargs) + def get_product_name(self, *args, **kwargs): + return self._custom_get_product_name(*args, **kwargs) def _store_new_instance(self, new_instance): instances_data = self.host.list_instances() @@ -135,8 +135,8 @@ class TVPaintAutoCreator(AutoCreator, TVPaintCreatorCommon): def update_instances(self, update_list): self._update_create_instances(update_list) - def get_subset_name(self, *args, **kwargs): - return self._custom_get_subset_name(*args, **kwargs) + def get_product_name(self, *args, **kwargs): + return self._custom_get_product_name(*args, **kwargs) class Loader(LoaderPlugin): @@ -161,8 +161,8 @@ class Loader(LoaderPlugin): `0` is used ase base. Args: - asset_name (str): Name of subset's parent asset document. - name (str): Name of loaded subset. + asset_name (str): Name of product's parent asset document. + name (str): Name of loaded product. Returns: (str): `{asset_name}_{name}_{higher suffix + 1}` diff --git a/client/ayon_core/hosts/tvpaint/plugins/create/convert_legacy.py b/client/ayon_core/hosts/tvpaint/plugins/create/convert_legacy.py index d3c6c06c8a..1415adac2b 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/create/convert_legacy.py +++ b/client/ayon_core/hosts/tvpaint/plugins/create/convert_legacy.py @@ -101,7 +101,7 @@ class TVPaintLegacyConverted(SubsetConvertorPlugin): render_layer["creator_attributes"] = { "group_id": group_id } - render_layer["family"] = "render" + render_layer["productType"] = "render" group = groups_by_id[group_id] # Use group name for variant group["variant"] = group["name"] @@ -128,7 +128,7 @@ class TVPaintLegacyConverted(SubsetConvertorPlugin): render_pass["creator_identifier"] = "render.pass" render_pass["instance_id"] = render_pass.pop("uuid") - render_pass["family"] = "render" + render_pass["productType"] = "render" render_pass["creator_attributes"] = { "render_layer_instance_id": render_layer["instance_id"] diff --git a/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py b/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py index 7d908e8018..718beb5883 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py +++ b/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py @@ -118,8 +118,8 @@ class CreateRenderlayer(TVPaintCreator): """ label = "Render Layer" - family = "render" - subset_template_family_filter = "renderLayer" + product_type = "render" + product_template_product_type = "renderLayer" identifier = "render.layer" icon = "fa5.images" @@ -165,7 +165,7 @@ class CreateRenderlayer(TVPaintCreator): if layer["selected"] } - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): self.log.debug("Query data from workfile.") group_name = instance_data["variant"] @@ -195,7 +195,7 @@ class CreateRenderlayer(TVPaintCreator): ): raise CreatorError(( f"Group \"{group_item.get('name')}\" is already used" - f" by another render layer \"{instance['subset']}\"" + f" by another render layer \"{instance['productName']}\"" )) self.log.debug(f"Selected group id is \"{group_id}\".") @@ -208,10 +208,10 @@ class CreateRenderlayer(TVPaintCreator): creator_attributes["group_id"] = group_id creator_attributes["mark_for_review"] = mark_for_review - self.log.info(f"Subset name is {subset_name}") + self.log.info(f"Subset name is {product_name}") new_instance = CreatedInstance( - self.family, - subset_name, + self.product_type, + product_name, instance_data, self ) @@ -374,8 +374,8 @@ class CreateRenderlayer(TVPaintCreator): class CreateRenderPass(TVPaintCreator): - family = "render" - subset_template_family_filter = "renderPass" + product_type = "render" + product_template_product_type = "renderPass" identifier = "render.pass" label = "Render Pass" icon = "fa5.image" @@ -447,7 +447,7 @@ class CreateRenderPass(TVPaintCreator): "renderlayer": render_layer_variant }) try: - new_label = instance["subset"].format(**render_layer_data) + new_label = instance["productName"].format(**render_layer_data) except (KeyError, ValueError): pass @@ -457,7 +457,7 @@ class CreateRenderPass(TVPaintCreator): instance["group"] = new_group return old_group != new_group or old_label != new_label - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): render_layer_instance_id = pre_create_data.get( "render_layer_instance_id" ) @@ -523,18 +523,18 @@ class CreateRenderPass(TVPaintCreator): instances_to_remove.append(instance) render_layer = render_layer_instance["variant"] - subset_name_fill_data = {"renderlayer": render_layer} + product_name_fill_data = {"renderlayer": render_layer} - # Format dynamic keys in subset name - label = subset_name + # Format dynamic keys in product name + label = product_name try: label = label.format( - **prepare_template_data(subset_name_fill_data) + **prepare_template_data(product_name_fill_data) ) except (KeyError, ValueError): pass - self.log.info(f"New subset name is \"{label}\".") + self.log.info(f"New product name is \"{label}\".") instance_data["label"] = label instance_data["group"] = f"{self.get_group_label()} ({render_layer})" instance_data["layer_names"] = list(marked_layer_names) @@ -551,8 +551,8 @@ class CreateRenderPass(TVPaintCreator): ) new_instance = CreatedInstance( - self.family, - subset_name, + self.product_type, + product_name, instance_data, self ) @@ -608,7 +608,7 @@ class CreateRenderPass(TVPaintCreator): render_layers = [ { "value": inst["instance_id"], - "label": inst["subset"] + "label": inst["productName"] } for inst in current_instances if inst.get("creator_identifier") == CreateRenderlayer.identifier @@ -674,7 +674,7 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): Never will have any instances, all instances belong to different creators. """ - family = "render" + product_type = "render" label = "Render Layer/Passes" identifier = "render.auto.detect.creator" order = CreateRenderPass.order + 10 @@ -777,7 +777,7 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): self.create_context.creators[CreateRenderlayer.identifier] ) - subset_name: str = creator.get_subset_name( + product_name: str = creator.get_product_name( variant, task_name, asset_doc, @@ -788,20 +788,20 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): if existing_instance is not None: existing_instance["folderPath"] = asset_name existing_instance["task"] = task_name - existing_instance["subset"] = subset_name + existing_instance["productName"] = product_name return existing_instance instance_data: dict[str, str] = { "folderPath": asset_name, "task": task_name, - "family": creator.family, + "productType": creator.product_type, "variant": variant, } pre_create_data: dict[str, str] = { "group_id": group_id, "mark_for_review": mark_for_review } - return creator.create(subset_name, instance_data, pre_create_data) + return creator.create(product_name, instance_data, pre_create_data) def _prepare_render_passes( self, @@ -831,7 +831,7 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): if (render_pass["layer_names"]) > 1: variant = render_pass["variant"] - subset_name = creator.get_subset_name( + product_name = creator.get_product_name( variant, task_name, asset_doc, @@ -843,13 +843,13 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): if render_pass is not None: render_pass["folderPath"] = asset_name render_pass["task"] = task_name - render_pass["subset"] = subset_name + render_pass["productName"] = product_name continue instance_data: dict[str, str] = { "folderPath": asset_name, "task": task_name, - "family": creator.family, + "productType": creator.product_type, "variant": variant } @@ -858,7 +858,7 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): "layer_names": [layer_name], "mark_for_review": mark_for_review } - creator.create(subset_name, instance_data, pre_create_data) + creator.create(product_name, instance_data, pre_create_data) def _filter_groups( self, @@ -884,7 +884,7 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): new_groups_order.append(group_id) return new_groups_order - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): project_name: str = self.create_context.get_current_project_name() asset_name: str = instance_data["folderPath"] task_name: str = instance_data["task"] @@ -1023,8 +1023,8 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): class TVPaintSceneRenderCreator(TVPaintAutoCreator): - family = "render" - subset_template_family_filter = "renderScene" + product_type = "render" + product_template_product_type = "renderScene" identifier = "render.scene" label = "Scene Render" icon = "fa.file-image-o" @@ -1058,7 +1058,7 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): task_name = create_context.get_current_task_name() asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( self.default_variant, task_name, asset_doc, @@ -1074,7 +1074,7 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): "mark_for_review": True }, "label": self._get_label( - subset_name, + product_name, self.default_pass_name ) } @@ -1082,7 +1082,7 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): data["active"] = False new_instance = CreatedInstance( - self.family, subset_name, data, self + self.product_type, product_name, data, self ) instances_data = self.host.list_instances() instances_data.append(new_instance.data_to_store()) @@ -1112,7 +1112,7 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): or existing_instance["task"] != task_name ): asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( existing_instance["variant"], task_name, asset_doc, @@ -1122,22 +1122,22 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): ) existing_instance["folderPath"] = asset_name existing_instance["task"] = task_name - existing_instance["subset"] = subset_name + existing_instance["productName"] = product_name existing_instance["label"] = self._get_label( - existing_instance["subset"], + existing_instance["productName"], existing_instance["creator_attributes"]["render_pass_name"] ) - def _get_label(self, subset_name, render_pass_name): + def _get_label(self, product_name, render_pass_name): try: - subset_name = subset_name.format(**prepare_template_data({ + product_name = product_name.format(**prepare_template_data({ "renderpass": render_pass_name })) except (KeyError, ValueError): pass - return subset_name + return product_name def get_instance_attr_defs(self): return [ diff --git a/client/ayon_core/hosts/tvpaint/plugins/create/create_review.py b/client/ayon_core/hosts/tvpaint/plugins/create/create_review.py index 773b85c1f5..f7ec71d1a7 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/create/create_review.py +++ b/client/ayon_core/hosts/tvpaint/plugins/create/create_review.py @@ -4,7 +4,7 @@ from ayon_core.hosts.tvpaint.api.plugin import TVPaintAutoCreator class TVPaintReviewCreator(TVPaintAutoCreator): - family = "review" + product_type = "review" identifier = "scene.review" label = "Review" icon = "ei.video" @@ -40,7 +40,7 @@ class TVPaintReviewCreator(TVPaintAutoCreator): if existing_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( self.default_variant, task_name, asset_doc, @@ -57,7 +57,7 @@ class TVPaintReviewCreator(TVPaintAutoCreator): data["active"] = False new_instance = CreatedInstance( - self.family, subset_name, data, self + self.product_type, product_name, data, self ) instances_data = self.host.list_instances() instances_data.append(new_instance.data_to_store()) @@ -69,7 +69,7 @@ class TVPaintReviewCreator(TVPaintAutoCreator): or existing_instance["task"] != task_name ): asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( existing_instance["variant"], task_name, asset_doc, @@ -79,4 +79,4 @@ class TVPaintReviewCreator(TVPaintAutoCreator): ) existing_instance["folderPath"] = asset_name existing_instance["task"] = task_name - existing_instance["subset"] = subset_name + existing_instance["productName"] = product_name diff --git a/client/ayon_core/hosts/tvpaint/plugins/create/create_workfile.py b/client/ayon_core/hosts/tvpaint/plugins/create/create_workfile.py index f0d1c7bae6..0d42adf203 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/tvpaint/plugins/create/create_workfile.py @@ -4,7 +4,7 @@ from ayon_core.hosts.tvpaint.api.plugin import TVPaintAutoCreator class TVPaintWorkfileCreator(TVPaintAutoCreator): - family = "workfile" + product_type = "workfile" identifier = "workfile" label = "Workfile" icon = "fa.file-o" @@ -36,7 +36,7 @@ class TVPaintWorkfileCreator(TVPaintAutoCreator): if existing_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( self.default_variant, task_name, asset_doc, @@ -50,7 +50,7 @@ class TVPaintWorkfileCreator(TVPaintAutoCreator): } new_instance = CreatedInstance( - self.family, subset_name, data, self + self.product_type, product_name, data, self ) instances_data = self.host.list_instances() instances_data.append(new_instance.data_to_store()) @@ -62,7 +62,7 @@ class TVPaintWorkfileCreator(TVPaintAutoCreator): or existing_instance["task"] != task_name ): asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( existing_instance["variant"], task_name, asset_doc, @@ -72,4 +72,4 @@ class TVPaintWorkfileCreator(TVPaintAutoCreator): ) existing_instance["folderPath"] = asset_name existing_instance["task"] = task_name - existing_instance["subset"] = subset_name + existing_instance["productName"] = product_name diff --git a/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py b/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py index 0a12e93f44..856bf69845 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py +++ b/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py @@ -83,8 +83,8 @@ class LoadImage(plugin.Loader): # Prepare layer name asset_name = context["asset"]["name"] - subset_name = context["subset"]["name"] - layer_name = self.get_unique_layer_name(asset_name, subset_name) + product_name = context["subset"]["name"] + layer_name = self.get_unique_layer_name(asset_name, product_name) path = self.filepath_from_context(context) diff --git a/client/ayon_core/hosts/tvpaint/plugins/load/load_workfile.py b/client/ayon_core/hosts/tvpaint/plugins/load/load_workfile.py index e29ecfd442..c642502ed5 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/load/load_workfile.py +++ b/client/ayon_core/hosts/tvpaint/plugins/load/load_workfile.py @@ -101,7 +101,7 @@ class LoadWorkfile(plugin.Loader): "tvpaint", task_name=task_name, task_type=data["task"]["type"], - family="workfile" + product_type="workfile" ) else: version += 1 diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_instance_frames.py b/client/ayon_core/hosts/tvpaint/plugins/publish/collect_instance_frames.py index 63f04cf3ce..e7b7b2cad1 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_instance_frames.py +++ b/client/ayon_core/hosts/tvpaint/plugins/publish/collect_instance_frames.py @@ -31,6 +31,6 @@ class CollectOutputFrameRange(pyblish.api.InstancePlugin): instance.data["frameEnd"] = frame_end self.log.info( "Set frames {}-{} on instance {} ".format( - frame_start, frame_end, instance.data["subset"] + frame_start, frame_end, instance.data["productName"] ) ) diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_render_instances.py b/client/ayon_core/hosts/tvpaint/plugins/publish/collect_render_instances.py index 029c4b7e18..596d257f22 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_render_instances.py +++ b/client/ayon_core/hosts/tvpaint/plugins/publish/collect_render_instances.py @@ -28,10 +28,10 @@ class CollectRenderInstances(pyblish.api.InstancePlugin): self._collect_data_for_review(instance) return - subset_name = instance.data["subset"] - instance.data["name"] = subset_name + product_name = instance.data["productName"] + instance.data["name"] = product_name instance.data["label"] = "{} [{}-{}]".format( - subset_name, + product_name, context.data["sceneMarkIn"] + 1, context.data["sceneMarkOut"] + 1 ) @@ -84,8 +84,8 @@ class CollectRenderInstances(pyblish.api.InstancePlugin): if render_layer_data is None: return render_layer_name = render_layer_data["variant"] - subset_name = instance.data["subset"] - instance.data["subset"] = subset_name.format( + product_name = instance.data["productName"] + instance.data["productName"] = product_name.format( **prepare_template_data({"renderlayer": render_layer_name}) ) @@ -103,8 +103,8 @@ class CollectRenderInstances(pyblish.api.InstancePlugin): render_pass_name = ( instance.data["creator_attributes"]["render_pass_name"] ) - subset_name = instance.data["subset"] - instance.data["subset"] = subset_name.format( + product_name = instance.data["productName"] + instance.data["productName"] = product_name.format( **prepare_template_data({"renderpass": render_pass_name}) ) diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile.py b/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile.py index a3449663f8..a9e9db3872 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile.py +++ b/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile.py @@ -14,7 +14,7 @@ class CollectWorkfile(pyblish.api.InstancePlugin): current_file = context.data["currentFile"] self.log.info( - "Workfile path used for workfile family: {}".format(current_file) + "Workfile path used for workfile product: {}".format(current_file) ) dirpath, filename = os.path.split(current_file) diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/extract_sequence.py b/client/ayon_core/hosts/tvpaint/plugins/publish/extract_sequence.py index 0ab9fbd038..ab30e3dc10 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/publish/extract_sequence.py +++ b/client/ayon_core/hosts/tvpaint/plugins/publish/extract_sequence.py @@ -111,7 +111,7 @@ class ExtractSequence(pyblish.api.Extractor): "Files will be rendered to folder: {}".format(output_dir) ) - if instance.data["family"] == "review": + if instance.data["productType"] == "review": result = self.render_review( output_dir, mark_in, mark_out, scene_bg_color ) diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml b/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml index 33a9ca4247..83753b3410 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml +++ b/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml @@ -2,9 +2,9 @@ Subset context -## Invalid subset context +## Invalid product context -Context of the given subset doesn't match your current scene. +Context of the given product doesn't match your current scene. ### How to repair? @@ -15,7 +15,7 @@ After that restart publishing with Reload button. ### How could this happen? -The subset was created in different scene with different context +The product was created in different scene with different context or the scene file was copy pasted from different context. diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_duplicated_layer_names.xml b/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_duplicated_layer_names.xml index 5d798544c0..23c899cfc6 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_duplicated_layer_names.xml +++ b/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_duplicated_layer_names.xml @@ -10,13 +10,13 @@ Can't determine which layers should be published because there are duplicated la {layer_names} -*Check layer names for all subsets in list on left side.* +*Check layer names for all products 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. +If all of them should be published then you have duplicated product names in the scene. In that case you have to recrete them and use different variant name. diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml b/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml index 5832c74350..5013f38eca 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml +++ b/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml @@ -4,13 +4,13 @@ Layers visibility ## All layers are not visible -Layers visibility was changed during publishing which caused that all layers for subset "{instance_name}" are hidden. +Layers visibility was changed during publishing which caused that all layers for product "{instance_name}" are hidden. ### Layer names for **{instance_name}** {layer_names} -*Check layer names for all subsets in the list on the left side.* +*Check layer names for all products in the list on the left side.* ### How to repair? diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_missing_layer_names.xml b/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_missing_layer_names.xml index e96e7c5044..000fe84844 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_missing_layer_names.xml +++ b/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_missing_layer_names.xml @@ -4,7 +4,7 @@ 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. +Render pass product "{instance_name}" has stored layer names that belong to it's rendering scope but layers were not found in scene. ### Missing layer names @@ -12,7 +12,7 @@ Render pass subset "{instance_name}" has stored layer names that belong to it's ### 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. +Find layers that belong to product {instance_name} and rename them back to expected layer names or remove the product and create new with right layers. diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_render_layer_group.py b/client/ayon_core/hosts/tvpaint/plugins/publish/validate_render_layer_group.py index 66793cbc7f..0e97a01de2 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_render_layer_group.py +++ b/client/ayon_core/hosts/tvpaint/plugins/publish/validate_render_layer_group.py @@ -4,7 +4,7 @@ from ayon_core.pipeline import PublishXmlValidationError class ValidateRenderLayerGroups(pyblish.api.ContextPlugin): - """Validate group ids of renderLayer subsets. + """Validate group ids of renderLayer products. Validate that there are not 2 render layers using the same group. """ @@ -46,19 +46,21 @@ class ValidateRenderLayerGroups(pyblish.api.ContextPlugin): group["name"], group["group_id"], ) - line_join_subset_names = "\n".join([ - f" - {instance['subset']}" + line_join_product_names = "\n".join([ + f" - {instance['productName']}" for instance in instances ]) - joined_subset_names = ", ".join([ - f"\"{instance['subset']}\"" + joined_product_names = ", ".join([ + f"\"{instance['productName']}\"" for instance in instances ]) per_group_msgs.append( - "{} < {} >".format(group_label, joined_subset_names) + "{} < {} >".format(group_label, joined_product_names) ) groups_information_lines.append( - "{}\n{}".format(group_label, line_join_subset_names) + "{}\n{}".format( + group_label, line_join_product_names + ) ) # Raise an error diff --git a/client/ayon_core/pipeline/create/product_name.py b/client/ayon_core/pipeline/create/product_name.py index ece8a68b2a..64a2760797 100644 --- a/client/ayon_core/pipeline/create/product_name.py +++ b/client/ayon_core/pipeline/create/product_name.py @@ -89,7 +89,7 @@ def get_product_name( default_template=None, dynamic_data=None, project_settings=None, - family_filter=None, + product_type_filter=None, ): """Calculate product name based on passed context and AYON settings. @@ -120,8 +120,9 @@ def get_product_name( a creator which creates instance. project_settings (Optional[Union[Dict[str, Any]]]): Prepared settings for project. Settings are queried if not passed. - family_filter (Optional[str]): Use different family for product template - filtering. Value of 'family' is used when not passed. + product_type_filter (Optional[str]): Use different product type for + product template filtering. Value of `product_type` is used when + not passed. Raises: TemplateFillError: If filled template contains placeholder key which @@ -137,7 +138,7 @@ def get_product_name( template = get_product_name_template( project_name, - family_filter or product_type, + product_type_filter or product_type, task_name, task_type, host_name, From ba47f6dda7c289d6c5f807322ac1337062b78b3b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 22 Feb 2024 09:59:34 +0100 Subject: [PATCH 227/573] traypublisher is using product name and type --- .../hosts/traypublisher/api/plugin.py | 59 ++++++----- .../plugins/create/create_colorspace_look.py | 8 +- .../plugins/create/create_editorial.py | 100 ++++++++---------- .../plugins/create/create_from_settings.py | 1 - .../plugins/create/create_movie_batch.py | 37 +++---- .../plugins/create/create_online.py | 22 ++-- .../help/validate_existing_version.xml | 2 +- .../publish/validate_existing_version.py | 8 +- .../plugins/publish/validate_filepaths.py | 6 +- .../plugins/publish/validate_online_file.py | 8 +- 10 files changed, 119 insertions(+), 132 deletions(-) diff --git a/client/ayon_core/hosts/traypublisher/api/plugin.py b/client/ayon_core/hosts/traypublisher/api/plugin.py index d3d7f80e89..be50383510 100644 --- a/client/ayon_core/hosts/traypublisher/api/plugin.py +++ b/client/ayon_core/hosts/traypublisher/api/plugin.py @@ -111,7 +111,7 @@ class SettingsCreator(TrayPublishCreator): extensions = [] - def create(self, subset_name, data, pre_create_data): + def create(self, product_name, data, pre_create_data): # Pass precreate data to creator attributes thumbnail_path = pre_create_data.pop(PRE_CREATE_THUMBNAIL_KEY, None) @@ -119,8 +119,8 @@ class SettingsCreator(TrayPublishCreator): if self.allow_version_control: asset_name = data["folderPath"] subset_docs_by_asset_id = self._prepare_next_versions( - [asset_name], [subset_name]) - version = subset_docs_by_asset_id[asset_name].get(subset_name) + [asset_name], [product_name]) + version = subset_docs_by_asset_id[asset_name].get(product_name) pre_create_data["version_to_use"] = version data["_previous_last_version"] = version @@ -128,38 +128,41 @@ class SettingsCreator(TrayPublishCreator): data["settings_creator"] = True # Create new instance - new_instance = CreatedInstance(self.family, subset_name, data, self) + new_instance = CreatedInstance( + self.product_type, product_name, data, self + ) self._store_new_instance(new_instance) if thumbnail_path: self.set_instance_thumbnail_path(new_instance.id, thumbnail_path) - def _prepare_next_versions(self, asset_names, subset_names): - """Prepare next versions for given asset and subset names. + def _prepare_next_versions(self, asset_names, product_names): + """Prepare next versions for given asset and product names. Todos: - Expect combination of subset names by asset name to avoid - unnecessary server calls for unused subsets. + Expect combination of product names by asset name to avoid + unnecessary server calls for unused products. Args: asset_names (Iterable[str]): Asset names. - subset_names (Iterable[str]): Subset names. + product_names (Iterable[str]): Subset names. Returns: dict[str, dict[str, int]]: Last versions by asset - and subset names. + and product names. """ # Prepare all versions for all combinations to '1' + # TODO use 'ayon_core.pipeline.version_start' logic subset_docs_by_asset_id = { asset_name: { - subset_name: 1 - for subset_name in subset_names + product_name: 1 + for product_name in product_names } for asset_name in asset_names } - if not asset_names or not subset_names: + if not asset_names or not product_names: return subset_docs_by_asset_id asset_docs = get_assets( @@ -174,26 +177,26 @@ class SettingsCreator(TrayPublishCreator): subset_docs = list(get_subsets( self.project_name, asset_ids=asset_names_by_id.keys(), - subset_names=subset_names, + subset_names=product_names, fields=["_id", "name", "parent"] )) - subset_ids = {subset_doc["_id"] for subset_doc in subset_docs} + product_ids = {subset_doc["_id"] for subset_doc in subset_docs} last_versions = get_last_versions( self.project_name, - subset_ids, + product_ids, fields=["name", "parent"]) for subset_doc in subset_docs: asset_id = subset_doc["parent"] asset_name = asset_names_by_id[asset_id] - subset_name = subset_doc["name"] - subset_id = subset_doc["_id"] - last_version = last_versions.get(subset_id) + product_name = subset_doc["name"] + product_id = subset_doc["_id"] + last_version = last_versions.get(product_id) version = 0 if last_version is not None: version = last_version["name"] - subset_docs_by_asset_id[asset_name][subset_name] += version + subset_docs_by_asset_id[asset_name][product_name] += version return subset_docs_by_asset_id def _fill_next_versions(self, instances_data): @@ -223,16 +226,16 @@ class SettingsCreator(TrayPublishCreator): instance["folderPath"] for instance in filtered_instance_data } - subset_names = { - instance["subset"] + product_names = { + instance["productName"] for instance in filtered_instance_data} subset_docs_by_asset_id = self._prepare_next_versions( - asset_names, subset_names + asset_names, product_names ) for instance in filtered_instance_data: asset_name = instance["folderPath"] - subset_name = instance["subset"] - version = subset_docs_by_asset_id[asset_name][subset_name] + product_name = instance["productName"] + version = subset_docs_by_asset_id[asset_name][product_name] instance["creator_attributes"]["version_to_use"] = version instance["_previous_last_version"] = version @@ -311,14 +314,14 @@ class SettingsCreator(TrayPublishCreator): @classmethod def from_settings(cls, item_data): identifier = item_data["identifier"] - family = item_data["product_type"] + product_type = item_data["product_type"] if not identifier: - identifier = "settings_{}".format(family) + identifier = "settings_{}".format(product_type) return type( "{}{}".format(cls.__name__, identifier), (cls, ), { - "family": family, + "product_type": product_type, "identifier": identifier, "label": item_data["label"].strip(), "icon": item_data["icon"], diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py index 3969294f1e..b1b50e902a 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py @@ -23,7 +23,7 @@ class CreateColorspaceLook(TrayPublishCreator): identifier = "io.openpype.creators.traypublisher.colorspace_look" label = "Colorspace Look" - family = "ociolook" + product_type = "ociolook" description = "Publishes color space look file." extensions = [".cc", ".cube", ".3dl", ".spi1d", ".spi3d", ".csp", ".lut"] enabled = False @@ -44,7 +44,7 @@ This creator publishes color space look file (LUT). def get_icon(self): return "mdi.format-color-fill" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): repr_file = pre_create_data.get("luts_file") if not repr_file: raise CreatorError("No files specified") @@ -58,7 +58,7 @@ This creator publishes color space look file (LUT). asset_doc = get_asset_by_name( self.project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( variant=instance_data["variant"], task_name=instance_data["task"] or "Not set", project_name=self.project_name, @@ -71,7 +71,7 @@ This creator publishes color space look file (LUT). } # Create new instance - new_instance = CreatedInstance(self.family, subset_name, + new_instance = CreatedInstance(self.product_type, product_name, instance_data, self) new_instance.transient_data["config_items"] = self.config_items new_instance.transient_data["config_data"] = self.config_data diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py index d6501e65a2..a7abd3e6db 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py @@ -60,19 +60,15 @@ CLIP_ATTR_DEFS = [ class EditorialClipInstanceCreatorBase(HiddenTrayPublishCreator): - """ Wrapper class for clip family creators - - Args: - HiddenTrayPublishCreator (BaseCreator): hidden supporting class - """ + """Wrapper class for clip product type creators.""" host_name = "traypublisher" def create(self, instance_data, source_data=None): - subset_name = instance_data["subset"] + product_name = instance_data["productName"] # Create new instance new_instance = CreatedInstance( - self.family, subset_name, instance_data, self + self.product_type, product_name, instance_data, self ) self._store_new_instance(new_instance) @@ -90,15 +86,12 @@ class EditorialClipInstanceCreatorBase(HiddenTrayPublishCreator): class EditorialShotInstanceCreator(EditorialClipInstanceCreatorBase): - """ Shot family class + """Shot product type class The shot metadata instance carrier. - - Args: - EditorialClipInstanceCreatorBase (BaseCreator): hidden supporting class """ identifier = "editorial_shot" - family = "shot" + product_type = "shot" label = "Editorial Shot" def get_instance_attr_defs(self): @@ -113,57 +106,48 @@ class EditorialShotInstanceCreator(EditorialClipInstanceCreatorBase): class EditorialPlateInstanceCreator(EditorialClipInstanceCreatorBase): - """ Plate family class + """Plate product type class Plate representation instance. - - Args: - EditorialClipInstanceCreatorBase (BaseCreator): hidden supporting class """ identifier = "editorial_plate" - family = "plate" + product_type = "plate" label = "Editorial Plate" class EditorialAudioInstanceCreator(EditorialClipInstanceCreatorBase): - """ Audio family class + """Audio product type class Audio representation instance. - - Args: - EditorialClipInstanceCreatorBase (BaseCreator): hidden supporting class """ identifier = "editorial_audio" - family = "audio" + product_type = "audio" label = "Editorial Audio" class EditorialReviewInstanceCreator(EditorialClipInstanceCreatorBase): - """ Review family class + """Review product type class Review representation instance. - - Args: - EditorialClipInstanceCreatorBase (BaseCreator): hidden supporting class """ identifier = "editorial_review" - family = "review" + product_type = "review" label = "Editorial Review" class EditorialSimpleCreator(TrayPublishCreator): - """ Editorial creator class + """Editorial creator class Simple workflow creator. This creator only disecting input video file into clip chunks and then converts each to - defined format defined Settings for each subset preset. + defined format defined Settings for each product preset. Args: TrayPublishCreator (Creator): Tray publisher plugin class """ label = "Editorial Simple" - family = "editorial" + product_type = "editorial" identifier = "editorial_simple" default_variants = [ "main" @@ -197,7 +181,7 @@ or updating already created. Publishing will create OTIO file. if default_variants: self.default_variants = default_variants - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): allowed_product_type_presets = self._get_allowed_product_type_presets( pre_create_data) @@ -262,7 +246,7 @@ or updating already created. Publishing will create OTIO file. # create otio editorial instance self._create_otio_instance( - subset_name, + product_name, instance_data, seq_path, media_path, first_otio_timeline @@ -270,7 +254,7 @@ or updating already created. Publishing will create OTIO file. def _create_otio_instance( self, - subset_name, + product_name, data, sequence_path, media_path, @@ -279,7 +263,7 @@ or updating already created. Publishing will create OTIO file. """Otio instance creating function Args: - subset_name (str): name of subset + product_name (str): Product name. data (dict): instance data sequence_path (str): path to sequence file media_path (str): path to media file @@ -292,7 +276,7 @@ or updating already created. Publishing will create OTIO file. "otioTimeline": otio.adapters.write_to_string(otio_timeline) }) new_instance = CreatedInstance( - self.family, subset_name, data, self + self.product_type, product_name, data, self ) self._store_new_instance(new_instance) @@ -361,7 +345,7 @@ or updating already created. Publishing will create OTIO file. otio_timeline (otio.Timeline): otio timeline object media_path (str): media file path string instance_data (dict): clip instance data - product_type_presets (list): list of dict settings subset presets + product_type_presets (list): list of dict settings product presets """ tracks = [ @@ -408,14 +392,14 @@ or updating already created. Publishing will create OTIO file. } for product_type_preset in product_type_presets: - # exclude audio family if no audio stream + # exclude audio product type if no audio stream if ( product_type_preset["product_type"] == "audio" and not media_data.get("audio") ): continue - instance = self._make_subset_instance( + instance = self._make_product_instance( otio_clip, product_type_preset, deepcopy(base_instance_data), @@ -526,33 +510,33 @@ or updating already created. Publishing will create OTIO file. return return_data - def _make_subset_instance( + def _make_product_instance( self, otio_clip, product_type_preset, instance_data, parenting_data ): - """Making subset instance from input preset + """Making product instance from input preset Args: otio_clip (otio.Clip): otio clip object - product_type_preset (dict): single family preset + product_type_preset (dict): single product type preset instance_data (dict): instance data parenting_data (dict): shot instance parent data Returns: CreatedInstance: creator instance object """ - family = product_type_preset["product_type"] - label = self._make_subset_naming( + product_type = product_type_preset["product_type"] + label = self._make_product_naming( product_type_preset, instance_data ) instance_data["label"] = label - # add file extension filter only if it is not shot family - if family == "shot": + # add file extension filter only if it is not shot product type + if product_type == "shot": instance_data["otioClip"] = ( otio.adapters.write_to_string(otio_clip)) c_instance = self.create_context.creators[ @@ -573,7 +557,7 @@ or updating already created. Publishing will create OTIO file. } }) - creator_identifier = f"editorial_{family}" + creator_identifier = f"editorial_{product_type}" editorial_clip_creator = self.create_context.creators[ creator_identifier] c_instance = editorial_clip_creator.create( @@ -581,8 +565,8 @@ or updating already created. Publishing will create OTIO file. return c_instance - def _make_subset_naming(self, product_type_preset, instance_data): - """ Subset name maker + def _make_product_naming(self, product_type_preset, instance_data): + """Subset name maker Args: product_type_preset (dict): single preset item @@ -594,25 +578,25 @@ or updating already created. Publishing will create OTIO file. asset_name = instance_data["creator_attributes"]["folderPath"] variant_name = instance_data["variant"] - family = product_type_preset["product_type"] + product_type = product_type_preset["product_type"] # get variant name from preset or from inheritance _variant_name = product_type_preset.get("variant") or variant_name - # subset name - subset_name = "{}{}".format( - family, _variant_name.capitalize() + # product name + product_name = "{}{}".format( + product_type, _variant_name.capitalize() ) label = "{} {}".format( asset_name, - subset_name + product_name ) instance_data.update({ - "family": family, "label": label, "variant": _variant_name, - "subset": subset_name, + "productType": product_type, + "productName": product_name, }) return label @@ -623,7 +607,7 @@ or updating already created. Publishing will create OTIO file. instance_data, track_start_frame, ): - """ Factoring basic set of instance data. + """Factoring basic set of instance data. Args: otio_clip (otio.Clip): otio clip object @@ -756,7 +740,7 @@ or updating already created. Publishing will create OTIO file. } def _get_allowed_product_type_presets(self, pre_create_data): - """ Filter out allowed family presets. + """Filter out allowed product type presets. Args: pre_create_data (dict): precreate attributes inputs @@ -802,7 +786,7 @@ or updating already created. Publishing will create OTIO file. return True def get_pre_create_attr_defs(self): - """ Creating pre-create attributes at creator plugin. + """Creating pre-create attributes at creator plugin. Returns: list: list of attribute object instances diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_from_settings.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_from_settings.py index cc1429901d..fe7ba4c4a4 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_from_settings.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_from_settings.py @@ -15,7 +15,6 @@ def initialize(): global_variables = globals() for item in simple_creators: - dynamic_plugin = SettingsCreator.from_settings(item) global_variables[dynamic_plugin.__name__] = dynamic_plugin diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_movie_batch.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_movie_batch.py index 274495855b..e3e64203b9 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_movie_batch.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_movie_batch.py @@ -11,7 +11,7 @@ from ayon_core.pipeline import ( CreatedInstance, ) from ayon_core.pipeline.create import ( - get_subset_name, + get_product_name, TaskNotSetError, ) @@ -29,7 +29,7 @@ class BatchMovieCreator(TrayPublishCreator): """ identifier = "render_movie_batch" label = "Batch Movies" - family = "render" + product_type = "render" description = "Publish batch of video files" create_allow_context_change = False @@ -48,7 +48,7 @@ class BatchMovieCreator(TrayPublishCreator): def get_icon(self): return "fa.file" - def create(self, subset_name, data, pre_create_data): + def create(self, product_name, data, pre_create_data): file_paths = pre_create_data.get("filepath") if not file_paths: return @@ -62,7 +62,7 @@ class BatchMovieCreator(TrayPublishCreator): asset_doc, version = get_asset_doc_from_file_name( file_name, self.project_name, self.version_regex) - subset_name, task_name = self._get_subset_and_task( + product_name, task_name = self._get_product_and_task( asset_doc, data["variant"], self.project_name) asset_name = get_asset_name_identifier(asset_doc) @@ -71,21 +71,22 @@ class BatchMovieCreator(TrayPublishCreator): instance_data["task"] = task_name # Create new instance - new_instance = CreatedInstance(self.family, subset_name, + new_instance = CreatedInstance(self.product_type, product_name, instance_data, self) self._store_new_instance(new_instance) - def _get_subset_and_task(self, asset_doc, variant, project_name): - """Create subset name according to standard template process""" + def _get_product_and_task(self, asset_doc, variant, project_name): + """Create product name according to standard template process""" task_name = self._get_task_name(asset_doc) try: - subset_name = get_subset_name( - self.family, - variant, - task_name, + product_name = get_product_name( + project_name, asset_doc, - project_name + task_name, + self.create_context.host_name, + self.product_type, + variant, ) except TaskNotSetError: # Create instance with fake task @@ -93,15 +94,15 @@ class BatchMovieCreator(TrayPublishCreator): # but user have ability to change it # NOTE: This expect that there is not task 'Undefined' on asset task_name = "Undefined" - subset_name = get_subset_name( - self.family, - variant, - task_name, + product_name = get_product_name( + project_name, asset_doc, - project_name + task_name, + self.product_type, + variant, ) - return subset_name, task_name + return product_name, task_name def _get_task_name(self, asset_doc): """Get applicable task from 'asset_doc' """ diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_online.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_online.py index 36d2fba976..1003a3bfb7 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_online.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_online.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- """Creator of online files. -Online file retain their original name and use it as subset name. To -avoid conflicts, this creator checks if subset with this name already +Online file retain their original name and use it as product name. To +avoid conflicts, this creator checks if product with this name already exists under selected asset. """ from pathlib import Path @@ -21,7 +21,7 @@ class OnlineCreator(TrayPublishCreator): identifier = "io.openpype.creators.traypublisher.online" label = "Online" - family = "online" + product_type = "online" description = "Publish file retaining its original file name" extensions = [".mov", ".mp4", ".mxf", ".m4v", ".mpg", ".exr", ".dpx", ".tif", ".png", ".jpg"] @@ -30,7 +30,7 @@ class OnlineCreator(TrayPublishCreator): return """# Create file retaining its original file name. This will publish files using template helping to retain original - file name and that file name is used as subset name. + file name and that file name is used as product name. Bz default it tries to guard against multiple publishes of the same file.""" @@ -38,7 +38,7 @@ class OnlineCreator(TrayPublishCreator): def get_icon(self): return "fa.file" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): repr_file = pre_create_data.get("representation_file") if not repr_file: raise CreatorError("No files specified") @@ -50,7 +50,7 @@ class OnlineCreator(TrayPublishCreator): origin_basename = Path(files[0]).stem - # disable check for existing subset with the same name + # disable check for existing product with the same name """ asset = get_asset_by_name( self.project_name, instance_data["folderPath"], fields=["_id"]) @@ -58,19 +58,19 @@ class OnlineCreator(TrayPublishCreator): if get_subset_by_name( self.project_name, origin_basename, asset["_id"], fields=["_id"]): - raise CreatorError(f"subset with {origin_basename} already " + raise CreatorError(f"product with {origin_basename} already " "exists in selected asset") """ instance_data["originalBasename"] = origin_basename - subset_name = origin_basename + product_name = origin_basename instance_data["creator_attributes"] = { "path": (Path(repr_file["directory"]) / files[0]).as_posix() } # Create new instance - new_instance = CreatedInstance(self.family, subset_name, + new_instance = CreatedInstance(self.product_type, product_name, instance_data, self) self._store_new_instance(new_instance) @@ -100,7 +100,7 @@ class OnlineCreator(TrayPublishCreator): ) ] - def get_subset_name( + def get_product_name( self, variant, task_name, @@ -112,4 +112,4 @@ class OnlineCreator(TrayPublishCreator): if instance is None: return "{originalBasename}" - return instance.data["subset"] + return instance.data["productName"] diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/help/validate_existing_version.xml b/client/ayon_core/hosts/traypublisher/plugins/publish/help/validate_existing_version.xml index 8a3b8f4d7d..726ccdffe3 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/help/validate_existing_version.xml +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/help/validate_existing_version.xml @@ -5,7 +5,7 @@ ## Version already exists -Version {version} you have set on instance '{subset_name}' under '{asset_name}' already exists. This validation is enabled by default to prevent accidental override of existing versions. +Version {version} you have set on instance '{product_name}' under '{asset_name}' already exists. This validation is enabled by default to prevent accidental override of existing versions. ### How to repair? - Click on 'Repair' action -> this will change version to next available. diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_existing_version.py b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_existing_version.py index b75ae674e8..ddfe8904fa 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_existing_version.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_existing_version.py @@ -34,12 +34,12 @@ class ValidateExistingVersion( if last_version is None or last_version < version: return - subset_name = instance.data["subset"] - msg = "Version {} already exists for subset {}.".format( - version, subset_name) + product_name = instance.data["productName"] + msg = "Version {} already exists for product {}.".format( + version, product_name) formatting_data = { - "subset_name": subset_name, + "product_name": product_name, "asset_name": instance.data["folderPath"], "version": version } diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_filepaths.py b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_filepaths.py index 4a4f3dae69..c673b1977b 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_filepaths.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_filepaths.py @@ -28,14 +28,14 @@ class ValidateFilePath(pyblish.api.InstancePlugin): )) return - family = instance.data["family"] + product_type = instance.data["productType"] label = instance.data["name"] filepaths = instance.data["sourceFilepaths"] if not filepaths: raise PublishValidationError( ( "Source filepaths of '{}' instance \"{}\" are not filled" - ).format(family, label), + ).format(product_type, label), "File not filled", ( "## Files were not filled" @@ -59,7 +59,7 @@ class ValidateFilePath(pyblish.api.InstancePlugin): raise PublishValidationError( ( "Filepath of '{}' instance \"{}\" does not exist:\n{}" - ).format(family, label, joined_paths), + ).format(product_type, label, joined_paths), "File not found", ( "## Files were not found\nFiles\n{}" diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_online_file.py b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_online_file.py index e655578095..3bd55342af 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_online_file.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_online_file.py @@ -11,7 +11,7 @@ from ayon_core.client import get_subset_by_name class ValidateOnlineFile(OptionalPyblishPluginMixin, pyblish.api.InstancePlugin): - """Validate that subset doesn't exist yet.""" + """Validate that product doesn't exist yet.""" label = "Validate Existing Online Files" hosts = ["traypublisher"] families = ["online"] @@ -24,10 +24,10 @@ class ValidateOnlineFile(OptionalPyblishPluginMixin, return project_name = instance.context.data["projectName"] asset_id = instance.data["assetEntity"]["_id"] - subset = get_subset_by_name( - project_name, instance.data["subset"], asset_id) + subset_doc = get_subset_by_name( + project_name, instance.data["productName"], asset_id) - if subset: + if subset_doc: raise PublishValidationError( "Subset to be published already exists.", title=self.label From f9960370047347a37cdef6673af4c22d3179c011 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 22 Feb 2024 10:03:36 +0100 Subject: [PATCH 228/573] substance painter is using product name and type --- .../plugins/create/create_textures.py | 12 +++--- .../plugins/create/create_workfile.py | 16 ++++---- .../publish/collect_textureset_images.py | 37 ++++++++++--------- 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/client/ayon_core/hosts/substancepainter/plugins/create/create_textures.py b/client/ayon_core/hosts/substancepainter/plugins/create/create_textures.py index 831ab6bb23..f204ff7728 100644 --- a/client/ayon_core/hosts/substancepainter/plugins/create/create_textures.py +++ b/client/ayon_core/hosts/substancepainter/plugins/create/create_textures.py @@ -24,12 +24,12 @@ class CreateTextures(Creator): """Create a texture set.""" identifier = "io.openpype.creators.substancepainter.textureset" label = "Textures" - family = "textureSet" + product_type = "textureSet" icon = "picture-o" default_variant = "Main" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): if not substance_painter.project.is_open(): raise CreatorError("Can't create a Texture Set instance without " @@ -47,7 +47,7 @@ class CreateTextures(Creator): if key in pre_create_data: creator_attributes[key] = pre_create_data[key] - instance = self.create_instance_in_context(subset_name, + instance = self.create_instance_in_context(product_name, instance_data) set_instance( instance_id=instance["instance_id"], @@ -57,7 +57,7 @@ class CreateTextures(Creator): def collect_instances(self): for instance in get_instances(): if (instance.get("creator_identifier") == self.identifier or - instance.get("family") == self.family): + instance.get("productType") == self.product_type): self.create_instance_in_context_from_existing(instance) def update_instances(self, update_list): @@ -75,9 +75,9 @@ class CreateTextures(Creator): self._remove_instance_from_context(instance) # Helper methods (this might get moved into Creator class) - def create_instance_in_context(self, subset_name, data): + def create_instance_in_context(self, product_name, data): instance = CreatedInstance( - self.family, subset_name, data, self + self.product_type, product_name, data, self ) self.create_context.creator_adds_instance(instance) return instance diff --git a/client/ayon_core/hosts/substancepainter/plugins/create/create_workfile.py b/client/ayon_core/hosts/substancepainter/plugins/create/create_workfile.py index a51b7d859b..209bd87d13 100644 --- a/client/ayon_core/hosts/substancepainter/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/substancepainter/plugins/create/create_workfile.py @@ -17,7 +17,7 @@ class CreateWorkfile(AutoCreator): """Workfile auto-creator.""" identifier = "io.openpype.creators.substancepainter.workfile" label = "Workfile" - family = "workfile" + product_type = "workfile" icon = "document" default_variant = "Main" @@ -49,7 +49,7 @@ class CreateWorkfile(AutoCreator): if current_instance is None: self.log.info("Auto-creating workfile instance...") asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( variant, task_name, asset_doc, project_name, host_name ) data = { @@ -57,7 +57,7 @@ class CreateWorkfile(AutoCreator): "task": task_name, "variant": variant } - current_instance = self.create_instance_in_context(subset_name, + current_instance = self.create_instance_in_context(product_name, data) elif ( current_instance_asset != asset_name @@ -65,12 +65,12 @@ class CreateWorkfile(AutoCreator): ): # Update instance context if is not the same asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( variant, task_name, asset_doc, project_name, host_name ) current_instance["folderPath"] = asset_name current_instance["task"] = task_name - current_instance["subset"] = subset_name + current_instance["productName"] = product_name set_instance( instance_id=current_instance.get("instance_id"), @@ -80,7 +80,7 @@ class CreateWorkfile(AutoCreator): def collect_instances(self): for instance in get_instances(): if (instance.get("creator_identifier") == self.identifier or - instance.get("family") == self.family): + instance.get("productType") == self.product_type): self.create_instance_in_context_from_existing(instance) def update_instances(self, update_list): @@ -93,9 +93,9 @@ class CreateWorkfile(AutoCreator): set_instances(instance_data_by_id, update=True) # Helper methods (this might get moved into Creator class) - def create_instance_in_context(self, subset_name, data): + def create_instance_in_context(self, product_name, data): instance = CreatedInstance( - self.family, subset_name, data, self + self.product_type, product_name, data, self ) self.create_context.creator_adds_instance(instance) return instance diff --git a/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py b/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py index 03e17192d2..d977ccff68 100644 --- a/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py +++ b/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py @@ -9,7 +9,7 @@ from ayon_core.hosts.substancepainter.api.lib import ( get_parsed_export_maps, strip_template ) -from ayon_core.pipeline.create import get_subset_name +from ayon_core.pipeline.create import get_product_name from ayon_core.client import get_asset_by_name @@ -35,7 +35,7 @@ class CollectTextureSet(pyblish.api.InstancePlugin): maps = get_parsed_export_maps(config) # Let's break the instance into multiple instances to integrate - # a subset per generated texture or texture UDIM sequence + # a product per generated texture or texture UDIM sequence for (texture_set_name, stack_name), template_maps in maps.items(): self.log.info(f"Processing {texture_set_name}/{stack_name}") for template, outputs in template_maps.items(): @@ -49,7 +49,7 @@ class CollectTextureSet(pyblish.api.InstancePlugin): asset_doc, texture_set_name, stack_name): """Create a new instance per image or UDIM sequence. - The new instances will be of family `image`. + The new instances will be of product type `image`. """ @@ -66,7 +66,7 @@ class CollectTextureSet(pyblish.api.InstancePlugin): ) # Define the suffix we want to give this particular texture - # set and set up a remapped subset naming for it. + # set and set up a remapped product naming for it. suffix = "" if always_include_texture_set_name or len(all_texture_sets) > 1: # More than one texture set, include texture set name @@ -79,15 +79,16 @@ class CollectTextureSet(pyblish.api.InstancePlugin): map_identifier = strip_template(template) suffix += f".{map_identifier}" - image_subset = get_subset_name( - # TODO: The family actually isn't 'texture' currently but for now - # this is only done so the subset name starts with 'texture' - family="texture", - variant=instance.data["variant"] + suffix, - task_name=instance.data.get("task"), - asset_doc=asset_doc, + image_product_name = get_product_name( + # TODO: The product type actually isn't 'texture' currently but + # for now this is only done so the product name starts with + # 'texture' project_name=context.data["projectName"], + asset_doc=asset_doc, + task_name=instance.data.get("task"), host_name=context.data["hostName"], + product_type="texture", + variant=instance.data["variant"] + suffix, project_settings=context.data["project_settings"] ) @@ -112,18 +113,18 @@ class CollectTextureSet(pyblish.api.InstancePlugin): representation["stagingDir"] = staging_dir # Clone the instance - image_instance = context.create_instance(image_subset) + image_instance = context.create_instance(image_product_name) image_instance[:] = instance[:] image_instance.data.update(copy.deepcopy(dict(instance.data))) - image_instance.data["name"] = image_subset - image_instance.data["label"] = image_subset - image_instance.data["subset"] = image_subset - image_instance.data["family"] = "image" + image_instance.data["name"] = image_product_name + image_instance.data["label"] = image_product_name + image_instance.data["productName"] = image_product_name + image_instance.data["productType"] = "image" image_instance.data["families"] = ["image", "textures"] image_instance.data["representations"] = [representation] # Group the textures together in the loader - image_instance.data["subsetGroup"] = instance.data["subset"] + image_instance.data["subsetGroup"] = image_product_name # Store the texture set name and stack name on the instance image_instance.data["textureSetName"] = texture_set_name @@ -133,7 +134,7 @@ class CollectTextureSet(pyblish.api.InstancePlugin): # Note: The extractor will assign it to the representation colorspace = outputs[0].get("colorSpace") if colorspace: - self.log.debug(f"{image_subset} colorspace: {colorspace}") + self.log.debug(f"{image_product_name} colorspace: {colorspace}") image_instance.data["colorspace"] = colorspace # Store the instance in the original instance as a member From 6b18915e9ff63cdb1215cc65650cdcec7c50f284 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 22 Feb 2024 17:11:48 +0800 Subject: [PATCH 229/573] update loader scripts --- client/ayon_core/hosts/max/api/pipeline.py | 5 +++-- .../ayon_core/hosts/max/plugins/load/load_camera_fbx.py | 7 ++++--- client/ayon_core/hosts/max/plugins/load/load_max_scene.py | 6 +++--- client/ayon_core/hosts/max/plugins/load/load_model.py | 7 ++++--- client/ayon_core/hosts/max/plugins/load/load_model_fbx.py | 6 +++--- client/ayon_core/hosts/max/plugins/load/load_model_obj.py | 6 +++--- client/ayon_core/hosts/max/plugins/load/load_model_usd.py | 6 ++++-- .../ayon_core/hosts/max/plugins/load/load_pointcache.py | 7 ++++--- .../hosts/max/plugins/load/load_pointcache_ornatrix.py | 6 ++++-- .../ayon_core/hosts/max/plugins/load/load_pointcloud.py | 6 +++--- .../hosts/max/plugins/load/load_redshift_proxy.py | 8 ++++---- client/ayon_core/hosts/max/plugins/load/load_tycache.py | 6 +++--- 12 files changed, 42 insertions(+), 34 deletions(-) diff --git a/client/ayon_core/hosts/max/api/pipeline.py b/client/ayon_core/hosts/max/api/pipeline.py index 13b7f867f2..b7cec7e0ca 100644 --- a/client/ayon_core/hosts/max/api/pipeline.py +++ b/client/ayon_core/hosts/max/api/pipeline.py @@ -260,8 +260,9 @@ def remove_container_data(container_node: str): # clean up the children of alembic dummy objects for current_set_member in all_set_members_names: shape_list = [members for members in current_set_member.Children - if rt.ClassOf(members) == rt.AlembicObject] - if shape_list: + if rt.ClassOf(members) == rt.AlembicObject + or rt.isValidNode(members)] + if shape_list: # noqa rt.Delete(shape_list) rt.Delete(current_set_member) rt.deleteModifier(container_node, container_node.modifiers[0]) diff --git a/client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py b/client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py index 34b120c179..8387d7a837 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py +++ b/client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py @@ -1,6 +1,6 @@ import os -from ayon_core.hosts.max.api import lib, maintained_selection +from ayon_core.hosts.max.api import lib from ayon_core.hosts.max.api.lib import ( unique_namespace, get_namespace, @@ -9,7 +9,8 @@ from ayon_core.hosts.max.api.lib import ( from ayon_core.hosts.max.api.pipeline import ( containerise, get_previous_loaded_object, - update_custom_attribute_data + update_custom_attribute_data, + remove_container_data ) from ayon_core.pipeline import get_representation_path, load @@ -96,4 +97,4 @@ class FbxLoader(load.LoaderPlugin): from pymxs import runtime as rt node = rt.GetNodeByName(container["instance_node"]) - rt.Delete(node) + remove_container_data(node) diff --git a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py index 7267d7a59e..ead77cd2f2 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py +++ b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py @@ -8,7 +8,8 @@ from ayon_core.hosts.max.api.lib import ( ) from ayon_core.hosts.max.api.pipeline import ( containerise, get_previous_loaded_object, - update_custom_attribute_data + update_custom_attribute_data, + remove_container_data ) from ayon_core.pipeline import get_representation_path, load @@ -93,6 +94,5 @@ class MaxSceneLoader(load.LoaderPlugin): def remove(self, container): from pymxs import runtime as rt - node = rt.GetNodeByName(container["instance_node"]) - rt.Delete(node) + remove_container_data(node) diff --git a/client/ayon_core/hosts/max/plugins/load/load_model.py b/client/ayon_core/hosts/max/plugins/load/load_model.py index 796e1b80ad..cf35e107c2 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_model.py +++ b/client/ayon_core/hosts/max/plugins/load/load_model.py @@ -2,7 +2,8 @@ import os from ayon_core.pipeline import load, get_representation_path from ayon_core.hosts.max.api.pipeline import ( containerise, - get_previous_loaded_object + get_previous_loaded_object, + remove_container_data ) from ayon_core.hosts.max.api import lib from ayon_core.hosts.max.api.lib import ( @@ -97,9 +98,9 @@ class ModelAbcLoader(load.LoaderPlugin): def remove(self, container): from pymxs import runtime as rt - node = rt.GetNodeByName(container["instance_node"]) - rt.Delete(node) + remove_container_data(node) + @staticmethod def get_container_children(parent, type_name): diff --git a/client/ayon_core/hosts/max/plugins/load/load_model_fbx.py b/client/ayon_core/hosts/max/plugins/load/load_model_fbx.py index 827cf63b39..c0bacca33a 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_model_fbx.py +++ b/client/ayon_core/hosts/max/plugins/load/load_model_fbx.py @@ -2,7 +2,8 @@ import os from ayon_core.pipeline import load, get_representation_path from ayon_core.hosts.max.api.pipeline import ( containerise, get_previous_loaded_object, - update_custom_attribute_data + update_custom_attribute_data, + remove_container_data ) from ayon_core.hosts.max.api import lib from ayon_core.hosts.max.api.lib import ( @@ -92,6 +93,5 @@ class FbxModelLoader(load.LoaderPlugin): def remove(self, container): from pymxs import runtime as rt - node = rt.GetNodeByName(container["instance_node"]) - rt.Delete(node) + remove_container_data(node) diff --git a/client/ayon_core/hosts/max/plugins/load/load_model_obj.py b/client/ayon_core/hosts/max/plugins/load/load_model_obj.py index 22d3d4b58a..1023b67f0c 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_model_obj.py +++ b/client/ayon_core/hosts/max/plugins/load/load_model_obj.py @@ -11,7 +11,8 @@ from ayon_core.hosts.max.api.lib import maintained_selection from ayon_core.hosts.max.api.pipeline import ( containerise, get_previous_loaded_object, - update_custom_attribute_data + update_custom_attribute_data, + remove_container_data ) from ayon_core.pipeline import get_representation_path, load @@ -84,6 +85,5 @@ class ObjLoader(load.LoaderPlugin): def remove(self, container): from pymxs import runtime as rt - node = rt.GetNodeByName(container["instance_node"]) - rt.Delete(node) + remove_container_data(node) diff --git a/client/ayon_core/hosts/max/plugins/load/load_model_usd.py b/client/ayon_core/hosts/max/plugins/load/load_model_usd.py index 8d42219217..0ec6e5e8e7 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_model_usd.py +++ b/client/ayon_core/hosts/max/plugins/load/load_model_usd.py @@ -13,7 +13,8 @@ from ayon_core.hosts.max.api.lib import maintained_selection from ayon_core.hosts.max.api.pipeline import ( containerise, get_previous_loaded_object, - update_custom_attribute_data + update_custom_attribute_data, + remove_container_data ) from ayon_core.pipeline import get_representation_path, load @@ -113,5 +114,6 @@ class ModelUSDLoader(load.LoaderPlugin): self.update(container, representation) def remove(self, container): + from pymxs import runtime as rt node = rt.GetNodeByName(container["instance_node"]) - rt.Delete(node) + remove_container_data(node) diff --git a/client/ayon_core/hosts/max/plugins/load/load_pointcache.py b/client/ayon_core/hosts/max/plugins/load/load_pointcache.py index a92fa66757..e9cde4c654 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_pointcache.py +++ b/client/ayon_core/hosts/max/plugins/load/load_pointcache.py @@ -10,7 +10,8 @@ from ayon_core.hosts.max.api import lib, maintained_selection from ayon_core.hosts.max.api.lib import unique_namespace from ayon_core.hosts.max.api.pipeline import ( containerise, - get_previous_loaded_object + get_previous_loaded_object, + remove_container_data ) @@ -103,9 +104,9 @@ class AbcLoader(load.LoaderPlugin): def remove(self, container): from pymxs import runtime as rt - node = rt.GetNodeByName(container["instance_node"]) - rt.Delete(node) + remove_container_data(node) + @staticmethod def get_container_children(parent, type_name): diff --git a/client/ayon_core/hosts/max/plugins/load/load_pointcache_ornatrix.py b/client/ayon_core/hosts/max/plugins/load/load_pointcache_ornatrix.py index 27b2e271d2..338cbfafb9 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_pointcache_ornatrix.py +++ b/client/ayon_core/hosts/max/plugins/load/load_pointcache_ornatrix.py @@ -4,7 +4,8 @@ from ayon_core.pipeline.load import LoadError from ayon_core.hosts.max.api.pipeline import ( containerise, get_previous_loaded_object, - update_custom_attribute_data + update_custom_attribute_data, + remove_container_data ) from ayon_core.hosts.max.api.lib import ( @@ -104,5 +105,6 @@ class OxAbcLoader(load.LoaderPlugin): self.update(container, representation) def remove(self, container): + from pymxs import runtime as rt node = rt.GetNodeByName(container["instance_node"]) - rt.Delete(node) + remove_container_data(node) diff --git a/client/ayon_core/hosts/max/plugins/load/load_pointcloud.py b/client/ayon_core/hosts/max/plugins/load/load_pointcloud.py index 45e3da5621..7f4fba50b3 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_pointcloud.py +++ b/client/ayon_core/hosts/max/plugins/load/load_pointcloud.py @@ -8,7 +8,8 @@ from ayon_core.hosts.max.api.lib import ( from ayon_core.hosts.max.api.pipeline import ( containerise, get_previous_loaded_object, - update_custom_attribute_data + update_custom_attribute_data, + remove_container_data ) from ayon_core.pipeline import get_representation_path, load @@ -63,6 +64,5 @@ class PointCloudLoader(load.LoaderPlugin): def remove(self, container): """remove the container""" from pymxs import runtime as rt - node = rt.GetNodeByName(container["instance_node"]) - rt.Delete(node) + remove_container_data(node) diff --git a/client/ayon_core/hosts/max/plugins/load/load_redshift_proxy.py b/client/ayon_core/hosts/max/plugins/load/load_redshift_proxy.py index 3f73210c24..5f2f5ec1ad 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_redshift_proxy.py +++ b/client/ayon_core/hosts/max/plugins/load/load_redshift_proxy.py @@ -9,7 +9,8 @@ from ayon_core.pipeline.load import LoadError from ayon_core.hosts.max.api.pipeline import ( containerise, update_custom_attribute_data, - get_previous_loaded_object + get_previous_loaded_object, + remove_container_data ) from ayon_core.hosts.max.api import lib from ayon_core.hosts.max.api.lib import ( @@ -72,6 +73,5 @@ class RedshiftProxyLoader(load.LoaderPlugin): def remove(self, container): from pymxs import runtime as rt - - node = rt.getNodeByName(container["instance_node"]) - rt.delete(node) + node = rt.GetNodeByName(container["instance_node"]) + remove_container_data(node) diff --git a/client/ayon_core/hosts/max/plugins/load/load_tycache.py b/client/ayon_core/hosts/max/plugins/load/load_tycache.py index 48fb5c447a..7ae1aea72c 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_tycache.py +++ b/client/ayon_core/hosts/max/plugins/load/load_tycache.py @@ -7,7 +7,8 @@ from ayon_core.hosts.max.api.lib import ( from ayon_core.hosts.max.api.pipeline import ( containerise, get_previous_loaded_object, - update_custom_attribute_data + update_custom_attribute_data, + remove_container_data ) from ayon_core.pipeline import get_representation_path, load @@ -59,6 +60,5 @@ class TyCacheLoader(load.LoaderPlugin): def remove(self, container): """remove the container""" from pymxs import runtime as rt - node = rt.GetNodeByName(container["instance_node"]) - rt.Delete(node) + remove_container_data(node) From e00a185469968cd270c294dc598869ecf30c0cfc Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 22 Feb 2024 17:33:46 +0800 Subject: [PATCH 230/573] make sure viewport gets refreshed after removing items in scene inventory --- client/ayon_core/hosts/max/api/pipeline.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/hosts/max/api/pipeline.py b/client/ayon_core/hosts/max/api/pipeline.py index b7cec7e0ca..1486f7218d 100644 --- a/client/ayon_core/hosts/max/api/pipeline.py +++ b/client/ayon_core/hosts/max/api/pipeline.py @@ -268,3 +268,4 @@ def remove_container_data(container_node: str): rt.deleteModifier(container_node, container_node.modifiers[0]) rt.Delete(container_node) + rt.redrawViews() From 99db531abe079a60904211b3368f5f16da15ece4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 22 Feb 2024 11:11:25 +0100 Subject: [PATCH 231/573] photoshop is using product type and name --- .../ayon_core/hosts/photoshop/api/README.md | 4 +- .../ayon_core/hosts/photoshop/api/plugin.py | 18 +- .../ayon_core/hosts/photoshop/api/ws_stub.py | 8 +- client/ayon_core/hosts/photoshop/lib.py | 35 ++-- .../plugins/create/create_flatten_image.py | 52 +++-- .../photoshop/plugins/create/create_image.py | 45 ++-- .../photoshop/plugins/create/create_review.py | 2 +- .../plugins/create/create_workfile.py | 2 +- .../plugins/publish/collect_auto_image.py | 25 ++- .../plugins/publish/collect_auto_review.py | 34 +-- .../plugins/publish/collect_auto_workfile.py | 28 +-- .../publish/collect_color_coded_instances.py | 198 ++++++++++-------- .../publish/collect_published_version.py | 16 +- .../plugins/publish/collect_workfile.py | 2 +- .../plugins/publish/extract_image.py | 2 +- .../plugins/publish/extract_review.py | 29 ++- .../plugins/publish/help/validate_naming.xml | 4 +- .../plugins/publish/validate_naming.py | 35 ++-- 18 files changed, 304 insertions(+), 235 deletions(-) diff --git a/client/ayon_core/hosts/photoshop/api/README.md b/client/ayon_core/hosts/photoshop/api/README.md index 02868753d1..51a9b9ad5e 100644 --- a/client/ayon_core/hosts/photoshop/api/README.md +++ b/client/ayon_core/hosts/photoshop/api/README.md @@ -55,7 +55,7 @@ class CreateImage(photoshop.Creator): name = "imageDefault" label = "Image" - family = "image" + product_type = "image" def __init__(self, *args, **kwargs): super(CreateImage, self).__init__(*args, **kwargs) @@ -114,7 +114,7 @@ class CollectInstances(pyblish.api.ContextPlugin): instance.append(layer) instance.data.update(layer_data) instance.data["families"] = self.families_mapping[ - layer_data["family"] + layer_data["productType"] ] instance.data["publish"] = layer.visible diff --git a/client/ayon_core/hosts/photoshop/api/plugin.py b/client/ayon_core/hosts/photoshop/api/plugin.py index 22645a1f9b..d4eb38300f 100644 --- a/client/ayon_core/hosts/photoshop/api/plugin.py +++ b/client/ayon_core/hosts/photoshop/api/plugin.py @@ -4,19 +4,21 @@ from ayon_core.pipeline import LoaderPlugin from .launch_logic import stub -def get_unique_layer_name(layers, asset_name, subset_name): - """ - Gets all layer names and if 'asset_name_subset_name' is present, it - increases suffix by 1 (eg. creates unique layer name - for Loader) +def get_unique_layer_name(layers, asset_name, product_name): + """Prepare unique layer name. + + Gets all layer names and if '_' is present, + it adds suffix '1', or increases the suffix by 1. + Args: layers (list) of dict with layers info (name, id etc.) - asset_name (string): - subset_name (string): + asset_name (str): + product_name (str): Returns: - (string): name_00X (without version) + str: name_00X (without version) """ - name = "{}_{}".format(asset_name, subset_name) + name = "{}_{}".format(asset_name, product_name) names = {} for layer in layers: layer_name = re.sub(r'_\d{3}$', '', layer.name) diff --git a/client/ayon_core/hosts/photoshop/api/ws_stub.py b/client/ayon_core/hosts/photoshop/api/ws_stub.py index 78cb5fac5d..36fe0af2f8 100644 --- a/client/ayon_core/hosts/photoshop/api/ws_stub.py +++ b/client/ayon_core/hosts/photoshop/api/ws_stub.py @@ -117,8 +117,8 @@ class PhotoshopServerStub: Stores metadata in format: [{ "active":true, - "subset":"imageBG", - "family":"image", + "productName":"imageBG", + "productType":"image", "id":"ayon.create.instance", "folderPath":"Town", "uuid": "8" @@ -419,8 +419,8 @@ class PhotoshopServerStub: Returns: (list) example: - {"8":{"active":true,"subset":"imageBG", - "family":"image","id":"ayon.create.instance", + {"8":{"active":true,"productName":"imageBG", + "productType":"image","id":"ayon.create.instance", "folderPath":"/Town"}} 8 is layer(group) id - used for deletion, update etc. """ diff --git a/client/ayon_core/hosts/photoshop/lib.py b/client/ayon_core/hosts/photoshop/lib.py index b905caf1bd..9f800300b9 100644 --- a/client/ayon_core/hosts/photoshop/lib.py +++ b/client/ayon_core/hosts/photoshop/lib.py @@ -34,7 +34,7 @@ class PSAutoCreator(AutoCreator): def create(self, options=None): existing_instance = None for instance in self.create_context.instances: - if instance.family == self.family: + if instance.product_type == self.product_type: existing_instance = instance break @@ -51,9 +51,13 @@ class PSAutoCreator(AutoCreator): if existing_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( - self.default_variant, task_name, asset_doc, - project_name, host_name + product_name = self.get_product_name( + project_name, + asset_doc, + task_name, + host_name, + self.product_type, + self.default_variant, ) data = { "folderPath": asset_name, @@ -69,7 +73,7 @@ class PSAutoCreator(AutoCreator): data["active"] = False new_instance = CreatedInstance( - self.family, subset_name, data, self + self.product_type, product_name, data, self ) self._add_instance_to_context(new_instance) api.stub().imprint(new_instance.get("instance_id"), @@ -80,24 +84,27 @@ class PSAutoCreator(AutoCreator): or existing_instance["task"] != task_name ): asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( self.default_variant, task_name, asset_doc, project_name, host_name ) existing_instance["folderPath"] = asset_name existing_instance["task"] = task_name - existing_instance["subset"] = subset_name + existing_instance["productName"] = product_name -def clean_subset_name(subset_name): - """Clean all variants leftover {layer} from subset name.""" +def clean_product_name(product_name): + """Clean all variants leftover {layer} from product name.""" dynamic_data = prepare_template_data({"layer": "{layer}"}) for value in dynamic_data.values(): - if value in subset_name: - subset_name = (subset_name.replace(value, "") - .replace("__", "_") - .replace("..", ".")) + if value in product_name: + product_name = ( + product_name + .replace(value, "") + .replace("__", "_") + .replace("..", ".") + ) # clean trailing separator as Main_ pattern = r'[\W_]+$' replacement = '' - return re.sub(pattern, replacement, subset_name) + return re.sub(pattern, replacement, product_name) diff --git a/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py b/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py index 666fd52f78..ad31463e46 100644 --- a/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py @@ -2,8 +2,8 @@ from ayon_core.pipeline import CreatedInstance from ayon_core.lib import BoolDef import ayon_core.hosts.photoshop.api as api -from ayon_core.hosts.photoshop.lib import PSAutoCreator, clean_subset_name -from ayon_core.pipeline.create import get_subset_name +from ayon_core.hosts.photoshop.lib import PSAutoCreator, clean_product_name +from ayon_core.pipeline.create import get_product_name from ayon_core.lib import prepare_template_data from ayon_core.client import get_asset_by_name @@ -12,10 +12,10 @@ class AutoImageCreator(PSAutoCreator): """Creates flatten image from all visible layers. Used in simplified publishing as auto created instance. - Must be enabled in Setting and template for subset name provided + Must be enabled in Setting and template for product name provided """ identifier = "auto_image" - family = "image" + product_type = "image" # Settings default_variant = "" @@ -43,9 +43,12 @@ class AutoImageCreator(PSAutoCreator): existing_instance_asset = existing_instance["folderPath"] if existing_instance is None: - subset_name = self.get_subset_name( - self.default_variant, task_name, asset_doc, - project_name, host_name + product_name = self.get_product_name( + project_name, + asset_doc, + task_name, + host_name, + self.default_variant, ) data = { @@ -60,7 +63,7 @@ class AutoImageCreator(PSAutoCreator): data.update({"creator_attributes": creator_attributes}) new_instance = CreatedInstance( - self.family, subset_name, data, self + self.product_type, product_name, data, self ) self._add_instance_to_context(new_instance) api.stub().imprint(new_instance.get("instance_id"), @@ -70,13 +73,13 @@ class AutoImageCreator(PSAutoCreator): existing_instance_asset != asset_name or existing_instance["task"] != task_name ): - subset_name = self.get_subset_name( + product_name = self.get_product_name( self.default_variant, task_name, asset_doc, project_name, host_name ) existing_instance["folderPath"] = asset_name existing_instance["task"] = task_name - existing_instance["subset"] = subset_name + existing_instance["productName"] = product_name api.stub().imprint(existing_instance.get("instance_id"), existing_instance.data_to_store()) @@ -119,18 +122,23 @@ class AutoImageCreator(PSAutoCreator): review for it though. """ - def get_subset_name( - self, - variant, - task_name, - asset_doc, - project_name, - host_name=None, - instance=None + def get_product_name( + self, + variant, + task_name, + asset_doc, + project_name, + host_name=None, + instance=None ): dynamic_data = prepare_template_data({"layer": "{layer}"}) - subset_name = get_subset_name( - self.family, variant, task_name, asset_doc, - project_name, host_name, dynamic_data=dynamic_data + product_name = get_product_name( + project_name, + asset_doc, + task_name, + self.product_type, + variant, + host_name, + dynamic_data=dynamic_data ) - return clean_subset_name(subset_name) + return clean_product_name(product_name) diff --git a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py b/client/ayon_core/hosts/photoshop/plugins/create/create_image.py index 2ef746fc94..5da275e10f 100644 --- a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/create/create_image.py @@ -10,7 +10,7 @@ from ayon_core.pipeline import ( from ayon_core.lib import prepare_template_data from ayon_core.pipeline.create import SUBSET_NAME_ALLOWED_SYMBOLS from ayon_core.hosts.photoshop.api.pipeline import cache_and_get_instances -from ayon_core.hosts.photoshop.lib import clean_subset_name +from ayon_core.hosts.photoshop.lib import clean_product_name class ImageCreator(Creator): @@ -21,7 +21,7 @@ class ImageCreator(Creator): """ identifier = "image" label = "Image" - family = "image" + product_type = "image" description = "Image creator" # Settings @@ -29,7 +29,7 @@ class ImageCreator(Creator): mark_for_review = False active_on_create = True - def create(self, subset_name_from_ui, data, pre_create_data): + def create(self, product_name_from_ui, data, pre_create_data): groups_to_create = [] top_layers_to_wrap = [] create_empty_group = False @@ -47,19 +47,19 @@ class ImageCreator(Creator): else: top_layers_to_wrap.append(selected_item) else: - group = stub.group_selected_layers(subset_name_from_ui) + group = stub.group_selected_layers(product_name_from_ui) groups_to_create.append(group) else: stub.select_layers(stub.get_layers()) try: - group = stub.group_selected_layers(subset_name_from_ui) + group = stub.group_selected_layers(product_name_from_ui) except: raise CreatorError("Cannot group locked Background layer!") groups_to_create.append(group) # create empty group if nothing selected if not groups_to_create and not top_layers_to_wrap: - group = stub.create_group(subset_name_from_ui) + group = stub.create_group(product_name_from_ui) groups_to_create.append(group) # wrap each top level layer into separate new group @@ -69,12 +69,12 @@ class ImageCreator(Creator): groups_to_create.append(group) layer_name = '' - # use artist chosen option OR force layer if more subsets are created + # use artist chosen option OR force layer if more products are created # to differentiate them use_layer_name = (pre_create_data.get("use_layer_name") or len(groups_to_create) > 1) for group in groups_to_create: - subset_name = subset_name_from_ui # reset to name from creator UI + product_name = product_name_from_ui # reset to name from creator UI layer_names_in_hierarchy = [] created_group_name = self._clean_highlights(stub, group.name) @@ -84,12 +84,12 @@ class ImageCreator(Creator): "", group.name ) - if "{layer}" not in subset_name.lower(): - subset_name += "{Layer}" + if "{layer}" not in product_name.lower(): + product_name += "{Layer}" layer_fill = prepare_template_data({"layer": layer_name}) - subset_name = subset_name.format(**layer_fill) - subset_name = clean_subset_name(subset_name) + product_name = product_name.format(**layer_fill) + product_name = clean_product_name(product_name) if group.long_name: for directory in group.long_name[::-1]: @@ -97,7 +97,7 @@ class ImageCreator(Creator): layer_names_in_hierarchy.append(name) data_update = { - "subset": subset_name, + "productName": product_name, "members": [str(group.id)], "layer_name": layer_name, "long_name": "_".join(layer_names_in_hierarchy) @@ -112,8 +112,9 @@ class ImageCreator(Creator): if not self.active_on_create: data["active"] = False - new_instance = CreatedInstance(self.family, subset_name, data, - self) + new_instance = CreatedInstance( + self.product_type, product_name, data, self + ) stub.imprint(new_instance.get("instance_id"), new_instance.data_to_store()) @@ -159,7 +160,7 @@ class ImageCreator(Creator): label="Create separate instance for each selected"), BoolDef("use_layer_name", default=False, - label="Use layer name in subset"), + label="Use layer name in product"), BoolDef( "mark_for_review", label="Create separate review", @@ -189,9 +190,9 @@ class ImageCreator(Creator): def get_detail_description(self): return """Creator for Image instances - Main publishable item in Photoshop will be of `image` family. Result of - this item (instance) is picture that could be loaded and used - in another DCCs (for example as single layer in composition in + Main publishable item in Photoshop will be of `image` product type. + Result of this item (instance) is picture that could be loaded and + used in another DCCs (for example as single layer in composition in AfterEffects, reference in Maya etc). There are couple of options what to publish: @@ -207,13 +208,13 @@ class ImageCreator(Creator): Use 'Create separate instance for each selected' to create separate images per selected layer (group of layers). - 'Use layer name in subset' will explicitly add layer name into subset - name. Position of this name is configurable in + 'Use layer name in product' will explicitly add layer name into + product name. Position of this name is configurable in `project_settings/global/tools/creator/product_name_profiles`. If layer placeholder ({layer}) is not used in `product_name_profiles` but layer name should be used (set explicitly in UI or implicitly if multiple images should be created), it is added in capitalized form - as a suffix to subset name. + as a suffix to product name. Each image could have its separate review created if necessary via `Create separate review` toggle. diff --git a/client/ayon_core/hosts/photoshop/plugins/create/create_review.py b/client/ayon_core/hosts/photoshop/plugins/create/create_review.py index 888b294248..229b736801 100644 --- a/client/ayon_core/hosts/photoshop/plugins/create/create_review.py +++ b/client/ayon_core/hosts/photoshop/plugins/create/create_review.py @@ -4,7 +4,7 @@ from ayon_core.hosts.photoshop.lib import PSAutoCreator class ReviewCreator(PSAutoCreator): """Creates review instance which might be disabled from publishing.""" identifier = "review" - family = "review" + product_type = "review" default_variant = "Main" diff --git a/client/ayon_core/hosts/photoshop/plugins/create/create_workfile.py b/client/ayon_core/hosts/photoshop/plugins/create/create_workfile.py index 3485027215..da0c9d1d12 100644 --- a/client/ayon_core/hosts/photoshop/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/photoshop/plugins/create/create_workfile.py @@ -3,7 +3,7 @@ from ayon_core.hosts.photoshop.lib import PSAutoCreator class WorkfileCreator(PSAutoCreator): identifier = "workfile" - family = "workfile" + product_type = "workfile" default_variant = "Main" diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image.py index 479d9139af..8e27539425 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image.py @@ -2,7 +2,7 @@ import pyblish.api from ayon_core.client import get_asset_name_identifier from ayon_core.hosts.photoshop import api as photoshop -from ayon_core.pipeline.create import get_subset_name +from ayon_core.pipeline.create import get_product_name class CollectAutoImage(pyblish.api.ContextPlugin): @@ -67,7 +67,7 @@ class CollectAutoImage(pyblish.api.ContextPlugin): # active might not be in legacy meta if layer_meta_data.get("active", True) and layer_item.visible: - instance_names.append(layer_meta_data["subset"]) + instance_names.append(layer_meta_data["productName"]) if len(instance_names) == 0: variants = proj_settings.get( @@ -75,25 +75,30 @@ class CollectAutoImage(pyblish.api.ContextPlugin): "create", {}).get( "CreateImage", {}).get( "default_variants", ['']) - family = "image" + product_type = "image" variant = context.data.get("variant") or variants[0] - subset_name = get_subset_name( - family, variant, task_name, asset_doc, - project_name, host_name + product_name = get_product_name( + project_name, + asset_doc, + task_name, + host_name, + product_type, + variant, ) - instance = context.create_instance(subset_name) - instance.data["family"] = family + instance = context.create_instance(product_name) instance.data["folderPath"] = folder_path - instance.data["subset"] = subset_name + instance.data["productType"] = product_type + instance.data["productName"] = product_name instance.data["ids"] = publishable_ids instance.data["publish"] = True instance.data["creator_identifier"] = "auto_image" + instance.data["families"] = [product_type] if auto_creator["mark_for_review"]: instance.data["creator_attributes"] = {"mark_for_review": True} - instance.data["families"] = ["review"] + instance.data["families"].append("review") self.log.info("auto image instance: {} ".format(instance.data)) diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_review.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_review.py index e31508e641..fc8246c269 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_review.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_review.py @@ -3,13 +3,13 @@ Requires: None Provides: - instance -> family ("review") + instance -> productType ("review") """ import pyblish.api from ayon_core.client import get_asset_name_identifier from ayon_core.hosts.photoshop import api as photoshop -from ayon_core.pipeline.create import get_subset_name +from ayon_core.pipeline.create import get_product_name class CollectAutoReview(pyblish.api.ContextPlugin): @@ -26,10 +26,10 @@ class CollectAutoReview(pyblish.api.ContextPlugin): publish = True def process(self, context): - family = "review" + product_type = "review" has_review = False for instance in context: - if instance.data["family"] == family: + if instance.data["productType"] == product_type: self.log.debug("Review instance found, won't create new") has_review = True @@ -44,7 +44,7 @@ class CollectAutoReview(pyblish.api.ContextPlugin): stub = photoshop.stub() stored_items = stub.get_layers_metadata() for item in stored_items: - if item.get("creator_identifier") == family: + if item.get("creator_identifier") == product_type: if not item.get("active"): self.log.debug("Review instance disabled") return @@ -69,23 +69,23 @@ class CollectAutoReview(pyblish.api.ContextPlugin): folder_path = get_asset_name_identifier(asset_doc) - subset_name = get_subset_name( - family, - variant, - task_name, - asset_doc, + product_name = get_product_name( project_name, - host_name=host_name, + asset_doc, + task_name, + host_name, + product_type, + variant, project_settings=proj_settings ) - instance = context.create_instance(subset_name) + instance = context.create_instance(product_name) instance.data.update({ - "subset": subset_name, - "label": subset_name, - "name": subset_name, - "family": family, - "families": [], + "label": product_name, + "name": product_name, + "productName": product_name, + "productType": product_type, + "families": [product_type], "representations": [], "folderPath": folder_path, "publish": self.publish diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_workfile.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_workfile.py index 12fc31a2f2..af9d6cdf75 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_workfile.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_workfile.py @@ -3,7 +3,7 @@ import pyblish.api from ayon_core.client import get_asset_name_identifier from ayon_core.hosts.photoshop import api as photoshop -from ayon_core.pipeline.create import get_subset_name +from ayon_core.pipeline.create import get_product_name class CollectAutoWorkfile(pyblish.api.ContextPlugin): @@ -16,7 +16,7 @@ class CollectAutoWorkfile(pyblish.api.ContextPlugin): targets = ["automated"] def process(self, context): - family = "workfile" + product_type = "workfile" file_path = context.data["currentFile"] _, ext = os.path.splitext(file_path) staging_dir = os.path.dirname(file_path) @@ -29,7 +29,7 @@ class CollectAutoWorkfile(pyblish.api.ContextPlugin): } for instance in context: - if instance.data["family"] == family: + if instance.data["productType"] == product_type: self.log.debug("Workfile instance found, won't create new") instance.data.update({ "label": base_name, @@ -47,7 +47,7 @@ class CollectAutoWorkfile(pyblish.api.ContextPlugin): stub = photoshop.stub() stored_items = stub.get_layers_metadata() for item in stored_items: - if item.get("creator_identifier") == family: + if item.get("creator_identifier") == product_type: if not item.get("active"): self.log.debug("Workfile instance disabled") return @@ -72,24 +72,24 @@ class CollectAutoWorkfile(pyblish.api.ContextPlugin): asset_doc = context.data["assetEntity"] folder_path = get_asset_name_identifier(asset_doc) - subset_name = get_subset_name( - family, - variant, - task_name, - asset_doc, + product_name = get_product_name( project_name, - host_name=host_name, + asset_doc, + task_name, + host_name, + product_type, + variant, project_settings=proj_settings ) # Create instance - instance = context.create_instance(subset_name) + instance = context.create_instance(product_name) instance.data.update({ - "subset": subset_name, "label": base_name, "name": base_name, - "family": family, - "families": [], + "productName": product_name, + "productType": product_type, + "families": [product_type], "representations": [], "folderPath": folder_path }) diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_color_coded_instances.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_color_coded_instances.py index 2d3a9f4848..ab38398b30 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_color_coded_instances.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_color_coded_instances.py @@ -13,17 +13,17 @@ class CollectColorCodedInstances(pyblish.api.ContextPlugin): Used in remote publishing when artists marks publishable layers by color- coding. Top level layers (group) must be marked by specific color to be - published as an instance of 'image' family. + published as an instance of 'image' product type. Can add group for all publishable layers to allow creation of flattened image. (Cannot contain special background layer as it cannot be grouped!) Based on value `create_flatten_image` from Settings: - - "yes": create flattened 'image' subset of all publishable layers + create - 'image' subset per publishable layer - - "only": create ONLY flattened 'image' subset of all publishable layers - - "no": do not create flattened 'image' subset at all, - only separate subsets per marked layer. + - "yes": create flattened 'image' product of all publishable layers + create + 'image' product per publishable layer + - "only": create ONLY flattened 'image' product of all publishable layers + - "no": do not create flattened 'image' product at all, + only separate products per marked layer. Identifier: id (str): "ayon.create.instance" @@ -36,9 +36,6 @@ class CollectColorCodedInstances(pyblish.api.ContextPlugin): # configurable by Settings color_code_mapping = [] - # TODO check if could be set globally, probably doesn't make sense when - # flattened template cannot - subset_template_name = "" create_flatten_image = "no" flatten_product_name_template = "" @@ -48,12 +45,16 @@ class CollectColorCodedInstances(pyblish.api.ContextPlugin): os.environ.get("AYON_PUBLISH_DATA") or os.environ.get("OPENPYPE_PUBLISH_DATA") ) - if (is_in_tests() and - (not batch_dir or not os.path.exists(batch_dir))): + if ( + is_in_tests() + and ( + not batch_dir or not os.path.exists(batch_dir) + ) + ): self.log.debug("Automatic testing, no batch data, skipping") return - existing_subset_names = self._get_existing_subset_names(context) + existing_product_names = self._get_existing_product_names(context) # from CollectBatchData asset_name = context.data["folderPath"] @@ -71,7 +72,7 @@ class CollectColorCodedInstances(pyblish.api.ContextPlugin): publishable_layers = [] created_instances = [] - family_from_settings = None + product_type_from_settings = None for layer in layers: self.log.debug("Layer:: {}".format(layer)) if layer.parents: @@ -82,42 +83,50 @@ class CollectColorCodedInstances(pyblish.api.ContextPlugin): self.log.debug("Not visible, skip") continue - resolved_family, resolved_subset_template = self._resolve_mapping( - layer + resolved_product_type, resolved_product_template = ( + self._resolve_mapping(layer) ) - if not resolved_subset_template or not resolved_family: - self.log.debug("!!! Not found family or template, skip") + if not resolved_product_template or not resolved_product_type: + self.log.debug("!!! Not found product type or template, skip") continue - if not family_from_settings: - family_from_settings = resolved_family + if not product_type_from_settings: + product_type_from_settings = resolved_product_type fill_pairs = { "variant": variant, - "family": resolved_family, + "family": resolved_product_type, + "product": {"type": resolved_product_type}, "task": task_name, "layer": layer.clean_name } - subset = resolved_subset_template.format( + product_name = resolved_product_template.format( **prepare_template_data(fill_pairs)) - subset = self._clean_subset_name(stub, naming_conventions, - subset, layer) + product_name = self._clean_product_name( + stub, naming_conventions, product_name, layer + ) - if subset in existing_subset_names: - self.log.info( - "Subset {} already created, skipping.".format(subset)) + if product_name in existing_product_names: + self.log.info(( + "Product {} already created, skipping." + ).format(product_name)) continue if self.create_flatten_image != "flatten_only": - instance = self._create_instance(context, layer, - resolved_family, - asset_name, subset, task_name) + instance = self._create_instance( + context, + layer, + resolved_product_type, + asset_name, + product_name, + task_name + ) created_instances.append(instance) - existing_subset_names.append(subset) + existing_product_names.append(product_name) publishable_layers.append(layer) if self.create_flatten_image != "no" and publishable_layers: @@ -127,15 +136,20 @@ class CollectColorCodedInstances(pyblish.api.ContextPlugin): return fill_pairs.pop("layer") - subset = self.flatten_product_name_template.format( + product_name = self.flatten_product_name_template.format( **prepare_template_data(fill_pairs)) first_layer = publishable_layers[0] # dummy layer - first_layer.name = subset - family = family_from_settings # inherit family - instance = self._create_instance(context, first_layer, - family, - asset_name, subset, task_name) + first_layer.name = product_name + product_type = product_type_from_settings # inherit product type + instance = self._create_instance( + context, + first_layer, + product_type, + asset_name, + product_name, + task_name + ) instance.data["ids"] = [layer.id for layer in publishable_layers] created_instances.append(instance) @@ -145,29 +159,36 @@ class CollectColorCodedInstances(pyblish.api.ContextPlugin): self.log.info("Found: \"%s\" " % instance.data["name"]) self.log.info("instance: {} ".format(instance.data)) - def _get_existing_subset_names(self, context): + def _get_existing_product_names(self, context): """Collect manually created instances from workfile. Shouldn't be any as Webpublisher bypass publishing via Openpype, but might be some if workfile published through OP is reused. """ - existing_subset_names = [] + existing_product_names = [] for instance in context: - if instance.data.get('publish'): - existing_subset_names.append(instance.data.get('subset')) + if instance.data.get("publish") is not False: + existing_product_names.append(instance.data.get("productName")) - return existing_subset_names + return existing_product_names - def _create_instance(self, context, layer, family, - asset, subset, task_name): + def _create_instance( + self, + context, + layer, + product_type, + folder_path, + product_name, + task_name + ): instance = context.create_instance(layer.name) - instance.data["family"] = family instance.data["publish"] = True - instance.data["folderPath"] = asset + instance.data["productType"] = product_type + instance.data["productName"] = product_name + instance.data["folderPath"] = folder_path instance.data["task"] = task_name - instance.data["subset"] = subset instance.data["layer"] = layer - instance.data["families"] = [] + instance.data["families"] = [product_type] return instance @@ -177,50 +198,63 @@ class CollectColorCodedInstances(pyblish.api.ContextPlugin): If both color code AND name regex is configured, BOTH must be valid If layer matches to multiple mappings, only first is used! """ - family_list = [] - family = None - subset_name_list = [] - resolved_subset_template = None + product_type_list = [] + product_name_list = [] for mapping in self.color_code_mapping: - if mapping["color_code"] and \ - layer.color_code not in mapping["color_code"]: + if ( + mapping["color_code"] + and layer.color_code not in mapping["color_code"] + ): continue - if mapping["layer_name_regex"] and \ - not any(re.search(pattern, layer.name) - for pattern in mapping["layer_name_regex"]): + if ( + mapping["layer_name_regex"] + and not any( + re.search(pattern, layer.name) + for pattern in mapping["layer_name_regex"] + ) + ): continue - family_list.append(mapping["family"]) - subset_name_list.append(mapping["subset_template_name"]) - if len(subset_name_list) > 1: - self.log.warning("Multiple mappings found for '{}'". - format(layer.name)) - self.log.warning("Only first subset name template used!") - subset_name_list[:] = subset_name_list[0] + product_type_list.append(mapping["product_type"]) + product_name_list.append(mapping["product_name_template"]) - if len(family_list) > 1: - self.log.warning("Multiple mappings found for '{}'". - format(layer.name)) - self.log.warning("Only first family used!") - family_list[:] = family_list[0] - if subset_name_list: - resolved_subset_template = subset_name_list.pop() - if family_list: - family = family_list.pop() + if len(product_name_list) > 1: + self.log.warning( + "Multiple mappings found for '{}'".format(layer.name) + ) + self.log.warning("Only first product name template used!") + product_name_list[:] = product_name_list[0] - self.log.debug("resolved_family {}".format(family)) - self.log.debug("resolved_subset_template {}".format( - resolved_subset_template)) - return family, resolved_subset_template + if len(product_type_list) > 1: + self.log.warning( + "Multiple mappings found for '{}'".format(layer.name) + ) + self.log.warning("Only first product type used!") + product_type_list[:] = product_type_list[0] - def _clean_subset_name(self, stub, naming_conventions, subset, layer): - """Cleans invalid characters from subset name and layer name.""" - if re.search(naming_conventions["invalid_chars"], subset): - subset = re.sub( + resolved_product_template = None + if product_name_list: + resolved_product_template = product_name_list.pop() + + product_type = None + if product_type_list: + product_type = product_type_list.pop() + + self.log.debug("resolved_product_type {}".format(product_type)) + self.log.debug("resolved_product_template {}".format( + resolved_product_template)) + return product_type, resolved_product_template + + def _clean_product_name( + self, stub, naming_conventions, product_name, layer + ): + """Cleans invalid characters from product name and layer name.""" + if re.search(naming_conventions["invalid_chars"], product_name): + product_name = re.sub( naming_conventions["invalid_chars"], naming_conventions["replace_char"], - subset + product_name ) layer_name = re.sub( naming_conventions["invalid_chars"], @@ -230,4 +264,4 @@ class CollectColorCodedInstances(pyblish.api.ContextPlugin): layer.name = layer_name stub.rename_layer(layer.id, layer_name) - return subset + return product_name diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_published_version.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_published_version.py index e330b04a1f..53b2503ba2 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_published_version.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_published_version.py @@ -4,7 +4,7 @@ For synchronization of published image and workfile version it is required to store workfile version from workfile file name in context.data["version"]. In remote publishing this name is unreliable (artist might not follow naming convention etc.), last published workfile version for particular workfile -subset is used instead. +product is used instead. This plugin runs only in remote publishing (eg. Webpublisher). @@ -30,13 +30,13 @@ class CollectPublishedVersion(pyblish.api.ContextPlugin): targets = ["automated"] def process(self, context): - workfile_subset_name = None + workfile_product_name = None for instance in context: - if instance.data["family"] == "workfile": - workfile_subset_name = instance.data["subset"] + if instance.data["productType"] == "workfile": + workfile_product_name = instance.data["productName"] break - if not workfile_subset_name: + if not workfile_product_name: self.log.warning("No workfile instance found, " "synchronization of version will not work.") return @@ -45,9 +45,9 @@ class CollectPublishedVersion(pyblish.api.ContextPlugin): asset_doc = context.data["assetEntity"] asset_id = asset_doc["_id"] - version_doc = get_last_version_by_subset_name(project_name, - workfile_subset_name, - asset_id) + version_doc = get_last_version_by_subset_name( + project_name, workfile_product_name, asset_id + ) if version_doc: version_int = int(version_doc["name"]) + 1 diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_workfile.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_workfile.py index 6740a6c82a..5ca03faf8f 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_workfile.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_workfile.py @@ -15,7 +15,7 @@ class CollectWorkfile(pyblish.api.ContextPlugin): def process(self, context): for instance in context: - if instance.data["family"] == "workfile": + if instance.data["productType"] == "workfile": file_path = context.data["currentFile"] _, ext = os.path.splitext(file_path) staging_dir = os.path.dirname(file_path) diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/extract_image.py b/client/ayon_core/hosts/photoshop/plugins/publish/extract_image.py index 71605b53d9..7290a1437e 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/extract_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/extract_image.py @@ -36,7 +36,7 @@ class ExtractImage(pyblish.api.ContextPlugin): with photoshop.maintained_selection(): with photoshop.maintained_visibility(layers=all_layers): for instance in context: - if instance.data["family"] not in self.families: + if instance.data["productType"] not in self.families: continue staging_dir = self.staging_dir(instance) diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/extract_review.py b/client/ayon_core/hosts/photoshop/plugins/publish/extract_review.py index 732a53f194..3497e7ad75 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/extract_review.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/extract_review.py @@ -22,7 +22,8 @@ class ExtractReview(publish.Extractor): 'review' family could be used in other steps as a reference, as it contains flattened image by default. (Eg. artist could load this review as a single item and see full image. In most cases 'image' - family is separated by layers to better usage in animation or comp.) + product type is separated by layers to better usage in animation + or comp.) """ label = "Extract Review" @@ -55,18 +56,21 @@ class ExtractReview(publish.Extractor): "tags": self.jpg_options['tags'], } - if instance.data["family"] != "review": - self.log.debug("Existing extracted file from image family used.") + if instance.data["productType"] != "review": + self.log.debug( + "Existing extracted file from image product type used." + ) # enable creation of review, without this jpg review would clash - # with jpg of the image family + # with jpg of the image product type output_name = repre_name repre_name = "{}_{}".format(repre_name, output_name) repre_skeleton.update({"name": repre_name, "outputName": output_name}) img_file = self.output_seq_filename % 0 - self._prepare_file_for_image_family(img_file, instance, - staging_dir) + self._prepare_file_for_image_product_type( + img_file, instance, staging_dir + ) repre_skeleton.update({ "files": img_file, }) @@ -120,8 +124,10 @@ class ExtractReview(publish.Extractor): self.log.info(f"Extracted {instance} to {staging_dir}") - def _prepare_file_for_image_family(self, img_file, instance, staging_dir): - """Converts existing file for image family to .jpg + def _prepare_file_for_image_product_type( + self, img_file, instance, staging_dir + ): + """Converts existing file for image product type to .jpg Image instance could have its own separate review (instance per layer for example). This uses extracted file instead of extracting again. @@ -261,12 +267,15 @@ class ExtractReview(publish.Extractor): """ layers = [] # creating review for existing 'image' instance - if instance.data["family"] == "image" and instance.data.get("layer"): + if ( + instance.data["productType"] == "image" + and instance.data.get("layer") + ): layers.append(instance.data["layer"]) return layers for image_instance in instance.context: - if image_instance.data["family"] != "image": + if image_instance.data["productType"] != "image": continue if not image_instance.data.get("layer"): # dummy instance for flatten image diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/help/validate_naming.xml b/client/ayon_core/hosts/photoshop/plugins/publish/help/validate_naming.xml index 023bbf26fa..28c2c2c773 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/help/validate_naming.xml +++ b/client/ayon_core/hosts/photoshop/plugins/publish/help/validate_naming.xml @@ -3,9 +3,9 @@ Subset name -## Invalid subset or layer name +## Invalid product or layer name -Subset or layer name cannot contain specific characters (spaces etc) which could cause issue when subset name is used in a published file name. +Subset or layer name cannot contain specific characters (spaces etc) which could cause issue when product name is used in a published file name. {msg} ### How to repair? diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/validate_naming.py b/client/ayon_core/hosts/photoshop/plugins/publish/validate_naming.py index 89018a1cff..af4e646a67 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/validate_naming.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/validate_naming.py @@ -51,17 +51,17 @@ class ValidateNamingRepair(pyblish.api.Action): stub.rename_layer(current_layer_state.id, layer_name) - subset_name = re.sub(invalid_chars, replace_char, - instance.data["subset"]) + product_name = re.sub(invalid_chars, replace_char, + instance.data["productName"]) # format from Tool Creator - subset_name = re.sub( + product_name = re.sub( "[^{}]+".format(SUBSET_NAME_ALLOWED_SYMBOLS), "", - subset_name + product_name ) - layer_meta["subset"] = subset_name + layer_meta["productName"] = product_name stub.imprint(instance_id, layer_meta) return True @@ -88,21 +88,24 @@ class ValidateNaming(pyblish.api.InstancePlugin): layer = instance.data.get("layer") if layer: - msg = "Name \"{}\" is not allowed.{}".format(layer.clean_name, - help_msg) - + msg = "Name \"{}\" is not allowed.{}".format( + layer.clean_name, help_msg + ) formatting_data = {"msg": msg} if re.search(self.invalid_chars, layer.clean_name): - raise PublishXmlValidationError(self, msg, - formatting_data=formatting_data - ) + raise PublishXmlValidationError( + self, msg, formatting_data=formatting_data + ) - msg = "Subset \"{}\" is not allowed.{}".format(instance.data["subset"], - help_msg) + product_name = instance.data["productName"] + msg = "Product \"{}\" is not allowed.{}".format( + product_name, help_msg + ) formatting_data = {"msg": msg} - if re.search(self.invalid_chars, instance.data["subset"]): - raise PublishXmlValidationError(self, msg, - formatting_data=formatting_data) + if re.search(self.invalid_chars, product_name): + raise PublishXmlValidationError( + self, msg, formatting_data=formatting_data + ) @classmethod def get_replace_chars(cls): From 6f875f8799544d685e298cf8b19d025ecf179d20 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 22 Feb 2024 18:18:04 +0800 Subject: [PATCH 232/573] make sure all instance.context.data['asset'] converted to instance.context.data['folderPath'] --- .../hosts/max/plugins/publish/validate_instance_in_context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py b/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py index 872a687597..5d544513c0 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py @@ -100,7 +100,7 @@ class ValidateInstanceInContext(pyblish.api.InstancePlugin, @staticmethod def get_context_asset(instance): - return instance.context.data["asset"] + return instance.context.data["folderPath"] @staticmethod def get_context_task(instance): From c25161d454ca05dc21abff0eacf885c2e6cddbd8 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 22 Feb 2024 18:26:54 +0800 Subject: [PATCH 233/573] coverting asset to folderPath --- .../ayon_core/hosts/max/plugins/publish/validate_model_name.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py b/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py index 7859889561..87a9132989 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py @@ -58,7 +58,7 @@ class ValidateModelName(pyblish.api.InstancePlugin, reg = re.compile(regex) matched_name = reg.match(name) project_name = instance.context.data["projectName"] - current_asset_name = instance.context.data["asset"] + current_asset_name = instance.context.data["folderPath"] if matched_name is None: cls.log.error("invalid model name on: {}".format(name)) cls.log.error("name doesn't match regex {}".format(regex)) From 190baf83a5ecb35280d6d332b828ad28053152d5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 22 Feb 2024 11:58:42 +0100 Subject: [PATCH 234/573] removed unused deprecated functions from nuke --- client/ayon_core/hosts/nuke/api/__init__.py | 2 - client/ayon_core/hosts/nuke/api/lib.py | 552 -------------------- client/ayon_core/hosts/nuke/api/plugin.py | 201 ------- 3 files changed, 755 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/__init__.py b/client/ayon_core/hosts/nuke/api/__init__.py index 2536230637..caefba766f 100644 --- a/client/ayon_core/hosts/nuke/api/__init__.py +++ b/client/ayon_core/hosts/nuke/api/__init__.py @@ -13,7 +13,6 @@ from .plugin import ( NukeCreator, NukeWriteCreator, NukeCreatorError, - OpenPypeCreator, get_instance_group_node_childs, get_colorspace_from_node ) @@ -69,7 +68,6 @@ __all__ = ( "NukeCreator", "NukeWriteCreator", "NukeCreatorError", - "OpenPypeCreator", "NukeHost", "get_instance_group_node_childs", "get_colorspace_from_node", diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index a2ec8fdb98..2628df5ff9 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -531,55 +531,6 @@ def get_avalon_knob_data(node, prefix="avalon:", create=True): return data -@deprecated -def fix_data_for_node_create(data): - """[DEPRECATED] Fixing data to be used for nuke knobs - """ - for k, v in data.items(): - if isinstance(v, six.text_type): - data[k] = str(v) - if str(v).startswith("0x"): - data[k] = int(v, 16) - return data - - -@deprecated -def add_write_node_legacy(name, **kwarg): - """[DEPRECATED] Adding nuke write node - Arguments: - name (str): nuke node name - kwarg (attrs): data for nuke knobs - Returns: - node (obj): nuke write node - """ - use_range_limit = kwarg.get("use_range_limit", None) - - w = nuke.createNode( - "Write", - "name {}".format(name), - inpanel=False - ) - - w["file"].setValue(kwarg["file"]) - - for k, v in kwarg.items(): - if "frame_range" in k: - continue - log.info([k, v]) - try: - w[k].setValue(v) - except KeyError as e: - log.debug(e) - continue - - if use_range_limit: - w["use_limit"].setValue(True) - w["first"].setValue(kwarg["frame_range"][0]) - w["last"].setValue(kwarg["frame_range"][1]) - - return w - - def add_write_node(name, file_path, knobs, **kwarg): """Adding nuke write node @@ -711,86 +662,6 @@ def get_nuke_imageio_settings(): return get_project_settings(Context.project_name)["nuke"]["imageio"] -@deprecated("ayon_core.hosts.nuke.api.lib.get_nuke_imageio_settings") -def get_created_node_imageio_setting_legacy(nodeclass, creator, subset): - '''[DEPRECATED] Get preset data for dataflow (fileType, compression, bitDepth) - ''' - - assert any([creator, nodeclass]), nuke.message( - "`{}`: Missing mandatory kwargs `host`, `cls`".format(__file__)) - - imageio_nodes = get_nuke_imageio_settings()["nodes"] - required_nodes = imageio_nodes["requiredNodes"] - - # HACK: for backward compatibility this needs to be optional - override_nodes = imageio_nodes.get("overrideNodes", []) - - imageio_node = None - for node in required_nodes: - log.info(node) - if ( - nodeclass in node["nukeNodeClass"] - and creator in node["plugins"] - ): - imageio_node = node - break - - log.debug("__ imageio_node: {}".format(imageio_node)) - - # find matching override node - override_imageio_node = None - for onode in override_nodes: - log.info(onode) - if nodeclass not in node["nukeNodeClass"]: - continue - - if creator not in node["plugins"]: - continue - - if ( - onode["subsets"] - and not any( - re.search(s.lower(), subset.lower()) - for s in onode["subsets"] - ) - ): - continue - - override_imageio_node = onode - break - - log.debug("__ override_imageio_node: {}".format(override_imageio_node)) - # add overrides to imageio_node - if override_imageio_node: - # get all knob names in imageio_node - knob_names = [k["name"] for k in imageio_node["knobs"]] - - for oknob in override_imageio_node["knobs"]: - for knob in imageio_node["knobs"]: - # override matching knob name - if oknob["name"] == knob["name"]: - log.debug( - "_ overriding knob: `{}` > `{}`".format( - knob, oknob - )) - if not oknob["value"]: - # remove original knob if no value found in oknob - imageio_node["knobs"].remove(knob) - else: - # override knob value with oknob's - knob["value"] = oknob["value"] - - # add missing knobs into imageio_node - if oknob["name"] not in knob_names: - log.debug( - "_ adding knob: `{}`".format(oknob)) - imageio_node["knobs"].append(oknob) - knob_names.append(oknob["name"]) - - log.info("ImageIO node: {}".format(imageio_node)) - return imageio_node - - def get_imageio_node_setting(node_class, plugin_name, subset): ''' Get preset data for dataflow (fileType, compression, bitDepth) ''' @@ -1410,285 +1281,6 @@ def create_write_node( return GN -@deprecated("ayon_core.hosts.nuke.api.lib.create_write_node") -def create_write_node_legacy( - name, data, input=None, prenodes=None, - review=True, linked_knobs=None, farm=True -): - ''' Creating write node which is group node - - Arguments: - name (str): name of node - data (dict): data to be imprinted - input (node): selected node to connect to - prenodes (list, optional): list of lists, definitions for nodes - to be created before write - review (bool): adding review knob - - Example: - prenodes = [ - { - "nodeName": { - "class": "" # string - "knobs": [ - ("knobName": value), - ... - ], - "dependent": [ - following_node_01, - ... - ] - } - }, - ... - ] - - Return: - node (obj): group node with avalon data as Knobs - ''' - knob_overrides = data.get("knobs", []) - nodeclass = data["nodeclass"] - creator = data["creator"] - subset = data["subset"] - - imageio_writes = get_created_node_imageio_setting_legacy( - nodeclass, creator, subset - ) - for knob in imageio_writes["knobs"]: - if knob["name"] == "file_type": - representation = knob["value"] - - host_name = get_current_host_name() - try: - data.update({ - "app": host_name, - "imageio_writes": imageio_writes, - "representation": representation, - }) - anatomy_filled = format_anatomy(data) - - except Exception as e: - msg = "problem with resolving anatomy template: {}".format(e) - log.error(msg) - nuke.message(msg) - - # build file path to workfiles - fdir = str(anatomy_filled["work"]["folder"]).replace("\\", "/") - fpath = data["fpath_template"].format( - work=fdir, version=data["version"], subset=data["subset"], - frame=data["frame"], - ext=representation - ) - - # create directory - if not os.path.isdir(os.path.dirname(fpath)): - log.warning("Path does not exist! I am creating it.") - os.makedirs(os.path.dirname(fpath)) - - _data = OrderedDict({ - "file": fpath - }) - - # adding dataflow template - log.debug("imageio_writes: `{}`".format(imageio_writes)) - for knob in imageio_writes["knobs"]: - _data[knob["name"]] = knob["value"] - - _data = fix_data_for_node_create(_data) - - log.debug("_data: `{}`".format(_data)) - - if "frame_range" in data.keys(): - _data["frame_range"] = data.get("frame_range", None) - log.debug("_data[frame_range]: `{}`".format(_data["frame_range"])) - - GN = nuke.createNode("Group", "name {}".format(name)) - - prev_node = None - with GN: - if input: - input_name = str(input.name()).replace(" ", "") - # if connected input node was defined - prev_node = nuke.createNode( - "Input", "name {}".format(input_name)) - else: - # generic input node connected to nothing - prev_node = nuke.createNode( - "Input", - "name {}".format("rgba"), - inpanel=False - ) - # creating pre-write nodes `prenodes` - if prenodes: - for node in prenodes: - # get attributes - pre_node_name = node["name"] - klass = node["class"] - knobs = node["knobs"] - dependent = node["dependent"] - - # create node - now_node = nuke.createNode( - klass, - "name {}".format(pre_node_name), - inpanel=False - ) - - # add data to knob - for _knob in knobs: - knob, value = _knob - try: - now_node[knob].value() - except NameError: - log.warning( - "knob `{}` does not exist on node `{}`".format( - knob, now_node["name"].value() - )) - continue - - if not knob and not value: - continue - - log.info((knob, value)) - - if isinstance(value, str): - if "[" in value: - now_node[knob].setExpression(value) - else: - now_node[knob].setValue(value) - - # connect to previous node - if dependent: - if isinstance(dependent, (tuple or list)): - for i, node_name in enumerate(dependent): - input_node = nuke.createNode( - "Input", - "name {}".format(node_name), - inpanel=False - ) - now_node.setInput(1, input_node) - - elif isinstance(dependent, str): - input_node = nuke.createNode( - "Input", - "name {}".format(node_name), - inpanel=False - ) - now_node.setInput(0, input_node) - - else: - now_node.setInput(0, prev_node) - - # switch actual node to previous - prev_node = now_node - - # creating write node - - write_node = now_node = add_write_node_legacy( - "inside_{}".format(name), - **_data - ) - # connect to previous node - now_node.setInput(0, prev_node) - - # switch actual node to previous - prev_node = now_node - - now_node = nuke.createNode("Output", "name Output1", inpanel=False) - - # connect to previous node - now_node.setInput(0, prev_node) - - # imprinting group node - set_avalon_knob_data(GN, data["avalon"]) - add_publish_knob(GN) - add_rendering_knobs(GN, farm) - - if review: - add_review_knob(GN) - - # add divider - GN.addKnob(nuke.Text_Knob('', 'Rendering')) - - # Add linked knobs. - linked_knob_names = [] - - # add input linked knobs and create group only if any input - if linked_knobs: - linked_knob_names.append("_grp-start_") - linked_knob_names.extend(linked_knobs) - linked_knob_names.append("_grp-end_") - - linked_knob_names.append("Render") - - for _k_name in linked_knob_names: - if "_grp-start_" in _k_name: - knob = nuke.Tab_Knob( - "rnd_attr", "Rendering attributes", nuke.TABBEGINCLOSEDGROUP) - GN.addKnob(knob) - elif "_grp-end_" in _k_name: - knob = nuke.Tab_Knob( - "rnd_attr_end", "Rendering attributes", nuke.TABENDGROUP) - GN.addKnob(knob) - else: - if "___" in _k_name: - # add divider - GN.addKnob(nuke.Text_Knob("")) - else: - # add linked knob by _k_name - link = nuke.Link_Knob("") - link.makeLink(write_node.name(), _k_name) - link.setName(_k_name) - - # make render - if "Render" in _k_name: - link.setLabel("Render Local") - link.setFlag(0x1000) - GN.addKnob(link) - - # adding write to read button - add_button_write_to_read(GN) - - # adding write to read button - add_button_clear_rendered(GN, os.path.dirname(fpath)) - - # Deadline tab. - add_deadline_tab(GN) - - # open the our Tab as default - GN[_NODE_TAB_NAME].setFlag(0) - - # set tile color - tile_color = _data.get("tile_color", "0xff0000ff") - GN["tile_color"].setValue(tile_color) - - # override knob values from settings - for knob in knob_overrides: - knob_type = knob["type"] - knob_name = knob["name"] - knob_value = knob["value"] - if knob_name not in GN.knobs(): - continue - if not knob_value: - continue - - # set correctly knob types - if knob_type == "string": - knob_value = str(knob_value) - if knob_type == "number": - knob_value = int(knob_value) - if knob_type == "decimal_number": - knob_value = float(knob_value) - if knob_type == "bool": - knob_value = bool(knob_value) - if knob_type in ["2d_vector", "3d_vector", "color", "box"]: - knob_value = list(knob_value) - - GN[knob_name].setValue(knob_value) - - return GN - - def set_node_knobs_from_settings(node, knob_settings, **kwargs): """ Overriding knob values from settings @@ -1775,84 +1367,6 @@ def color_gui_to_int(color_gui): return int(hex_value, 16) -@deprecated -def add_rendering_knobs(node, farm=True): - ''' Adds additional rendering knobs to given node - - Arguments: - node (obj): nuke node object to be fixed - - Return: - node (obj): with added knobs - ''' - knob_options = ["Use existing frames", "Local"] - if farm: - knob_options.append("On farm") - - if "render" not in node.knobs(): - knob = nuke.Enumeration_Knob("render", "", knob_options) - knob.clearFlag(nuke.STARTLINE) - node.addKnob(knob) - return node - - -@deprecated -def add_review_knob(node): - ''' Adds additional review knob to given node - - Arguments: - node (obj): nuke node object to be fixed - - Return: - node (obj): with added knob - ''' - if "review" not in node.knobs(): - knob = nuke.Boolean_Knob("review", "Review") - knob.setValue(True) - node.addKnob(knob) - return node - - -@deprecated -def add_deadline_tab(node): - # TODO: remove this as it is only linked to legacy create - node.addKnob(nuke.Tab_Knob("Deadline")) - - knob = nuke.Int_Knob("deadlinePriority", "Priority") - knob.setValue(50) - node.addKnob(knob) - - knob = nuke.Int_Knob("deadlineChunkSize", "Chunk Size") - knob.setValue(0) - node.addKnob(knob) - - knob = nuke.Int_Knob("deadlineConcurrentTasks", "Concurrent tasks") - # zero as default will get value from Settings during collection - # instead of being an explicit user override, see precollect_write.py - knob.setValue(0) - node.addKnob(knob) - - knob = nuke.Text_Knob("divd", '') - knob.setValue('') - node.addKnob(knob) - - knob = nuke.Boolean_Knob("suspend_publish", "Suspend publish") - knob.setValue(False) - node.addKnob(knob) - - -@deprecated -def get_deadline_knob_names(): - # TODO: remove this as it is only linked to legacy - # validate_write_deadline_tab - return [ - "Deadline", - "deadlineChunkSize", - "deadlinePriority", - "deadlineConcurrentTasks" - ] - - def create_backdrop(label="", color=None, layer=0, nodes=None): """ @@ -3012,72 +2526,6 @@ def start_workfile_template_builder(): log.warning("Template profile not found. Skipping...") -@deprecated -def recreate_instance(origin_node, avalon_data=None): - """Recreate input instance to different data - - Args: - origin_node (nuke.Node): Nuke node to be recreating from - avalon_data (dict, optional): data to be used in new node avalon_data - - Returns: - nuke.Node: newly created node - """ - knobs_wl = ["render", "publish", "review", "ypos", - "use_limit", "first", "last"] - # get data from avalon knobs - data = get_avalon_knob_data( - origin_node) - - # add input data to avalon data - if avalon_data: - data.update(avalon_data) - - # capture all node knobs allowed in op_knobs - knobs_data = {k: origin_node[k].value() - for k in origin_node.knobs() - for key in knobs_wl - if key in k} - - # get node dependencies - inputs = origin_node.dependencies() - outputs = origin_node.dependent() - - # remove the node - nuke.delete(origin_node) - - # create new node - # get appropriate plugin class - creator_plugin = None - for Creator in discover_legacy_creator_plugins(): - if Creator.__name__ == data["creator"]: - creator_plugin = Creator - break - - # create write node with creator - new_node_name = data["subset"] - new_node = creator_plugin(new_node_name, data["asset"]).process() - - # white listed knobs to the new node - for _k, _v in knobs_data.items(): - try: - print(_k, _v) - new_node[_k].setValue(_v) - except Exception as e: - print(e) - - # connect to original inputs - for i, n in enumerate(inputs): - new_node.setInput(i, n) - - # connect to outputs - if len(outputs) > 0: - for dn in outputs: - dn.setInput(0, new_node) - - return new_node - - def add_scripts_menu(): try: from scriptsmenu import launchfornuke diff --git a/client/ayon_core/hosts/nuke/api/plugin.py b/client/ayon_core/hosts/nuke/api/plugin.py index 59042acee1..3b93a4e3cd 100644 --- a/client/ayon_core/hosts/nuke/api/plugin.py +++ b/client/ayon_core/hosts/nuke/api/plugin.py @@ -409,54 +409,6 @@ class NukeWriteCreator(NukeCreator): ) -class OpenPypeCreator(LegacyCreator): - """Pype Nuke Creator class wrapper""" - node_color = "0xdfea5dff" - - def __init__(self, *args, **kwargs): - super(OpenPypeCreator, self).__init__(*args, **kwargs) - if check_subsetname_exists( - nuke.allNodes(), - self.data["subset"]): - msg = ("The subset name `{0}` is already used on a node in" - "this workfile.".format(self.data["subset"])) - self.log.error(msg + "\n\nPlease use other subset name!") - raise NameError("`{0}: {1}".format(__name__, msg)) - return - - def process(self): - from nukescripts import autoBackdrop - - instance = None - - if (self.options or {}).get("useSelection"): - - nodes = nuke.selectedNodes() - if not nodes: - nuke.message("Please select nodes that you " - "wish to add to a container") - return - - elif len(nodes) == 1: - # only one node is selected - instance = nodes[0] - - if not instance: - # Not using selection or multiple nodes selected - bckd_node = autoBackdrop() - bckd_node["tile_color"].setValue(int(self.node_color, 16)) - bckd_node["note_font_size"].setValue(24) - bckd_node["label"].setValue("[{}]".format(self.name)) - - instance = bckd_node - - # add avalon knobs - set_avalon_knob_data(instance, self.data) - add_publish_knob(instance) - - return instance - - def get_instance_group_node_childs(instance): """Return list of instance group node children @@ -1050,159 +1002,6 @@ class ExporterReviewMov(ExporterReview): self._shift_to_previous_node_and_temp(subset, node, message) -@deprecated("ayon_core.hosts.nuke.api.plugin.NukeWriteCreator") -class AbstractWriteRender(OpenPypeCreator): - """Abstract creator to gather similar implementation for Write creators""" - name = "" - label = "" - hosts = ["nuke"] - n_class = "Write" - family = "render" - icon = "sign-out" - defaults = ["Main", "Mask"] - knobs = [] - prenodes = {} - - def __init__(self, *args, **kwargs): - super(AbstractWriteRender, self).__init__(*args, **kwargs) - - data = OrderedDict() - - data["family"] = self.family - data["families"] = self.n_class - - for k, v in self.data.items(): - if k not in data.keys(): - data.update({k: v}) - - self.data = data - self.nodes = nuke.selectedNodes() - - def process(self): - - inputs = [] - outputs = [] - instance = nuke.toNode(self.data["subset"]) - selected_node = None - - # use selection - if (self.options or {}).get("useSelection"): - nodes = self.nodes - - if not (len(nodes) < 2): - msg = ("Select only one node. " - "The node you want to connect to, " - "or tick off `Use selection`") - self.log.error(msg) - nuke.message(msg) - return - - if len(nodes) == 0: - msg = ( - "No nodes selected. Please select a single node to connect" - " to or tick off `Use selection`" - ) - self.log.error(msg) - nuke.message(msg) - return - - selected_node = nodes[0] - inputs = [selected_node] - outputs = selected_node.dependent() - - if instance: - if (instance.name() in selected_node.name()): - selected_node = instance.dependencies()[0] - - # if node already exist - if instance: - # collect input / outputs - inputs = instance.dependencies() - outputs = instance.dependent() - selected_node = inputs[0] - # remove old one - nuke.delete(instance) - - # recreate new - write_data = { - "nodeclass": self.n_class, - "families": [self.family], - "avalon": self.data, - "subset": self.data["subset"], - "knobs": self.knobs - } - - # add creator data - creator_data = {"creator": self.__class__.__name__} - self.data.update(creator_data) - write_data.update(creator_data) - - write_node = self._create_write_node( - selected_node, - inputs, - outputs, - write_data - ) - - # relinking to collected connections - for i, input in enumerate(inputs): - write_node.setInput(i, input) - - write_node.autoplace() - - for output in outputs: - output.setInput(0, write_node) - - write_node = self._modify_write_node(write_node) - - return write_node - - def is_legacy(self): - """Check if it needs to run legacy code - - In case where `type` key is missing in single - knob it is legacy project anatomy. - - Returns: - bool: True if legacy - """ - imageio_nodes = get_nuke_imageio_settings()["nodes"] - node = imageio_nodes["requiredNodes"][0] - if "type" not in node["knobs"][0]: - # if type is not yet in project anatomy - return True - elif next(iter( - _k for _k in node["knobs"] - if _k.get("type") == "__legacy__" - ), None): - # in case someone re-saved anatomy - # with old configuration - return True - - @abstractmethod - def _create_write_node(self, selected_node, inputs, outputs, write_data): - """Family dependent implementation of Write node creation - - Args: - selected_node (nuke.Node) - inputs (list of nuke.Node) - input dependencies (what is connected) - outputs (list of nuke.Node) - output dependencies - write_data (dict) - values used to fill Knobs - Returns: - node (nuke.Node): group node with data as Knobs - """ - pass - - @abstractmethod - def _modify_write_node(self, write_node): - """Family dependent modification of created 'write_node' - - Returns: - node (nuke.Node): group node with data as Knobs - """ - pass - - def convert_to_valid_instaces(): """ Check and convert to latest publisher instances From f4c59525278e0255ccbe24698cb9ed96f5d8823a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 22 Feb 2024 14:22:52 +0100 Subject: [PATCH 235/573] auto fix missing alpha --- client/ayon_core/hosts/nuke/api/lib.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index b5702aef48..c11a9d9be0 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -1793,6 +1793,9 @@ def convert_knob_value_to_correct_type(knob_type, knob_value): def color_gui_to_int(color_gui): + # Append alpha channel if not present + if len(color_gui) == 3: + color_gui = list(color_gui) + [255] hex_value = ( "0x{0:0>2x}{1:0>2x}{2:0>2x}{3:0>2x}").format(*color_gui) return int(hex_value, 16) From dd053686e58af01da2ac71030a0fae546de640ed Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 22 Feb 2024 14:39:43 +0100 Subject: [PATCH 236/573] maya is using product name and type --- client/ayon_core/hosts/maya/api/lib.py | 62 +++++------ client/ayon_core/hosts/maya/api/plugin.py | 103 ++++++++++-------- client/ayon_core/hosts/maya/api/setdress.py | 5 +- .../maya/api/workfile_template_builder.py | 10 +- .../maya/plugins/create/convert_legacy.py | 72 ++++++------ .../maya/plugins/create/create_animation.py | 2 +- .../create/create_arnold_scene_source.py | 6 +- .../maya/plugins/create/create_assembly.py | 2 +- .../maya/plugins/create/create_camera.py | 4 +- .../maya/plugins/create/create_layout.py | 2 +- .../hosts/maya/plugins/create/create_look.py | 2 +- .../maya/plugins/create/create_matchmove.py | 2 +- .../maya/plugins/create/create_maya_usd.py | 2 +- .../maya/plugins/create/create_mayascene.py | 2 +- .../hosts/maya/plugins/create/create_model.py | 2 +- .../plugins/create/create_multishot_layout.py | 6 +- .../plugins/create/create_multiverse_look.py | 2 +- .../plugins/create/create_multiverse_usd.py | 2 +- .../create/create_multiverse_usd_comp.py | 2 +- .../create/create_multiverse_usd_over.py | 2 +- .../maya/plugins/create/create_pointcache.py | 6 +- .../maya/plugins/create/create_proxy_abc.py | 2 +- .../plugins/create/create_redshift_proxy.py | 6 +- .../maya/plugins/create/create_render.py | 8 +- .../maya/plugins/create/create_rendersetup.py | 8 +- .../maya/plugins/create/create_review.py | 12 +- .../hosts/maya/plugins/create/create_rig.py | 14 +-- .../maya/plugins/create/create_setdress.py | 2 +- .../create/create_unreal_skeletalmesh.py | 9 +- .../create/create_unreal_staticmesh.py | 9 +- .../plugins/create/create_unreal_yeticache.py | 2 +- .../maya/plugins/create/create_vrayproxy.py | 2 +- .../maya/plugins/create/create_vrayscene.py | 6 +- .../maya/plugins/create/create_workfile.py | 18 +-- .../hosts/maya/plugins/create/create_xgen.py | 2 +- .../maya/plugins/create/create_yeti_cache.py | 2 +- .../maya/plugins/create/create_yeti_rig.py | 6 +- .../plugins/inventory/connect_geometry.py | 18 +-- .../maya/plugins/inventory/connect_xgen.py | 18 +-- .../plugins/inventory/connect_yeti_rig.py | 14 +-- .../maya/plugins/load/_load_animation.py | 4 +- .../maya/plugins/load/load_arnold_standin.py | 4 +- .../hosts/maya/plugins/load/load_gpucache.py | 4 +- .../maya/plugins/load/load_image_plane.py | 10 +- .../maya/plugins/load/load_redshift_proxy.py | 8 +- .../hosts/maya/plugins/load/load_reference.py | 15 ++- .../maya/plugins/load/load_vdb_to_arnold.py | 8 +- .../maya/plugins/load/load_vdb_to_redshift.py | 8 +- .../maya/plugins/load/load_vdb_to_vray.py | 8 +- .../hosts/maya/plugins/load/load_vrayproxy.py | 8 +- .../hosts/maya/plugins/load/load_vrayscene.py | 10 +- .../maya/plugins/load/load_yeti_cache.py | 8 +- .../hosts/maya/plugins/load/load_yeti_rig.py | 2 +- .../maya/plugins/publish/collect_animation.py | 7 +- .../maya/plugins/publish/collect_history.py | 2 +- .../maya/plugins/publish/collect_instances.py | 6 +- .../maya/plugins/publish/collect_model.py | 5 +- .../maya/plugins/publish/collect_render.py | 23 ++-- .../publish/collect_render_layer_aovs.py | 4 +- .../maya/plugins/publish/collect_review.py | 26 ++--- .../maya/plugins/publish/collect_vrayproxy.py | 2 +- .../maya/plugins/publish/collect_vrayscene.py | 6 +- .../publish/determine_future_version.py | 34 +++--- .../plugins/publish/extract_camera_alembic.py | 4 +- .../publish/extract_camera_mayaScene.py | 4 +- .../maya/plugins/publish/extract_layout.py | 4 +- .../plugins/publish/extract_maya_scene_raw.py | 2 +- .../maya/plugins/publish/extract_playblast.py | 2 +- .../plugins/publish/extract_redshift_proxy.py | 2 +- .../maya/plugins/publish/extract_thumbnail.py | 2 +- .../maya/plugins/publish/extract_vrayproxy.py | 2 +- .../plugins/publish/extract_workfile_xgen.py | 2 +- .../publish/validate_animation_content.py | 2 +- .../plugins/publish/validate_attributes.py | 2 +- .../plugins/publish/validate_frame_range.py | 4 +- .../publish/validate_instance_has_members.py | 2 +- .../publish/validate_instance_subset.py | 44 ++++---- .../plugins/publish/validate_model_content.py | 2 +- .../publish/validate_renderlayer_aovs.py | 14 ++- .../validate_unreal_staticmesh_naming.py | 2 +- .../plugins/publish/validate_visible_only.py | 2 +- .../hosts/maya/tools/mayalookassigner/app.py | 22 ++-- .../tools/mayalookassigner/arnold_standin.py | 10 +- .../maya/tools/mayalookassigner/commands.py | 2 +- .../maya/tools/mayalookassigner/models.py | 20 ++-- .../tools/mayalookassigner/vray_proxies.py | 10 +- 86 files changed, 436 insertions(+), 394 deletions(-) diff --git a/client/ayon_core/hosts/maya/api/lib.py b/client/ayon_core/hosts/maya/api/lib.py index 77b18fb394..1aa2244111 100644 --- a/client/ayon_core/hosts/maya/api/lib.py +++ b/client/ayon_core/hosts/maya/api/lib.py @@ -1868,14 +1868,14 @@ def get_container_members(container): # region LOOKDEV def list_looks(project_name, asset_id): - """Return all look subsets for the given asset + """Return all look products for the given asset - This assumes all look subsets start with "look*" in their names. + This assumes all look products start with "look*" in their names. """ - # # get all subsets with look leading in + # # get all products with look leading in # the name associated with the asset - # TODO this should probably look for family 'look' instead of checking - # subset name that can not start with family + # TODO this should probably look for product type 'look' instead of + # checking product name that can not start with product type subset_docs = get_subsets(project_name, asset_ids=[asset_id]) return [ subset_doc @@ -1942,15 +1942,15 @@ def assign_look_by_version(nodes, version_id): apply_shaders(relationships, shader_nodes, nodes) -def assign_look(nodes, subset="lookDefault"): +def assign_look(nodes, product_name="lookDefault"): """Assigns a look to a node. Optimizes the nodes by grouping by asset id and finding - related subset by name. + related product by name. Args: nodes (list): all nodes to assign the look to - subset (str): name of the subset to find + product_name (str): name of the product to find """ # Group all nodes per asset id @@ -1965,22 +1965,22 @@ def assign_look(nodes, subset="lookDefault"): project_name = get_current_project_name() subset_docs = get_subsets( - project_name, subset_names=[subset], asset_ids=grouped.keys() + project_name, subset_names=[product_name], asset_ids=grouped.keys() ) subset_docs_by_asset_id = { str(subset_doc["parent"]): subset_doc for subset_doc in subset_docs } - subset_ids = { + product_ids = { subset_doc["_id"] for subset_doc in subset_docs_by_asset_id.values() } last_version_docs = get_last_versions( project_name, - subset_ids=subset_ids, + subset_ids=product_ids, fields=["_id", "name", "data.families"] ) - last_version_docs_by_subset_id = { + last_version_docs_by_product_id = { last_version_doc["parent"]: last_version_doc for last_version_doc in last_version_docs } @@ -1989,26 +1989,28 @@ def assign_look(nodes, subset="lookDefault"): # create objectId for database subset_doc = subset_docs_by_asset_id.get(asset_id) if not subset_doc: - log.warning("No subset '{}' found for {}".format(subset, asset_id)) + log.warning(( + "No product '{}' found for {}" + ).format(product_name, asset_id)) continue - last_version = last_version_docs_by_subset_id.get(subset_doc["_id"]) + last_version = last_version_docs_by_product_id.get(subset_doc["_id"]) if not last_version: log.warning(( - "Not found last version for subset '{}' on asset with id {}" - ).format(subset, asset_id)) + "Not found last version for product '{}' on asset with id {}" + ).format(product_name, asset_id)) continue families = last_version.get("data", {}).get("families") or [] if "look" not in families: log.warning(( - "Last version for subset '{}' on asset with id {}" - " does not have look family" - ).format(subset, asset_id)) + "Last version for product '{}' on asset with id {}" + " does not have look product type" + ).format(product_name, asset_id)) continue log.debug("Assigning look '{}' ".format( - subset, last_version["name"])) + product_name, last_version["name"])) assign_look_by_version(asset_nodes, last_version["_id"]) @@ -3955,7 +3957,9 @@ def get_all_children(nodes): return list(traversed) -def get_capture_preset(task_name, task_type, subset, project_settings, log): +def get_capture_preset( + task_name, task_type, product_name, project_settings, log +): """Get capture preset for playblasting. Logic for transitioning from old style capture preset to new capture preset @@ -3964,17 +3968,15 @@ def get_capture_preset(task_name, task_type, subset, project_settings, log): Args: task_name (str): Task name. task_type (str): Task type. - subset (str): Subset name. + product_name (str): Subset name. project_settings (dict): Project settings. log (logging.Logger): Logging object. """ capture_preset = None filtering_criteria = { - "hosts": "maya", - "families": "review", "task_names": task_name, "task_types": task_type, - "subset": subset + "product_names": product_name } plugin_settings = project_settings["maya"]["publish"]["ExtractPlayblast"] @@ -4117,8 +4119,8 @@ def create_rig_animation_instance( ) assert roots, "No root nodes in rig, this is a bug." - custom_subset = options.get("animationSubsetName") - if custom_subset: + custom_product_name = options.get("animationProductName") + if custom_product_name: formatting_data = { "asset": context["asset"], "subset": context['subset']['name'], @@ -4128,13 +4130,11 @@ def create_rig_animation_instance( ) } namespace = get_custom_namespace( - custom_subset.format( - **formatting_data - ) + custom_product_name.format(**formatting_data) ) if log: - log.info("Creating subset: {}".format(namespace)) + log.info("Creating product: {}".format(namespace)) # Fill creator identifier creator_identifier = "io.openpype.creators.maya.animation" diff --git a/client/ayon_core/hosts/maya/api/plugin.py b/client/ayon_core/hosts/maya/api/plugin.py index 7a01f1a174..e3cad2f862 100644 --- a/client/ayon_core/hosts/maya/api/plugin.py +++ b/client/ayon_core/hosts/maya/api/plugin.py @@ -29,7 +29,7 @@ from ayon_core.pipeline import ( ) from ayon_core.pipeline.load import LoadError from ayon_core.client import get_asset_by_name -from ayon_core.pipeline.create import get_subset_name +from ayon_core.pipeline.create import get_product_name from . import lib from .lib import imprint, read @@ -89,16 +89,16 @@ class Creator(LegacyCreator): class MayaCreatorBase(object): @staticmethod - def cache_subsets(shared_data): + def cache_instance_data(shared_data): """Cache instances for Creators to shared data. - Create `maya_cached_subsets` key when needed in shared data and + Create `maya_cached_instance_data` key when needed in shared data and fill it with all collected instances from the scene under its respective creator identifiers. If legacy instances are detected in the scene, create - `maya_cached_legacy_subsets` there and fill it with - all legacy subsets under family as a key. + `maya_cached_legacy_instances` there and fill it with + all legacy products under product type as a key. Args: Dict[str, Any]: Shared data. @@ -107,7 +107,7 @@ class MayaCreatorBase(object): Dict[str, Any]: Shared data dictionary. """ - if shared_data.get("maya_cached_subsets") is None: + if shared_data.get("maya_cached_instance_data") is None: cache = dict() cache_legacy = dict() @@ -131,8 +131,8 @@ class MayaCreatorBase(object): cache_legacy.setdefault(family, []).append(node) - shared_data["maya_cached_subsets"] = cache - shared_data["maya_cached_legacy_subsets"] = cache_legacy + shared_data["maya_cached_instance_data"] = cache + shared_data["maya_cached_legacy_instances"] = cache_legacy return shared_data def get_publish_families(self): @@ -143,8 +143,7 @@ class MayaCreatorBase(object): specify `usd` but apply different extractors like `usdMultiverse`. There is no need to override this method if you only have the - primary family defined by the `family` property as that will always - be set. + 'product_type' required for publish filtering. Returns: list: families for instances of this creator @@ -165,7 +164,7 @@ class MayaCreatorBase(object): data.pop("families", None) # We store creator attributes at the root level and assume they - # will not clash in names with `subset`, `task`, etc. and other + # will not clash in names with `product`, `task`, etc. and other # default names. This is just so these attributes in many cases # are still editable in the maya UI by artists. # note: pop to move to end of dict to sort attributes last on the node @@ -244,9 +243,11 @@ class MayaCreatorBase(object): return node_data def _default_collect_instances(self): - self.cache_subsets(self.collection_shared_data) - cached_subsets = self.collection_shared_data["maya_cached_subsets"] - for node in cached_subsets.get(self.identifier, []): + self.cache_instance_data(self.collection_shared_data) + cached_instances = ( + self.collection_shared_data["maya_cached_instance_data"] + ) + for node in cached_instances.get(self.identifier, []): node_data = self.read_instance_node(node) created_instance = CreatedInstance.from_existing(node_data, self) @@ -279,7 +280,7 @@ class MayaCreator(NewCreator, MayaCreatorBase): settings_category = "maya" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): members = list() if pre_create_data.get("use_selection"): @@ -294,11 +295,11 @@ class MayaCreator(NewCreator, MayaCreatorBase): families.append(family) with lib.undo_chunk(): - instance_node = cmds.sets(members, name=subset_name) + instance_node = cmds.sets(members, name=product_name) instance_data["instance_node"] = instance_node instance = CreatedInstance( - self.family, - subset_name, + self.product_type, + product_name, instance_data, self) self._add_instance_to_context(instance) @@ -385,7 +386,7 @@ def ensure_namespace(namespace): class RenderlayerCreator(NewCreator, MayaCreatorBase): """Creator which creates an instance per renderlayer in the workfile. - Create and manages renderlayer subset per renderLayer in workfile. + Create and manages renderlayer product per renderLayer in workfile. This generates a singleton node in the scene which, if it exists, tells the Creator to collect Maya rendersetup renderlayers as individual instances. As such, triggering create doesn't actually create the instance node per @@ -405,7 +406,7 @@ class RenderlayerCreator(NewCreator, MayaCreatorBase): if nodes: return nodes if return_all else nodes[0] - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): # A Renderlayer is never explicitly created using the create method. # Instead, renderlayers from the scene are collected. Thus "create" # would only ever be called to say, 'hey, please refresh collect' @@ -462,15 +463,15 @@ class RenderlayerCreator(NewCreator, MayaCreatorBase): "variant": layer.name(), } asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( layer.name(), instance_data["task"], asset_doc, project_name) instance = CreatedInstance( - family=self.family, - subset_name=subset_name, + product_type=self.product_type, + product_name=product_name, data=instance_data, creator=self ) @@ -574,7 +575,7 @@ class RenderlayerCreator(NewCreator, MayaCreatorBase): if node and cmds.objExists(node): cmds.delete(node) - def get_subset_name( + def get_product_name( self, variant, task_name, @@ -583,19 +584,27 @@ class RenderlayerCreator(NewCreator, MayaCreatorBase): host_name=None, instance=None ): - # creator.family != 'render' as expected - return get_subset_name(self.layer_instance_prefix, - variant, - task_name, - asset_doc, - project_name) + dynamic_data = self.get_dynamic_data( + variant, task_name, asset_doc, project_name, host_name, instance + ) + # creator.product_type != 'render' as expected + return get_product_name( + project_name, + asset_doc, + task_name, + host_name, + self.layer_instance_prefix or self.product_type, + variant, + dynamic_data=dynamic_data, + project_settings=self.project_settings + ) -def get_load_color_for_family(family, settings=None): - """Get color for family from settings. +def get_load_color_for_product_type(product_type, settings=None): + """Get color for product type from settings. Args: - family (str): Family name. + product_type (str): Family name. settings (Optional[dict]): Settings dictionary. Returns: @@ -606,7 +615,7 @@ def get_load_color_for_family(family, settings=None): settings = get_project_settings(get_current_project_name()) colors = settings["maya"]["load"]["colors"] - color = colors.get(family) + color = colors.get(product_type) if not color: return None @@ -657,24 +666,24 @@ class Loader(LoaderPlugin): self.log.debug("No custom group_name, no group will be created.") options["attach_to_root"] = False - asset = context["asset"] - subset = context["subset"] - family = ( - subset["data"].get("family") - or subset["data"]["families"][0] + asset_doc = context["asset"] + subset_doc = context["subset"] + product_type = ( + subset_doc["data"].get("family") + or subset_doc["data"]["families"][0] ) formatting_data = { - "asset_name": asset["name"], - "asset_type": asset["type"], + "asset_name": asset_doc["name"], + "asset_type": asset_doc["type"], "folder": { - "name": asset["name"], + "name": asset_doc["name"], }, - "subset": subset["name"], + "subset": subset_doc["name"], "product": { - "name": subset["name"], - "type": family, + "name": subset_doc["name"], + "type": product_type, }, - "family": family + "family": product_type } custom_namespace = custom_naming["namespace"].format( @@ -745,7 +754,7 @@ class ReferenceLoader(Loader): options['group_name'] = group_name - # Offset loaded subset + # Offset loaded product if "offset" in options: offset = [i * c for i in options["offset"]] options["translate"] = offset diff --git a/client/ayon_core/hosts/maya/api/setdress.py b/client/ayon_core/hosts/maya/api/setdress.py index 7a1054cc49..8d09716bf6 100644 --- a/client/ayon_core/hosts/maya/api/setdress.py +++ b/client/ayon_core/hosts/maya/api/setdress.py @@ -480,7 +480,7 @@ def update_scene(set_container, containers, current_data, new_data, new_file): continue # Check whether the conversion can be done by the Loader. - # They *must* use the same asset, subset and Loader for + # They *must* use the same asset, product and Loader for # `update_container` to make sense. old = get_representation_by_id( project_name, representation_current @@ -559,13 +559,14 @@ def compare_representations(old, new): new_context = new["context"] old_context = old["context"] + # TODO add better validation e.g. based on parent ids if new_context["asset"] != old_context["asset"]: log.error("Changing assets between updates is " "not supported.") return False if new_context["subset"] != old_context["subset"]: - log.error("Changing subsets between updates is " + log.error("Changing products between updates is " "not supported.") return False diff --git a/client/ayon_core/hosts/maya/api/workfile_template_builder.py b/client/ayon_core/hosts/maya/api/workfile_template_builder.py index e9db8ffe79..6ae2a075e3 100644 --- a/client/ayon_core/hosts/maya/api/workfile_template_builder.py +++ b/client/ayon_core/hosts/maya/api/workfile_template_builder.py @@ -140,10 +140,12 @@ class MayaPlaceholderLoadPlugin(PlaceholderPlugin, PlaceholderLoadMixin): placeholder_name_parts = placeholder_data["builder_type"].split("_") pos = 1 - # add family in any - placeholder_family = placeholder_data["family"] - if placeholder_family: - placeholder_name_parts.insert(pos, placeholder_family) + placeholder_product_type = placeholder_data.get("product_type") + if placeholder_product_type is None: + placeholder_product_type = placeholder_data.get("family") + + if placeholder_product_type: + placeholder_name_parts.insert(pos, placeholder_product_type) pos += 1 # add loader arguments if any diff --git a/client/ayon_core/hosts/maya/plugins/create/convert_legacy.py b/client/ayon_core/hosts/maya/plugins/create/convert_legacy.py index 029ea25b40..97a438a76f 100644 --- a/client/ayon_core/hosts/maya/plugins/create/convert_legacy.py +++ b/client/ayon_core/hosts/maya/plugins/create/convert_legacy.py @@ -10,30 +10,32 @@ from maya.app.renderSetup.model import renderSetup class MayaLegacyConvertor(SubsetConvertorPlugin, plugin.MayaCreatorBase): - """Find and convert any legacy subsets in the scene. + """Find and convert any legacy products in the scene. - This Convertor will find all legacy subsets in the scene and will - transform them to the current system. Since the old subsets doesn't + This Convertor will find all legacy products in the scene and will + transform them to the current system. Since the old products doesn't retain any information about their original creators, the only mapping we can do is based on their families. - Its limitation is that you can have multiple creators creating subset - of the same family and there is no way to handle it. This code should + Its limitation is that you can have multiple creators creating product + of the same type and there is no way to handle it. This code should nevertheless cover all creators that came with OpenPype. """ identifier = "io.openpype.creators.maya.legacy" - # Cases where the identifier or new family doesn't correspond to the + # Cases where the identifier or new product type doesn't correspond to the # original family on the legacy instances - special_family_conversions = { + product_type_mapping = { "rendering": "io.openpype.creators.maya.renderlayer", } def find_instances(self): - self.cache_subsets(self.collection_shared_data) - legacy = self.collection_shared_data.get("maya_cached_legacy_subsets") + self.cache_instance_data(self.collection_shared_data) + legacy = self.collection_shared_data.get( + "maya_cached_legacy_instances" + ) if not legacy: return @@ -45,43 +47,43 @@ class MayaLegacyConvertor(SubsetConvertorPlugin, # We can't use the collected shared data cache here # we re-query it here directly to convert all found. cache = {} - self.cache_subsets(cache) - legacy = cache.get("maya_cached_legacy_subsets") + self.cache_instance_data(cache) + legacy = cache.get("maya_cached_legacy_instances") if not legacy: return # From all current new style manual creators find the mapping - # from family to identifier - family_to_id = {} + # from product type to identifier + product_type_to_id = {} for identifier, creator in self.create_context.creators.items(): - family = getattr(creator, "family", None) - if not family: + product_type = getattr(creator, "product_type", None) + if not product_type: continue - if family in family_to_id: - # We have a clash of family -> identifier. Multiple - # new style creators use the same family - self.log.warning("Clash on family->identifier: " - "{}".format(identifier)) - family_to_id[family] = identifier + if product_type in product_type_to_id: + # We have a clash of product type -> identifier. Multiple + # new style creators use the same product type + self.log.warning( + "Clash on product type->identifier: {}".format(identifier) + ) + product_type_to_id[product_type] = identifier - family_to_id.update(self.special_family_conversions) + product_type_to_id.update(self.product_type_mapping) # We also embed the current 'task' into the instance since legacy # instances didn't store that data on the instances. The old style # logic was thus to be live to the current task to begin with. data = dict() data["task"] = self.create_context.get_current_task_name() - for family, instance_nodes in legacy.items(): - if family not in family_to_id: - self.log.warning( + for product_type, instance_nodes in legacy.items(): + if product_type not in product_type_to_id: + self.log.warning(( "Unable to convert legacy instance with family '{}'" - " because there is no matching new creator's family" - "".format(family) - ) + " because there is no matching new creator" + ).format(product_type)) continue - creator_id = family_to_id[family] + creator_id = product_type_to_id[family] creator = self.create_context.creators[creator_id] data["creator_identifier"] = creator_id @@ -133,21 +135,21 @@ class MayaLegacyConvertor(SubsetConvertorPlugin, # Copy the attributes of the original instance to the new node original_data = read(instance_node) - # The family gets converted to the new family (this is due to - # "rendering" family being converted to "renderlayer" family) - original_data["family"] = creator.family + # The product type gets converted to the new product type (this + # is due to "rendering" being converted to "renderlayer") + original_data["productType"] = creator.product_type - # recreate subset name as without it would be + # recreate product name as without it would be # `renderingMain` vs correct `renderMain` project_name = self.create_context.get_current_project_name() asset_doc = get_asset_by_name(project_name, original_data["asset"]) - subset_name = creator.get_subset_name( + product_name = creator.get_product_name( original_data["variant"], data["task"], asset_doc, project_name) - original_data["subset"] = subset_name + original_data["productName"] = product_name # Convert to creator attributes when relevant creator_attributes = {} diff --git a/client/ayon_core/hosts/maya/plugins/create/create_animation.py b/client/ayon_core/hosts/maya/plugins/create/create_animation.py index e6849b4468..f30d9aba81 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_animation.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_animation.py @@ -18,7 +18,7 @@ class CreateAnimation(plugin.MayaHiddenCreator): identifier = "io.openpype.creators.maya.animation" name = "animationDefault" label = "Animation" - family = "animation" + product_type = "animation" icon = "male" write_color_sets = False diff --git a/client/ayon_core/hosts/maya/plugins/create/create_arnold_scene_source.py b/client/ayon_core/hosts/maya/plugins/create/create_arnold_scene_source.py index a9455620b8..dc0ffb02c1 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_arnold_scene_source.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_arnold_scene_source.py @@ -13,7 +13,7 @@ class CreateArnoldSceneSource(plugin.MayaCreator): identifier = "io.openpype.creators.maya.ass" label = "Arnold Scene Source" - family = "ass" + product_type = "ass" icon = "cube" settings_name = "CreateAss" @@ -87,12 +87,12 @@ class CreateArnoldSceneSource(plugin.MayaCreator): return defs - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): from maya import cmds instance = super(CreateArnoldSceneSource, self).create( - subset_name, instance_data, pre_create_data + product_name, instance_data, pre_create_data ) instance_node = instance.get("instance_node") diff --git a/client/ayon_core/hosts/maya/plugins/create/create_assembly.py b/client/ayon_core/hosts/maya/plugins/create/create_assembly.py index 2b78271a49..92df125748 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_assembly.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_assembly.py @@ -6,5 +6,5 @@ class CreateAssembly(plugin.MayaCreator): identifier = "io.openpype.creators.maya.assembly" label = "Assembly" - family = "assembly" + product_type = "assembly" icon = "cubes" diff --git a/client/ayon_core/hosts/maya/plugins/create/create_camera.py b/client/ayon_core/hosts/maya/plugins/create/create_camera.py index 37d5a817a5..4b1265bd3b 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_camera.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_camera.py @@ -10,7 +10,7 @@ class CreateCamera(plugin.MayaCreator): identifier = "io.openpype.creators.maya.camera" label = "Camera" - family = "camera" + product_type = "camera" icon = "video-camera" def get_instance_attr_defs(self): @@ -32,5 +32,5 @@ class CreateCameraRig(plugin.MayaCreator): identifier = "io.openpype.creators.maya.camerarig" label = "Camera Rig" - family = "camerarig" + product_type = "camerarig" icon = "video-camera" diff --git a/client/ayon_core/hosts/maya/plugins/create/create_layout.py b/client/ayon_core/hosts/maya/plugins/create/create_layout.py index bd61fa44c6..6cbc697502 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_layout.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_layout.py @@ -7,7 +7,7 @@ class CreateLayout(plugin.MayaCreator): identifier = "io.openpype.creators.maya.layout" label = "Layout" - family = "layout" + product_type = "layout" icon = "cubes" def get_instance_attr_defs(self): diff --git a/client/ayon_core/hosts/maya/plugins/create/create_look.py b/client/ayon_core/hosts/maya/plugins/create/create_look.py index 4655ec1377..ac3625c38f 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_look.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_look.py @@ -13,7 +13,7 @@ class CreateLook(plugin.MayaCreator): identifier = "io.openpype.creators.maya.look" label = "Look" - family = "look" + product_type = "look" icon = "paint-brush" make_tx = True diff --git a/client/ayon_core/hosts/maya/plugins/create/create_matchmove.py b/client/ayon_core/hosts/maya/plugins/create/create_matchmove.py index 00de553404..44443a8b9f 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_matchmove.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_matchmove.py @@ -15,7 +15,7 @@ class CreateMatchmove(plugin.MayaCreator): identifier = "io.openpype.creators.maya.matchmove" label = "Matchmove" - family = "matchmove" + product_type = "matchmove" icon = "video-camera" def get_instance_attr_defs(self): diff --git a/client/ayon_core/hosts/maya/plugins/create/create_maya_usd.py b/client/ayon_core/hosts/maya/plugins/create/create_maya_usd.py index f6c8a55e68..3f34a541b4 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_maya_usd.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_maya_usd.py @@ -13,7 +13,7 @@ class CreateMayaUsd(plugin.MayaCreator): identifier = "io.openpype.creators.maya.mayausd" label = "Maya USD" - family = "usd" + product_type = "usd" icon = "cubes" description = "Create Maya USD Export" diff --git a/client/ayon_core/hosts/maya/plugins/create/create_mayascene.py b/client/ayon_core/hosts/maya/plugins/create/create_mayascene.py index c4024d3710..cfe46336a2 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_mayascene.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_mayascene.py @@ -7,5 +7,5 @@ class CreateMayaScene(plugin.MayaCreator): identifier = "io.openpype.creators.maya.mayascene" name = "mayaScene" label = "Maya Scene" - family = "mayaScene" + product_type = "mayaScene" icon = "file-archive-o" diff --git a/client/ayon_core/hosts/maya/plugins/create/create_model.py b/client/ayon_core/hosts/maya/plugins/create/create_model.py index 67e6b87190..b47df421f3 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_model.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_model.py @@ -10,7 +10,7 @@ class CreateModel(plugin.MayaCreator): identifier = "io.openpype.creators.maya.model" label = "Model" - family = "model" + product_type = "model" icon = "cube" default_variants = ["Main", "Proxy", "_MD", "_HD", "_LD"] diff --git a/client/ayon_core/hosts/maya/plugins/create/create_multishot_layout.py b/client/ayon_core/hosts/maya/plugins/create/create_multishot_layout.py index d05c5ae9a1..8b03f4ef98 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_multishot_layout.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_multishot_layout.py @@ -27,7 +27,7 @@ class CreateMultishotLayout(plugin.MayaCreator): """ identifier = "io.openpype.creators.maya.multishotlayout" label = "Multi-shot Layout" - family = "layout" + product_type = "layout" icon = "project-diagram" def get_pre_create_attr_defs(self): @@ -106,7 +106,7 @@ class CreateMultishotLayout(plugin.MayaCreator): default="layout"), ] - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): shots = list( self.get_related_shots(folder_path=pre_create_data["shotParent"]) ) @@ -164,7 +164,7 @@ class CreateMultishotLayout(plugin.MayaCreator): instance_data["task"] = layout_task layout_creator.create( - subset_name=layout_creator.get_subset_name( + product_name=layout_creator.get_product_name( layout_creator.get_default_variant(), self.create_context.get_current_task_name(), asset_doc, diff --git a/client/ayon_core/hosts/maya/plugins/create/create_multiverse_look.py b/client/ayon_core/hosts/maya/plugins/create/create_multiverse_look.py index 11e13b2748..de604a33b3 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_multiverse_look.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_multiverse_look.py @@ -10,7 +10,7 @@ class CreateMultiverseLook(plugin.MayaCreator): identifier = "io.openpype.creators.maya.mvlook" label = "Multiverse Look" - family = "mvLook" + product_type = "mvLook" icon = "cubes" def get_instance_attr_defs(self): diff --git a/client/ayon_core/hosts/maya/plugins/create/create_multiverse_usd.py b/client/ayon_core/hosts/maya/plugins/create/create_multiverse_usd.py index af0ffa9f23..668700995f 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_multiverse_usd.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_multiverse_usd.py @@ -12,7 +12,7 @@ class CreateMultiverseUsd(plugin.MayaCreator): identifier = "io.openpype.creators.maya.mvusdasset" label = "Multiverse USD Asset" - family = "usd" + product_type = "usd" icon = "cubes" description = "Create Multiverse USD Asset" diff --git a/client/ayon_core/hosts/maya/plugins/create/create_multiverse_usd_comp.py b/client/ayon_core/hosts/maya/plugins/create/create_multiverse_usd_comp.py index 202fbbcbc8..120e6ad920 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_multiverse_usd_comp.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_multiverse_usd_comp.py @@ -11,7 +11,7 @@ class CreateMultiverseUsdComp(plugin.MayaCreator): identifier = "io.openpype.creators.maya.mvusdcomposition" label = "Multiverse USD Composition" - family = "mvUsdComposition" + product_type = "mvUsdComposition" icon = "cubes" def get_instance_attr_defs(self): diff --git a/client/ayon_core/hosts/maya/plugins/create/create_multiverse_usd_over.py b/client/ayon_core/hosts/maya/plugins/create/create_multiverse_usd_over.py index cca2b54392..26208794e3 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_multiverse_usd_over.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_multiverse_usd_over.py @@ -11,7 +11,7 @@ class CreateMultiverseUsdOver(plugin.MayaCreator): identifier = "io.openpype.creators.maya.mvusdoverride" label = "Multiverse USD Override" - family = "mvUsdOverride" + product_type = "mvUsdOverride" icon = "cubes" def get_instance_attr_defs(self): diff --git a/client/ayon_core/hosts/maya/plugins/create/create_pointcache.py b/client/ayon_core/hosts/maya/plugins/create/create_pointcache.py index 832e0bfbc5..05e3a1a29f 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_pointcache.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_pointcache.py @@ -15,7 +15,7 @@ class CreatePointCache(plugin.MayaCreator): identifier = "io.openpype.creators.maya.pointcache" label = "Pointcache" - family = "pointcache" + product_type = "pointcache" icon = "gears" write_color_sets = False write_face_sets = False @@ -76,10 +76,10 @@ class CreatePointCache(plugin.MayaCreator): return defs - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): instance = super(CreatePointCache, self).create( - subset_name, instance_data, pre_create_data + product_name, instance_data, pre_create_data ) instance_node = instance.get("instance_node") diff --git a/client/ayon_core/hosts/maya/plugins/create/create_proxy_abc.py b/client/ayon_core/hosts/maya/plugins/create/create_proxy_abc.py index 8b8cedd7ab..ecc031436c 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_proxy_abc.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_proxy_abc.py @@ -13,7 +13,7 @@ class CreateProxyAlembic(plugin.MayaCreator): identifier = "io.openpype.creators.maya.proxyabc" label = "Proxy Alembic" - family = "proxyAbc" + product_type = "proxyAbc" icon = "gears" write_color_sets = False write_face_sets = False diff --git a/client/ayon_core/hosts/maya/plugins/create/create_redshift_proxy.py b/client/ayon_core/hosts/maya/plugins/create/create_redshift_proxy.py index 72c86a0b74..d99fe5a787 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_redshift_proxy.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_redshift_proxy.py @@ -1,16 +1,16 @@ # -*- coding: utf-8 -*- -"""Creator of Redshift proxy subset types.""" +"""Creator of Redshift proxy product types.""" from ayon_core.hosts.maya.api import plugin, lib from ayon_core.lib import BoolDef class CreateRedshiftProxy(plugin.MayaCreator): - """Create instance of Redshift Proxy subset.""" + """Create instance of Redshift Proxy product.""" identifier = "io.openpype.creators.maya.redshiftproxy" label = "Redshift Proxy" - family = "redshiftproxy" + product_type = "redshiftproxy" icon = "gears" def get_instance_attr_defs(self): diff --git a/client/ayon_core/hosts/maya/plugins/create/create_render.py b/client/ayon_core/hosts/maya/plugins/create/create_render.py index 4481836c89..213d5b543e 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_render.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_render.py @@ -13,7 +13,7 @@ from ayon_core.lib import ( class CreateRenderlayer(plugin.RenderlayerCreator): - """Create and manages renderlayer subset per renderLayer in workfile. + """Create and manages renderlayer product per renderLayer in workfile. This generates a single node in the scene which tells the Creator to if it exists collect Maya rendersetup renderlayers as individual instances. @@ -24,7 +24,7 @@ class CreateRenderlayer(plugin.RenderlayerCreator): """ identifier = "io.openpype.creators.maya.renderlayer" - family = "renderlayer" + product_type = "renderlayer" label = "Render" icon = "eye" @@ -37,7 +37,7 @@ class CreateRenderlayer(plugin.RenderlayerCreator): def apply_settings(cls, project_settings): cls.render_settings = project_settings["maya"]["render_settings"] - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): # Only allow a single render instance to exist if self._get_singleton_node(): raise CreatorError("A Render instance already exists - only " @@ -47,7 +47,7 @@ class CreateRenderlayer(plugin.RenderlayerCreator): if self.render_settings.get("apply_render_settings"): lib_rendersettings.RenderSettings().set_default_renderer_settings() - super(CreateRenderlayer, self).create(subset_name, + super(CreateRenderlayer, self).create(product_name, instance_data, pre_create_data) diff --git a/client/ayon_core/hosts/maya/plugins/create/create_rendersetup.py b/client/ayon_core/hosts/maya/plugins/create/create_rendersetup.py index dc47325a34..3d8d6a7309 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_rendersetup.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_rendersetup.py @@ -7,18 +7,18 @@ class CreateRenderSetup(plugin.MayaCreator): identifier = "io.openpype.creators.maya.rendersetup" label = "Render Setup Preset" - family = "rendersetup" + product_type = "rendersetup" icon = "tablet" def get_pre_create_attr_defs(self): # Do not show the "use_selection" setting from parent class return [] - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): existing_instance = None for instance in self.create_context.instances: - if instance.family == self.family: + if instance.product_type == self.product_type: existing_instance = instance break @@ -26,6 +26,6 @@ class CreateRenderSetup(plugin.MayaCreator): raise CreatorError("A RenderSetup instance already exists - only " "one can be configured.") - super(CreateRenderSetup, self).create(subset_name, + super(CreateRenderSetup, self).create(product_name, instance_data, pre_create_data) diff --git a/client/ayon_core/hosts/maya/plugins/create/create_review.py b/client/ayon_core/hosts/maya/plugins/create/create_review.py index 3e75b52556..c4fa045427 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_review.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_review.py @@ -29,14 +29,14 @@ class CreateReview(plugin.MayaCreator): identifier = "io.openpype.creators.maya.review" label = "Review" - family = "review" + product_type = "review" icon = "video-camera" useMayaTimeline = True panZoom = False # Overriding "create" method to prefill values from settings. - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): members = list() if pre_create_data.get("use_selection"): @@ -49,7 +49,7 @@ class CreateReview(plugin.MayaCreator): preset = lib.get_capture_preset( task_name, asset_doc["data"]["tasks"][task_name]["type"], - subset_name, + product_name, self.project_settings, self.log ) @@ -60,11 +60,11 @@ class CreateReview(plugin.MayaCreator): ) with lib.undo_chunk(): - instance_node = cmds.sets(members, name=subset_name) + instance_node = cmds.sets(members, name=product_name) instance_data["instance_node"] = instance_node instance = CreatedInstance( - self.family, - subset_name, + self.product_type, + product_name, instance_data, self) diff --git a/client/ayon_core/hosts/maya/plugins/create/create_rig.py b/client/ayon_core/hosts/maya/plugins/create/create_rig.py index e49e3040ba..54be50c169 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_rig.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_rig.py @@ -8,12 +8,12 @@ class CreateRig(plugin.MayaCreator): identifier = "io.openpype.creators.maya.rig" label = "Rig" - family = "rig" + product_type = "rig" icon = "wheelchair" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): - instance = super(CreateRig, self).create(subset_name, + instance = super(CreateRig, self).create(product_name, instance_data, pre_create_data) @@ -21,12 +21,12 @@ class CreateRig(plugin.MayaCreator): self.log.info("Creating Rig instance set up ...") # TODO:change name (_controls_SET -> _rigs_SET) - controls = cmds.sets(name=subset_name + "_controls_SET", empty=True) + controls = cmds.sets(name=product_name + "_controls_SET", empty=True) # TODO:change name (_out_SET -> _geo_SET) - pointcache = cmds.sets(name=subset_name + "_out_SET", empty=True) + pointcache = cmds.sets(name=product_name + "_out_SET", empty=True) skeleton = cmds.sets( - name=subset_name + "_skeletonAnim_SET", empty=True) + name=product_name + "_skeletonAnim_SET", empty=True) skeleton_mesh = cmds.sets( - name=subset_name + "_skeletonMesh_SET", empty=True) + name=product_name + "_skeletonMesh_SET", empty=True) cmds.sets([controls, pointcache, skeleton, skeleton_mesh], forceElement=instance_node) diff --git a/client/ayon_core/hosts/maya/plugins/create/create_setdress.py b/client/ayon_core/hosts/maya/plugins/create/create_setdress.py index dfc38f5d76..0f72d4d184 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_setdress.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_setdress.py @@ -7,7 +7,7 @@ class CreateSetDress(plugin.MayaCreator): identifier = "io.openpype.creators.maya.setdress" label = "Set Dress" - family = "setdress" + product_type = "setdress" icon = "cubes" default_variants = ["Main", "Anim"] diff --git a/client/ayon_core/hosts/maya/plugins/create/create_unreal_skeletalmesh.py b/client/ayon_core/hosts/maya/plugins/create/create_unreal_skeletalmesh.py index 550a9cdb0f..783d80412b 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_unreal_skeletalmesh.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_unreal_skeletalmesh.py @@ -14,9 +14,8 @@ class CreateUnrealSkeletalMesh(plugin.MayaCreator): identifier = "io.openpype.creators.maya.unrealskeletalmesh" label = "Unreal - Skeletal Mesh" - family = "skeletalMesh" + product_type = "skeletalMesh" icon = "thumbs-up" - dynamic_subset_keys = ["asset"] # Defined in settings joint_hints = set() @@ -32,7 +31,7 @@ class CreateUnrealSkeletalMesh(plugin.MayaCreator): self, variant, task_name, asset_doc, project_name, host_name, instance ): """ - The default subset name templates for Unreal include {asset} and thus + The default product name templates for Unreal include {asset} and thus we should pass that along as dynamic data. """ dynamic_data = super(CreateUnrealSkeletalMesh, self).get_dynamic_data( @@ -41,11 +40,11 @@ class CreateUnrealSkeletalMesh(plugin.MayaCreator): dynamic_data["asset"] = asset_doc["name"] return dynamic_data - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): with lib.undo_chunk(): instance = super(CreateUnrealSkeletalMesh, self).create( - subset_name, instance_data, pre_create_data) + product_name, instance_data, pre_create_data) instance_node = instance.get("instance_node") # We reorganize the geometry that was originally added into the diff --git a/client/ayon_core/hosts/maya/plugins/create/create_unreal_staticmesh.py b/client/ayon_core/hosts/maya/plugins/create/create_unreal_staticmesh.py index d1fac03bdf..4172168497 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_unreal_staticmesh.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_unreal_staticmesh.py @@ -9,9 +9,8 @@ class CreateUnrealStaticMesh(plugin.MayaCreator): identifier = "io.openpype.creators.maya.unrealstaticmesh" label = "Unreal - Static Mesh" - family = "staticMesh" + product_type = "staticMesh" icon = "cube" - dynamic_subset_keys = ["asset"] # Defined in settings collision_prefixes = [] @@ -25,7 +24,7 @@ class CreateUnrealStaticMesh(plugin.MayaCreator): self, variant, task_name, asset_doc, project_name, host_name, instance ): """ - The default subset name templates for Unreal include {asset} and thus + The default product name templates for Unreal include {asset} and thus we should pass that along as dynamic data. """ dynamic_data = super(CreateUnrealStaticMesh, self).get_dynamic_data( @@ -34,11 +33,11 @@ class CreateUnrealStaticMesh(plugin.MayaCreator): dynamic_data["asset"] = asset_doc["name"] return dynamic_data - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): with lib.undo_chunk(): instance = super(CreateUnrealStaticMesh, self).create( - subset_name, instance_data, pre_create_data) + product_name, instance_data, pre_create_data) instance_node = instance.get("instance_node") # We reorganize the geometry that was originally added into the diff --git a/client/ayon_core/hosts/maya/plugins/create/create_unreal_yeticache.py b/client/ayon_core/hosts/maya/plugins/create/create_unreal_yeticache.py index 4cd7288cfc..1eac8a5ea9 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_unreal_yeticache.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_unreal_yeticache.py @@ -10,7 +10,7 @@ class CreateYetiCache(plugin.MayaCreator): identifier = "io.openpype.creators.maya.unrealyeticache" label = "Unreal - Yeti Cache" - family = "yeticacheUE" + product_type = "yeticacheUE" icon = "pagelines" def get_instance_attr_defs(self): diff --git a/client/ayon_core/hosts/maya/plugins/create/create_vrayproxy.py b/client/ayon_core/hosts/maya/plugins/create/create_vrayproxy.py index 7d16c5bc2c..d565ec37e0 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_vrayproxy.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_vrayproxy.py @@ -10,7 +10,7 @@ class CreateVrayProxy(plugin.MayaCreator): identifier = "io.openpype.creators.maya.vrayproxy" label = "VRay Proxy" - family = "vrayproxy" + product_type = "vrayproxy" icon = "gears" vrmesh = True diff --git a/client/ayon_core/hosts/maya/plugins/create/create_vrayscene.py b/client/ayon_core/hosts/maya/plugins/create/create_vrayscene.py index bc8110eff1..cf5e7b5364 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_vrayscene.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_vrayscene.py @@ -14,7 +14,7 @@ class CreateVRayScene(plugin.RenderlayerCreator): identifier = "io.openpype.creators.maya.vrayscene" - family = "vrayscene" + product_type = "vrayscene" label = "VRay Scene" icon = "cubes" @@ -25,13 +25,13 @@ class CreateVRayScene(plugin.RenderlayerCreator): def apply_settings(cls, project_settings): cls.render_settings = project_settings["maya"]["render_settings"] - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): # Only allow a single render instance to exist if self._get_singleton_node(): raise CreatorError("A Render instance already exists - only " "one can be configured.") - super(CreateVRayScene, self).create(subset_name, + super(CreateVRayScene, self).create(product_name, instance_data, pre_create_data) diff --git a/client/ayon_core/hosts/maya/plugins/create/create_workfile.py b/client/ayon_core/hosts/maya/plugins/create/create_workfile.py index 396ad6ffbb..81afd65119 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_workfile.py @@ -10,7 +10,7 @@ class CreateWorkfile(plugin.MayaCreatorBase, AutoCreator): """Workfile auto-creator.""" identifier = "io.openpype.creators.maya.workfile" label = "Workfile" - family = "workfile" + product_type = "workfile" icon = "fa5.file" default_variant = "Main" @@ -36,7 +36,7 @@ class CreateWorkfile(plugin.MayaCreatorBase, AutoCreator): if current_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( variant, task_name, asset_doc, project_name, host_name ) data = { @@ -51,7 +51,7 @@ class CreateWorkfile(plugin.MayaCreatorBase, AutoCreator): ) self.log.info("Auto-creating workfile instance...") current_instance = CreatedInstance( - self.family, subset_name, data, self + self.product_type, product_name, data, self ) self._add_instance_to_context(current_instance) elif ( @@ -60,19 +60,21 @@ class CreateWorkfile(plugin.MayaCreatorBase, AutoCreator): ): # Update instance context if is not the same asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( variant, task_name, asset_doc, project_name, host_name ) asset_name = get_asset_name_identifier(asset_doc) current_instance["folderPath"] = asset_name current_instance["task"] = task_name - current_instance["subset"] = subset_name + current_instance["productName"] = product_name def collect_instances(self): - self.cache_subsets(self.collection_shared_data) - cached_subsets = self.collection_shared_data["maya_cached_subsets"] - for node in cached_subsets.get(self.identifier, []): + self.cache_instance_data(self.collection_shared_data) + cached_instances = ( + self.collection_shared_data["maya_cached_instance_data"] + ) + for node in cached_instances.get(self.identifier, []): node_data = self.read_instance_node(node) created_instance = CreatedInstance.from_existing(node_data, self) diff --git a/client/ayon_core/hosts/maya/plugins/create/create_xgen.py b/client/ayon_core/hosts/maya/plugins/create/create_xgen.py index 4e0d41b689..fec2f07456 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_xgen.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_xgen.py @@ -6,5 +6,5 @@ class CreateXgen(plugin.MayaCreator): identifier = "io.openpype.creators.maya.xgen" label = "Xgen" - family = "xgen" + product_type = "xgen" icon = "pagelines" diff --git a/client/ayon_core/hosts/maya/plugins/create/create_yeti_cache.py b/client/ayon_core/hosts/maya/plugins/create/create_yeti_cache.py index 82b18f113a..bf20acaca8 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_yeti_cache.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_yeti_cache.py @@ -10,7 +10,7 @@ class CreateYetiCache(plugin.MayaCreator): identifier = "io.openpype.creators.maya.yeticache" label = "Yeti Cache" - family = "yeticache" + product_type = "yeticache" icon = "pagelines" def get_instance_attr_defs(self): diff --git a/client/ayon_core/hosts/maya/plugins/create/create_yeti_rig.py b/client/ayon_core/hosts/maya/plugins/create/create_yeti_rig.py index df3c89a64d..dfe224ceb1 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_yeti_rig.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_yeti_rig.py @@ -11,13 +11,13 @@ class CreateYetiRig(plugin.MayaCreator): identifier = "io.openpype.creators.maya.yetirig" label = "Yeti Rig" - family = "yetiRig" + product_type = "yetiRig" icon = "usb" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): with lib.undo_chunk(): - instance = super(CreateYetiRig, self).create(subset_name, + instance = super(CreateYetiRig, self).create(product_name, instance_data, pre_create_data) instance_node = instance.get("instance_node") diff --git a/client/ayon_core/hosts/maya/plugins/inventory/connect_geometry.py b/client/ayon_core/hosts/maya/plugins/inventory/connect_geometry.py index 13c9de4693..054c84bea2 100644 --- a/client/ayon_core/hosts/maya/plugins/inventory/connect_geometry.py +++ b/client/ayon_core/hosts/maya/plugins/inventory/connect_geometry.py @@ -9,7 +9,7 @@ class ConnectGeometry(InventoryAction): Source container will connect to the target containers, by searching for matching geometry IDs (cbid). - Source containers are of family; "animation" and "pointcache". + Source containers are of product type: "animation" and "pointcache". The connection with be done with a live world space blendshape. """ @@ -27,19 +27,19 @@ class ConnectGeometry(InventoryAction): return # Categorize containers by family. - containers_by_family = {} + containers_by_product_type = {} for container in containers: - family = get_representation_context( + product_type = get_representation_context( container["representation"] )["subset"]["data"]["family"] try: - containers_by_family[family].append(container) + containers_by_product_type[product_type].append(container) except KeyError: - containers_by_family[family] = [container] + containers_by_product_type[product_type] = [container] # Validate to only 1 source container. - source_containers = containers_by_family.get("animation", []) - source_containers += containers_by_family.get("pointcache", []) + source_containers = containers_by_product_type.get("animation", []) + source_containers += containers_by_product_type.get("pointcache", []) source_container_namespaces = [ x["namespace"] for x in source_containers ] @@ -57,8 +57,8 @@ class ConnectGeometry(InventoryAction): # Collect matching geometry transforms based cbId attribute. target_containers = [] - for family, containers in containers_by_family.items(): - if family in ["animation", "pointcache"]: + for product_type, containers in containers_by_product_type.items(): + if product_type in ["animation", "pointcache"]: continue target_containers.extend(containers) diff --git a/client/ayon_core/hosts/maya/plugins/inventory/connect_xgen.py b/client/ayon_core/hosts/maya/plugins/inventory/connect_xgen.py index 2a198addf2..fa6440fc37 100644 --- a/client/ayon_core/hosts/maya/plugins/inventory/connect_xgen.py +++ b/client/ayon_core/hosts/maya/plugins/inventory/connect_xgen.py @@ -23,20 +23,20 @@ class ConnectXgen(InventoryAction): self.display_warning(message) return - # Categorize containers by family. - containers_by_family = {} + # Categorize containers by product type. + containers_by_product_type = {} for container in containers: - family = get_representation_context( + product_type = get_representation_context( container["representation"] )["subset"]["data"]["family"] try: - containers_by_family[family].append(container) + containers_by_product_type[product_type].append(container) except KeyError: - containers_by_family[family] = [container] + containers_by_product_type[product_type] = [container] # Validate to only 1 source container. - source_containers = containers_by_family.get("animation", []) - source_containers += containers_by_family.get("pointcache", []) + source_containers = containers_by_product_type.get("animation", []) + source_containers += containers_by_product_type.get("pointcache", []) source_container_namespaces = [ x["namespace"] for x in source_containers ] @@ -68,8 +68,8 @@ class ConnectXgen(InventoryAction): # Target containers. target_containers = [] - for family, containers in containers_by_family.items(): - if family in ["animation", "pointcache"]: + for product_type, containers in containers_by_product_type.items(): + if product_type in ["animation", "pointcache"]: continue target_containers.extend(containers) diff --git a/client/ayon_core/hosts/maya/plugins/inventory/connect_yeti_rig.py b/client/ayon_core/hosts/maya/plugins/inventory/connect_yeti_rig.py index 19498e5c1c..66807e9d5d 100644 --- a/client/ayon_core/hosts/maya/plugins/inventory/connect_yeti_rig.py +++ b/client/ayon_core/hosts/maya/plugins/inventory/connect_yeti_rig.py @@ -26,17 +26,17 @@ class ConnectYetiRig(InventoryAction): self.display_warning(message) return - # Categorize containers by family. - containers_by_family = defaultdict(list) + # Categorize containers by product type. + containers_by_product_type = defaultdict(list) for container in containers: - family = get_representation_context( + product_type = get_representation_context( container["representation"] )["subset"]["data"]["family"] - containers_by_family[family].append(container) + containers_by_product_type[product_type].append(container) # Validate to only 1 source container. - source_containers = containers_by_family.get("animation", []) - source_containers += containers_by_family.get("pointcache", []) + source_containers = containers_by_product_type.get("animation", []) + source_containers += containers_by_product_type.get("pointcache", []) source_container_namespaces = [ x["namespace"] for x in source_containers ] @@ -57,7 +57,7 @@ class ConnectYetiRig(InventoryAction): target_ids = {} inputs = [] - yeti_rig_containers = containers_by_family.get("yetiRig") + yeti_rig_containers = containers_by_product_type.get("yetiRig") if not yeti_rig_containers: self.display_warning( "Select at least one yetiRig container" diff --git a/client/ayon_core/hosts/maya/plugins/load/_load_animation.py b/client/ayon_core/hosts/maya/plugins/load/_load_animation.py index bf7f3859e1..e6dc1e520a 100644 --- a/client/ayon_core/hosts/maya/plugins/load/_load_animation.py +++ b/client/ayon_core/hosts/maya/plugins/load/_load_animation.py @@ -7,7 +7,7 @@ def _process_reference(file_url, name, namespace, options): Args: file_url (str): fileapth of the objects to be loaded - name (str): subset name + name (str): product name namespace (str): namespace options (dict): dict of storing the param @@ -16,7 +16,7 @@ def _process_reference(file_url, name, namespace, options): """ from ayon_core.hosts.maya.api.lib import unique_namespace # Get name from asset being loaded - # Assuming name is subset name from the animation, we split the number + # Assuming name is product name from the animation, we split the number # suffix from the name to ensure the namespace is unique name = name.split("_")[0] ext = file_url.split(".")[-1] diff --git a/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py b/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py index 16ac460cf7..312cc3bd6b 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py @@ -15,7 +15,7 @@ from ayon_core.hosts.maya.api.lib import ( convert_to_maya_fps ) from ayon_core.hosts.maya.api.pipeline import containerise -from ayon_core.hosts.maya.api.plugin import get_load_color_for_family +from ayon_core.hosts.maya.api.plugin import get_load_color_for_product_type def is_sequence(files): @@ -67,7 +67,7 @@ class ArnoldStandinLoader(load.LoaderPlugin): # Set color. settings = get_project_settings(context["project"]["name"]) - color = get_load_color_for_family("ass", settings) + color = get_load_color_for_product_type("ass", settings) if color is not None: red, green, blue = color cmds.setAttr(root + ".useOutlinerColor", True) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_gpucache.py b/client/ayon_core/hosts/maya/plugins/load/load_gpucache.py index cdaaeeae6a..38f9d1b7cb 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_gpucache.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_gpucache.py @@ -9,7 +9,7 @@ from ayon_core.pipeline import ( get_representation_path ) from ayon_core.settings import get_project_settings -from ayon_core.hosts.maya.api.plugin import get_load_color_for_family +from ayon_core.hosts.maya.api.plugin import get_load_color_for_product_type class GpuCacheLoader(load.LoaderPlugin): @@ -40,7 +40,7 @@ class GpuCacheLoader(load.LoaderPlugin): project_name = context["project"]["name"] settings = get_project_settings(project_name) - color = get_load_color_for_family("model", settings) + color = get_load_color_for_product_type("model", settings) if color is not None: red, green, blue = color cmds.setAttr(root + ".useOutlinerColor", 1) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_image_plane.py b/client/ayon_core/hosts/maya/plugins/load/load_image_plane.py index fb27e6597a..a685a4a41c 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_image_plane.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_image_plane.py @@ -225,14 +225,14 @@ class ImagePlaneLoader(load.LoaderPlugin): version = get_version_by_id( project_name, representation["parent"], fields=["parent"] ) - subset = get_subset_by_id( + subset_doc = get_subset_by_id( project_name, version["parent"], fields=["parent"] ) - asset = get_asset_by_id( - project_name, subset["parent"], fields=["parent"] + asset_doc = get_asset_by_id( + project_name, subset_doc["parent"], fields=["parent"] ) - start_frame = asset["data"]["frameStart"] - end_frame = asset["data"]["frameEnd"] + start_frame = asset_doc["data"]["frameStart"] + end_frame = asset_doc["data"]["frameEnd"] for attr, value in { "frameOffset": 0, diff --git a/client/ayon_core/hosts/maya/plugins/load/load_redshift_proxy.py b/client/ayon_core/hosts/maya/plugins/load/load_redshift_proxy.py index 8910d0fcd0..eb7e0957ac 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_redshift_proxy.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_redshift_proxy.py @@ -16,7 +16,7 @@ from ayon_core.hosts.maya.api.lib import ( unique_namespace ) from ayon_core.hosts.maya.api.pipeline import containerise -from ayon_core.hosts.maya.api.plugin import get_load_color_for_family +from ayon_core.hosts.maya.api.plugin import get_load_color_for_product_type class RedshiftProxyLoader(load.LoaderPlugin): @@ -33,9 +33,9 @@ class RedshiftProxyLoader(load.LoaderPlugin): def load(self, context, name=None, namespace=None, options=None): """Plugin entry point.""" try: - family = context["representation"]["context"]["family"] + product_type = context["representation"]["context"]["family"] except ValueError: - family = "redshiftproxy" + product_type = "redshiftproxy" asset_name = context['asset']["name"] namespace = namespace or unique_namespace( @@ -60,7 +60,7 @@ class RedshiftProxyLoader(load.LoaderPlugin): # colour the group node project_name = context["project"]["name"] settings = get_project_settings(project_name) - color = get_load_color_for_family(family, settings) + color = get_load_color_for_product_type(product_type, settings) if color is not None: red, green, blue = color cmds.setAttr("{0}.useOutlinerColor".format(group_node), 1) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_reference.py b/client/ayon_core/hosts/maya/plugins/load/load_reference.py index 75f42a9fe6..eee3d92641 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_reference.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_reference.py @@ -116,9 +116,9 @@ class ReferenceLoader(plugin.ReferenceLoader): import maya.cmds as cmds try: - family = context["representation"]["context"]["family"] + product_type = context["representation"]["context"]["family"] except ValueError: - family = "model" + product_type = "model" project_name = context["project"]["name"] # True by default to keep legacy behaviours @@ -169,8 +169,9 @@ class ReferenceLoader(plugin.ReferenceLoader): children=True, fullPath=True) or [] - if family not in {"layout", "setdress", - "mayaAscii", "mayaScene"}: + if product_type not in { + "layout", "setdress", "mayaAscii", "mayaScene" + }: # QUESTION Why do we need to exclude these families? with parent_nodes(roots, parent=None): cmds.xform(group_name, zeroTransformPivots=True) @@ -184,7 +185,9 @@ class ReferenceLoader(plugin.ReferenceLoader): "{}.displayHandle".format(group_name), display_handle ) - color = plugin.get_load_color_for_family(family, settings) + color = plugin.get_load_color_for_product_type( + product_type, settings + ) if color is not None: red, green, blue = color cmds.setAttr("{}.useOutlinerColor".format(group_name), 1) @@ -215,7 +218,7 @@ class ReferenceLoader(plugin.ReferenceLoader): cmds.setAttr("{}.selectHandleY".format(group_name), cy) cmds.setAttr("{}.selectHandleZ".format(group_name), cz) - if family == "rig": + if product_type == "rig": self._post_process_rig(namespace, context, options) else: if "translate" in options: diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py index c68fddc60a..80a7fa6006 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py @@ -5,7 +5,7 @@ from ayon_core.pipeline import ( load, get_representation_path ) -from ayon_core.hosts.maya.api.plugin import get_load_color_for_family +from ayon_core.hosts.maya.api.plugin import get_load_color_for_product_type # TODO aiVolume doesn't automatically set velocity fps correctly, set manual? @@ -26,9 +26,9 @@ class LoadVDBtoArnold(load.LoaderPlugin): from ayon_core.hosts.maya.api.lib import unique_namespace try: - family = context["representation"]["context"]["family"] + product_type = context["representation"]["context"]["family"] except ValueError: - family = "vdbcache" + product_type = "vdbcache" # Check if the plugin for arnold is available on the pc try: @@ -51,7 +51,7 @@ class LoadVDBtoArnold(load.LoaderPlugin): project_name = context["project"]["name"] settings = get_project_settings(project_name) - color = get_load_color_for_family(family, settings) + color = get_load_color_for_product_type(product_type, settings) if color is not None: red, green, blue = color cmds.setAttr(root + ".useOutlinerColor", 1) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_redshift.py b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_redshift.py index 1bc75ae4c6..65bef51ec6 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_redshift.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_redshift.py @@ -5,7 +5,7 @@ from ayon_core.pipeline import ( load, get_representation_path ) -from ayon_core.hosts.maya.api.plugin import get_load_color_for_family +from ayon_core.hosts.maya.api.plugin import get_load_color_for_product_type class LoadVDBtoRedShift(load.LoaderPlugin): @@ -32,9 +32,9 @@ class LoadVDBtoRedShift(load.LoaderPlugin): from ayon_core.hosts.maya.api.lib import unique_namespace try: - family = context["representation"]["context"]["family"] + product_type = context["representation"]["context"]["family"] except ValueError: - family = "vdbcache" + product_type = "vdbcache" # Check if the plugin for redshift is available on the pc try: @@ -70,7 +70,7 @@ class LoadVDBtoRedShift(load.LoaderPlugin): project_name = context["project"]["name"] settings = get_project_settings(project_name) - color = get_load_color_for_family(family, settings) + color = get_load_color_for_product_type(product_type, settings) if color is not None: red, green, blue = color cmds.setAttr(root + ".useOutlinerColor", 1) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py index 0c87162629..4b18e60c9d 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py @@ -5,7 +5,7 @@ from ayon_core.pipeline import ( load, get_representation_path ) -from ayon_core.hosts.maya.api.plugin import get_load_color_for_family +from ayon_core.hosts.maya.api.plugin import get_load_color_for_product_type from maya import cmds @@ -95,9 +95,9 @@ class LoadVDBtoVRay(load.LoaderPlugin): ) try: - family = context["representation"]["context"]["family"] + product_type = context["representation"]["context"]["family"] except ValueError: - family = "vdbcache" + product_type = "vdbcache" # Ensure V-ray is loaded with the vrayvolumegrid if not cmds.pluginInfo("vrayformaya", query=True, loaded=True): @@ -130,7 +130,7 @@ class LoadVDBtoVRay(load.LoaderPlugin): project_name = context["project"]["name"] settings = get_project_settings(project_name) - color = get_load_color_for_family(family, settings) + color = get_load_color_for_product_type(product_type, settings) if color is not None: red, green, blue = color cmds.setAttr(root + ".useOutlinerColor", 1) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py b/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py index 50b63f4f11..d4aad10762 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py @@ -22,7 +22,7 @@ from ayon_core.hosts.maya.api.lib import ( unique_namespace ) from ayon_core.hosts.maya.api.pipeline import containerise -from ayon_core.hosts.maya.api.plugin import get_load_color_for_family +from ayon_core.hosts.maya.api.plugin import get_load_color_for_product_type class VRayProxyLoader(load.LoaderPlugin): @@ -49,9 +49,9 @@ class VRayProxyLoader(load.LoaderPlugin): """ try: - family = context["representation"]["context"]["family"] + product_type = context["representation"]["context"]["family"] except ValueError: - family = "vrayproxy" + product_type = "vrayproxy" # get all representations for this version filename = self._get_abc(context["version"]["_id"]) @@ -81,7 +81,7 @@ class VRayProxyLoader(load.LoaderPlugin): # colour the group node project_name = context["project"]["name"] settings = get_project_settings(project_name) - color = get_load_color_for_family(family, settings) + color = get_load_color_for_product_type(product_type, settings) if color is not None: red, green, blue = color cmds.setAttr("{0}.useOutlinerColor".format(group_node), 1) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py b/client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py index 7b4edb0567..04ccf57808 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py @@ -11,7 +11,7 @@ from ayon_core.hosts.maya.api.lib import ( unique_namespace ) from ayon_core.hosts.maya.api.pipeline import containerise -from ayon_core.hosts.maya.api.plugin import get_load_color_for_family +from ayon_core.hosts.maya.api.plugin import get_load_color_for_product_type class VRaySceneLoader(load.LoaderPlugin): @@ -26,12 +26,10 @@ class VRaySceneLoader(load.LoaderPlugin): color = "orange" def load(self, context, name, namespace, data): - - try: - family = context["representation"]["context"]["family"] + product_type = context["representation"]["context"]["family"] except ValueError: - family = "vrayscene_layer" + product_type = "vrayscene_layer" asset_name = context['asset']["name"] namespace = namespace or unique_namespace( @@ -58,7 +56,7 @@ class VRaySceneLoader(load.LoaderPlugin): # colour the group node project_name = context["project"]["name"] settings = get_project_settings(project_name) - color = get_load_color_for_family(family, settings) + color = get_load_color_for_product_type(product_type, settings) if color is not None: red, green, blue = color cmds.setAttr("{0}.useOutlinerColor".format(root_node), 1) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py b/client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py index afbb632d87..372727d400 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py @@ -13,7 +13,7 @@ from ayon_core.pipeline import ( ) from ayon_core.hosts.maya.api import lib from ayon_core.hosts.maya.api.pipeline import containerise -from ayon_core.hosts.maya.api.plugin import get_load_color_for_family +from ayon_core.hosts.maya.api.plugin import get_load_color_for_product_type # Do not reset these values on update but only apply on first load @@ -57,9 +57,9 @@ class YetiCacheLoader(load.LoaderPlugin): """ try: - family = context["representation"]["context"]["family"] + product_type = context["representation"]["context"]["family"] except ValueError: - family = "yeticache" + product_type = "yeticache" # Build namespace asset = context["asset"] @@ -82,7 +82,7 @@ class YetiCacheLoader(load.LoaderPlugin): project_name = context["project"]["name"] settings = get_project_settings(project_name) - color = get_load_color_for_family(family, settings) + color = get_load_color_for_product_type(product_type, settings) if color is not None: red, green, blue = color cmds.setAttr(group_node + ".useOutlinerColor", 1) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_yeti_rig.py b/client/ayon_core/hosts/maya/plugins/load/load_yeti_rig.py index e7178be38b..310c943198 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_yeti_rig.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_yeti_rig.py @@ -40,7 +40,7 @@ class YetiRigLoader(plugin.ReferenceLoader): groupName=group_name ) - color = plugin.get_load_color_for_family("yetiRig") + color = plugin.get_load_color_for_product_type("yetiRig") if color is not None: red, green, blue = color cmds.setAttr(group_name + ".useOutlinerColor", 1) diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_animation.py b/client/ayon_core/hosts/maya/plugins/publish/collect_animation.py index 26a0a01c8b..2ab6511ece 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/collect_animation.py +++ b/client/ayon_core/hosts/maya/plugins/publish/collect_animation.py @@ -25,13 +25,14 @@ class CollectAnimationOutputGeometry(pyblish.api.InstancePlugin): def process(self, instance): """Collect the hierarchy nodes""" - family = instance.data["family"] + product_type = instance.data["productType"] out_set = next((i for i in instance.data["setMembers"] if i.endswith("out_SET")), None) if out_set is None: - warning = "Expecting out_SET for instance of family '%s'" % family - self.log.warning(warning) + self.log.warning(( + "Expecting out_SET for instance of product type '{}'" + ).format(product_type)) return members = cmds.ls(cmds.sets(out_set, query=True), long=True) diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_history.py b/client/ayon_core/hosts/maya/plugins/publish/collect_history.py index d4e8c6298b..2da74991c0 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/collect_history.py +++ b/client/ayon_core/hosts/maya/plugins/publish/collect_history.py @@ -10,7 +10,7 @@ class CollectMayaHistory(pyblish.api.InstancePlugin): This removes render layers collected in the history This is separate from Collect Instances so we can target it towards only - specific family types. + specific product types. """ diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_instances.py b/client/ayon_core/hosts/maya/plugins/publish/collect_instances.py index 2d745d0ca8..85be15bb7b 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/collect_instances.py +++ b/client/ayon_core/hosts/maya/plugins/publish/collect_instances.py @@ -28,7 +28,7 @@ class CollectNewInstances(pyblish.api.InstancePlugin): order = pyblish.api.CollectorOrder hosts = ["maya"] - valid_empty_families = {"workfile", "renderlayer"} + valid_empty_product_types = {"workfile", "renderlayer"} def process(self, instance): @@ -60,7 +60,9 @@ class CollectNewInstances(pyblish.api.InstancePlugin): instance[:] = members_hierarchy - elif instance.data["family"] not in self.valid_empty_families: + elif ( + instance.data["productType"] not in self.valid_empty_product_types + ): self.log.warning("Empty instance: \"%s\" " % objset) # Store the exact members of the object set instance.data["setMembers"] = members diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_model.py b/client/ayon_core/hosts/maya/plugins/publish/collect_model.py index 557f96fe7a..9d45ed63bc 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/collect_model.py +++ b/client/ayon_core/hosts/maya/plugins/publish/collect_model.py @@ -8,8 +8,11 @@ class CollectModelData(pyblish.api.InstancePlugin): Ensures always only a single frame is extracted (current frame). + Todo: + Validate if is this plugin still useful. + Note: - This is a workaround so that the `pype.model` family can use the + This is a workaround so that the `model` product type can use the same pointcache extractor implementation as animation and pointcaches. This always enforces the "current" frame to be published. diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_render.py b/client/ayon_core/hosts/maya/plugins/publish/collect_render.py index d5392fba4a..8e5889f55f 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/collect_render.py +++ b/client/ayon_core/hosts/maya/plugins/publish/collect_render.py @@ -8,7 +8,7 @@ publishing on farm. Requires: instance -> families instance -> setMembers - instance -> asset + instance -> folderPath context -> currentFile context -> workspaceDir @@ -18,7 +18,7 @@ Optional: Provides: instance -> label - instance -> subset + instance -> productName instance -> attachTo instance -> setMembers instance -> publish @@ -26,9 +26,6 @@ Provides: instance -> frameEnd instance -> byFrameStep instance -> renderer - instance -> family - instance -> families - instance -> asset instance -> time instance -> author instance -> source @@ -90,18 +87,18 @@ class CollectMayaRender(pyblish.api.InstancePlugin): ) self.log.warning(msg) - # detect if there are sets (subsets) to attach render to + # detect if there are sets (products) to attach render to sets = cmds.sets(objset, query=True) or [] attach_to = [] for s in sets: - if not cmds.attributeQuery("family", node=s, exists=True): + if not cmds.attributeQuery("productType", node=s, exists=True): continue attach_to.append( { "version": None, # we need integrator for that - "subset": s, - "family": cmds.getAttr("{}.family".format(s)), + "productName": s, + "productType": cmds.getAttr("{}.productType".format(s)), } ) self.log.debug(" -> attach render to: {}".format(s)) @@ -145,13 +142,13 @@ class CollectMayaRender(pyblish.api.InstancePlugin): ) ) - # if we want to attach render to subset, check if we have AOV's + # if we want to attach render to product, check if we have AOV's # in expectedFiles. If so, raise error as we cannot attach AOV - # (considered to be subset on its own) to another subset + # (considered to be product on its own) to another product if attach_to: assert isinstance(expected_files, list), ( "attaching multiple AOVs or renderable cameras to " - "subset is not supported" + "product is not supported" ) # append full path @@ -303,7 +300,7 @@ class CollectMayaRender(pyblish.api.InstancePlugin): if self.sync_workfile_version: data["version"] = context.data["version"] for _instance in context: - if _instance.data['family'] == "workfile": + if _instance.data["productType"] == "workfile": _instance.data["version"] = context.data["version"] # Define nice label diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_render_layer_aovs.py b/client/ayon_core/hosts/maya/plugins/publish/collect_render_layer_aovs.py index 585eca5dce..1c83918155 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/collect_render_layer_aovs.py +++ b/client/ayon_core/hosts/maya/plugins/publish/collect_render_layer_aovs.py @@ -62,12 +62,12 @@ class CollectRenderLayerAOVS(pyblish.api.InstancePlugin): continue pass_name = self.get_pass_name(renderer, element) - render_pass = "%s.%s" % (instance.data["subset"], pass_name) + render_pass = "%s.%s" % (instance.data["productName"], pass_name) result.append(render_pass) self.log.debug("Found {} render elements / AOVs for " - "'{}'".format(len(result), instance.data["subset"])) + "'{}'".format(len(result), instance.data["productName"])) instance.data["renderPasses"] = result diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_review.py b/client/ayon_core/hosts/maya/plugins/publish/collect_review.py index 205c871c93..58d02294c5 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/collect_review.py +++ b/client/ayon_core/hosts/maya/plugins/publish/collect_review.py @@ -57,26 +57,26 @@ class CollectReview(pyblish.api.InstancePlugin): burninDataMembers["focalLength"] = focal_length # Account for nested instances like model. - reviewable_subsets = list(set(members) & objectset) - if reviewable_subsets: - if len(reviewable_subsets) > 1: + reviewable_products = list(set(members) & objectset) + if reviewable_products: + if len(reviewable_products) > 1: raise KnownPublishError( - "Multiple attached subsets for review are not supported. " - "Attached: {}".format(", ".join(reviewable_subsets)) + "Multiple attached products for review are not supported. " + "Attached: {}".format(", ".join(reviewable_products)) ) - reviewable_subset = reviewable_subsets[0] + reviewable_product = reviewable_products[0] self.log.debug( - "Subset attached to review: {}".format(reviewable_subset) + "Subset attached to review: {}".format(reviewable_product) ) # Find the relevant publishing instance in the current context reviewable_inst = next(inst for inst in context - if inst.name == reviewable_subset) + if inst.name == reviewable_product) data = reviewable_inst.data self.log.debug( - 'Adding review family to {}'.format(reviewable_subset) + 'Adding review family to {}'.format(reviewable_product) ) if data.get('families'): data['families'].append('review') @@ -119,16 +119,16 @@ class CollectReview(pyblish.api.InstancePlugin): project_name = instance.context.data["projectName"] asset_doc = instance.context.data['assetEntity'] task = instance.context.data["task"] - legacy_subset_name = task + 'Review' + legacy_product_name = task + 'Review' subset_doc = get_subset_by_name( project_name, - legacy_subset_name, + legacy_product_name, asset_doc["_id"], fields=["_id"] ) if subset_doc: - self.log.debug("Existing subsets found, keep legacy name.") - instance.data['subset'] = legacy_subset_name + self.log.debug("Existing products found, keep legacy name.") + instance.data["productName"] = legacy_product_name instance.data["cameras"] = cameras instance.data['review_camera'] = camera diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_vrayproxy.py b/client/ayon_core/hosts/maya/plugins/publish/collect_vrayproxy.py index 24521a2f09..8630f56e58 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/collect_vrayproxy.py +++ b/client/ayon_core/hosts/maya/plugins/publish/collect_vrayproxy.py @@ -14,7 +14,7 @@ class CollectVrayProxy(pyblish.api.InstancePlugin): def process(self, instance): """Collector entry point.""" - if not instance.data.get('families'): + if not instance.data.get("families"): instance.data["families"] = [] if instance.data.get("vrmesh"): diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_vrayscene.py b/client/ayon_core/hosts/maya/plugins/publish/collect_vrayscene.py index 979f49f7fe..57dd49e2d9 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/collect_vrayscene.py +++ b/client/ayon_core/hosts/maya/plugins/publish/collect_vrayscene.py @@ -57,7 +57,7 @@ class CollectVrayScene(pyblish.api.InstancePlugin): # Get layer specific settings, might be overrides data = { - "subset": layer_name, + "productName": layer_name, "layer": layer_name, # TODO: This likely needs fixing now # Before refactor: cmds.sets(layer, q=True) or ["*"] @@ -74,8 +74,8 @@ class CollectVrayScene(pyblish.api.InstancePlugin): self.get_render_attribute("byFrameStep", layer=layer_name)), "renderer": renderer, - # instance subset - "family": "vrayscene_layer", + # instance product type + "productType": "vrayscene_layer", "families": ["vrayscene_layer"], "time": get_formatted_current_time(), "author": context.data["user"], diff --git a/client/ayon_core/hosts/maya/plugins/publish/determine_future_version.py b/client/ayon_core/hosts/maya/plugins/publish/determine_future_version.py index afa249aca2..47fb4f03fe 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/determine_future_version.py +++ b/client/ayon_core/hosts/maya/plugins/publish/determine_future_version.py @@ -1,8 +1,9 @@ -import pyblish +import pyblish.api + class DetermineFutureVersion(pyblish.api.InstancePlugin): """ - This will determine version of subset if we want render to be attached to. + This will determine version of product if we want render to be attached to. """ label = "Determine Subset Version" order = pyblish.api.IntegratorOrder @@ -11,18 +12,25 @@ class DetermineFutureVersion(pyblish.api.InstancePlugin): def process(self, instance): context = instance.context - attach_to_subsets = [s["subset"] for s in instance.data['attachTo']] - - if not attach_to_subsets: + attatch_to_products = [ + i["productName"] + for i in instance.data["attachTo"] + ] + if not attatch_to_products: return for i in context: - if i.data["subset"] in attach_to_subsets: - # # this will get corresponding subset in attachTo list - # # so we can set version there - sub = next(item for item in instance.data['attachTo'] if item["subset"] == i.data["subset"]) # noqa: E501 + if i.data["productName"] not in attatch_to_products: + continue + # # this will get corresponding product in attachTo list + # # so we can set version there + sub = next( + item + for item in instance.data["attachTo"] + if item["productName"] == i.data["productName"] + ) - sub["version"] = i.data.get("version", 1) - self.log.info("render will be attached to {} v{}".format( - sub["subset"], sub["version"] - )) + sub["version"] = i.data.get("version", 1) + self.log.info("render will be attached to {} v{}".format( + sub["productName"], sub["version"] + )) diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_camera_alembic.py b/client/ayon_core/hosts/maya/plugins/publish/extract_camera_alembic.py index 2be9cfec95..74abc8de75 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_camera_alembic.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_camera_alembic.py @@ -14,8 +14,8 @@ class ExtractCameraAlembic(publish.Extractor, The camera gets baked to world space by default. Only when the instance's `bakeToWorldSpace` is set to False it will include its full hierarchy. - 'camera' family expects only single camera, if multiple cameras are needed, - 'matchmove' is better choice. + 'camera' product type expects only single camera, if multiple cameras + are needed, 'matchmove' is better choice. """ diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_camera_mayaScene.py b/client/ayon_core/hosts/maya/plugins/publish/extract_camera_mayaScene.py index 689eed09f8..c4af2914cd 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_camera_mayaScene.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_camera_mayaScene.py @@ -93,8 +93,8 @@ class ExtractCameraMayaScene(publish.Extractor, The cameras gets baked to world space by default. Only when the instance's `bakeToWorldSpace` is set to False it will include its full hierarchy. - 'camera' family expects only single camera, if multiple cameras are needed, - 'matchmove' is better choice. + 'camera' product type expects only single camera, if multiple cameras are + needed, 'matchmove' is better choice. Note: The extracted Maya ascii file gets "massaged" removing the uuid values diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_layout.py b/client/ayon_core/hosts/maya/plugins/publish/extract_layout.py index f6e663174a..441610b749 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_layout.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_layout.py @@ -67,10 +67,10 @@ class ExtractLayout(publish.Extractor): self.log.debug(representation) version_id = representation.get("parent") - family = representation.get("context").get("family") + product_type = representation.get("context").get("family") json_element = { - "family": family, + "product_type": product_type, "instance_name": cmds.getAttr( "{}.namespace".format(container)), "representation": str(representation_id), diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_maya_scene_raw.py b/client/ayon_core/hosts/maya/plugins/publish/extract_maya_scene_raw.py index 135185fe5c..2fd4f44449 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_maya_scene_raw.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_maya_scene_raw.py @@ -83,7 +83,7 @@ class ExtractMayaSceneRaw(publish.Extractor, AYONPyblishPluginMixin): selection = members if set(self.add_for_families).intersection( set(instance.data.get("families", []))) or \ - instance.data.get("family") in self.add_for_families: + instance.data.get("productType") in self.add_for_families: selection += self._get_loaded_containers(members) # Perform extraction diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_playblast.py b/client/ayon_core/hosts/maya/plugins/publish/extract_playblast.py index c019d43b36..a394d880ff 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_playblast.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_playblast.py @@ -43,7 +43,7 @@ class ExtractPlayblast(publish.Extractor): capture_preset = lib.get_capture_preset( task_data.get("name"), task_data.get("type"), - instance.data["subset"], + instance.data["productName"], instance.context.data["project_settings"], self.log ) diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_redshift_proxy.py b/client/ayon_core/hosts/maya/plugins/publish/extract_redshift_proxy.py index 5600b980d9..9286869c60 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_redshift_proxy.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_redshift_proxy.py @@ -28,7 +28,7 @@ class ExtractRedshiftProxy(publish.Extractor): if not anim_on: # Remove animation information because it is not required for - # non-animated subsets + # non-animated products keys = ["frameStart", "frameEnd", "handleStart", diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_thumbnail.py b/client/ayon_core/hosts/maya/plugins/publish/extract_thumbnail.py index db26422897..d3140487a6 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_thumbnail.py @@ -27,7 +27,7 @@ class ExtractThumbnail(publish.Extractor): capture_preset = lib.get_capture_preset( task_data.get("name"), task_data.get("type"), - instance.data["subset"], + instance.data["productName"], instance.context.data["project_settings"], self.log ) diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_vrayproxy.py b/client/ayon_core/hosts/maya/plugins/publish/extract_vrayproxy.py index 28c6e98c33..d16f9e8701 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_vrayproxy.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_vrayproxy.py @@ -27,7 +27,7 @@ class ExtractVRayProxy(publish.Extractor): anim_on = instance.data["animation"] if not anim_on: # Remove animation information because it is not required for - # non-animated subsets + # non-animated products keys = ["frameStart", "frameEnd", "handleStart", "handleEnd", "frameStartHandle", "frameEndHandle"] diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_workfile_xgen.py b/client/ayon_core/hosts/maya/plugins/publish/extract_workfile_xgen.py index d8b352668a..9aaba532b2 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_workfile_xgen.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_workfile_xgen.py @@ -45,7 +45,7 @@ class ExtractWorkfileXgen(publish.Extractor): is_renderlayer = ( "renderlayer" in i.data.get("families", []) or - i.data["family"] == "renderlayer" + i.data["productType"] == "renderlayer" ) return is_renderlayer diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_animation_content.py b/client/ayon_core/hosts/maya/plugins/publish/validate_animation_content.py index f33ee1a7e7..8cf5c4278e 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_animation_content.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_animation_content.py @@ -7,7 +7,7 @@ from ayon_core.pipeline.publish import ( class ValidateAnimationContent(pyblish.api.InstancePlugin): - """Adheres to the content of 'animation' family + """Adheres to the content of 'animation' product type - Must have collected `out_hierarchy` data. - All nodes in `out_hierarchy` must be in the instance. diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_attributes.py b/client/ayon_core/hosts/maya/plugins/publish/validate_attributes.py index fc39756bf0..1514972159 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_attributes.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_attributes.py @@ -62,7 +62,7 @@ class ValidateAttributes(pyblish.api.InstancePlugin, attributes_data = cls.get_attributes_data() # Filter families. - families = [instance.data["family"]] + families = [instance.data["productType"]] families += instance.data.get("families", []) families = set(families) & set(attributes_data.keys()) if not families: diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_frame_range.py b/client/ayon_core/hosts/maya/plugins/publish/validate_frame_range.py index d5f99e5563..5c5b691f9d 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_frame_range.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_frame_range.py @@ -74,9 +74,9 @@ class ValidateFrameRange(pyblish.api.InstancePlugin, # compare with data on instance errors = [] # QUESTION shouldn't this be just: - # 'if instance.data["family"] in self.exclude_product_types:' + # 'if instance.data["productType"] in self.exclude_product_types:' if [ef for ef in self.exclude_product_types - if instance.data["family"] in ef]: + if instance.data["productType"] in ef]: return if (inst_start != frame_start_handle): errors.append("Instance start frame [ {} ] doesn't " diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_instance_has_members.py b/client/ayon_core/hosts/maya/plugins/publish/validate_instance_has_members.py index 5a530236db..16e04af446 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_instance_has_members.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_instance_has_members.py @@ -26,7 +26,7 @@ class ValidateInstanceHasMembers(pyblish.api.InstancePlugin): def process(self, instance): # Allow renderlayer, rendersetup and workfile to be empty skip_families = {"workfile", "renderlayer", "rendersetup"} - if instance.data.get("family") in skip_families: + if instance.data.get("productType") in skip_families: return invalid = self.get_invalid(instance) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_instance_subset.py b/client/ayon_core/hosts/maya/plugins/publish/validate_instance_subset.py index 4229cfeb55..da3a194e58 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_instance_subset.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_instance_subset.py @@ -14,36 +14,40 @@ allowed = set(string.ascii_lowercase + '_') -def validate_name(subset): - return all(x in allowed for x in subset) +def validate_name(product_name): + return all(x in allowed for x in product_name) class ValidateSubsetName(pyblish.api.InstancePlugin): - """Validates subset name has only valid characters""" + """Validates product name has only valid characters""" order = ValidateContentsOrder families = ["*"] - label = "Subset Name" + label = "Product Name" def process(self, instance): - subset = instance.data.get("subset", None) + product_name = instance.data.get("productName", None) - # Ensure subset data - if subset is None: - raise PublishValidationError("Instance is missing subset " - "name: {0}".format(subset)) + # Ensure product data + if product_name is None: + raise PublishValidationError( + "Instance is missing product name: {0}".format(product_name) + ) - if not isinstance(subset, six.string_types): - raise TypeError("Instance subset name must be string, " - "got: {0} ({1})".format(subset, type(subset))) + if not isinstance(product_name, six.string_types): + raise TypeError(( + "Instance product name must be string, got: {0} ({1})" + ).format(product_name, type(product_name))) - # Ensure is not empty subset - if not subset: - raise ValueError("Instance subset name is " - "empty: {0}".format(subset)) + # Ensure is not empty product + if not product_name: + raise ValueError( + "Instance product name is empty: {0}".format(product_name) + ) - # Validate subset characters - if not validate_name(subset): - raise ValueError("Instance subset name contains invalid " - "characters: {0}".format(subset)) + # Validate product characters + if not validate_name(product_name): + raise ValueError(( + "Instance product name contains invalid characters: {0}" + ).format(product_name)) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_model_content.py b/client/ayon_core/hosts/maya/plugins/publish/validate_model_content.py index 8cc2675dc7..b0db5e435a 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_model_content.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_model_content.py @@ -10,7 +10,7 @@ from ayon_core.pipeline.publish import ( class ValidateModelContent(pyblish.api.InstancePlugin): - """Adheres to the content of 'model' family + """Adheres to the content of 'model' product type - Must have one top group. (configurable) - Must only contain: transforms, meshes and groups diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_renderlayer_aovs.py b/client/ayon_core/hosts/maya/plugins/publish/validate_renderlayer_aovs.py index b5bfdcc9ec..900e5444a9 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_renderlayer_aovs.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_renderlayer_aovs.py @@ -8,7 +8,7 @@ from ayon_core.pipeline.publish import PublishValidationError class ValidateRenderLayerAOVs(pyblish.api.InstancePlugin): """Validate created AOVs / RenderElement is registered in the database - Each render element is registered as a subset which is formatted based on + Each render element is registered as a product which is formatted based on the render layer and the render element, example: . @@ -31,7 +31,7 @@ class ValidateRenderLayerAOVs(pyblish.api.InstancePlugin): invalid = self.get_invalid(instance) if invalid: raise PublishValidationError( - "Found unregistered subsets: {}".format(invalid)) + "Found unregistered products: {}".format(invalid)) def get_invalid(self, instance): invalid = [] @@ -40,7 +40,7 @@ class ValidateRenderLayerAOVs(pyblish.api.InstancePlugin): asset_doc = instance.data["assetEntity"] render_passes = instance.data.get("renderPasses", []) for render_pass in render_passes: - is_valid = self.validate_subset_registered( + is_valid = self.validate_product_registered( project_name, asset_doc, render_pass ) if not is_valid: @@ -48,9 +48,11 @@ class ValidateRenderLayerAOVs(pyblish.api.InstancePlugin): return invalid - def validate_subset_registered(self, project_name, asset_doc, subset_name): - """Check if subset is registered in the database under the asset""" + def validate_product_registered( + self, project_name, asset_doc, product_name + ): + """Check if product is registered in the database under the asset""" return get_subset_by_name( - project_name, subset_name, asset_doc["_id"], fields=["_id"] + project_name, product_name, asset_doc["_id"], fields=["_id"] ) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py b/client/ayon_core/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py index 21ea827f68..c9860d27a0 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py @@ -81,7 +81,7 @@ class ValidateUnrealStaticMeshName(pyblish.api.InstancePlugin, ("_" + cls.static_mesh_prefix) or "", cls.regex_mesh ) sm_r = re.compile(regex_mesh) - if not sm_r.match(instance.data.get("subset")): + if not sm_r.match(instance.data.get("productName")): cls.log.error("Mesh doesn't comply with name validation.") return True diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_visible_only.py b/client/ayon_core/hosts/maya/plugins/publish/validate_visible_only.py index f9e4c9212a..29cf9420a3 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_visible_only.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_visible_only.py @@ -36,7 +36,7 @@ class ValidateAlembicVisibleOnly(pyblish.api.InstancePlugin): @classmethod def get_invalid(cls, instance): - if instance.data["family"] == "animation": + if instance.data["productType"] == "animation": # Special behavior to use the nodes in out_SET nodes = instance.data["out_hierarchy"] else: diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/app.py b/client/ayon_core/hosts/maya/tools/mayalookassigner/app.py index d73c5e318f..0969666484 100644 --- a/client/ayon_core/hosts/maya/tools/mayalookassigner/app.py +++ b/client/ayon_core/hosts/maya/tools/mayalookassigner/app.py @@ -211,7 +211,7 @@ class MayaLookAssignerWindow(QtWidgets.QWidget): # Collect the looks we want to apply (by name) look_items = self.look_outliner.get_selected_items() - looks = {look["subset"] for look in look_items} + looks = {look["product"] for look in look_items} selection = self.assign_selected.isChecked() asset_nodes = self.asset_outliner.get_nodes(selection=selection) @@ -225,22 +225,28 @@ class MayaLookAssignerWindow(QtWidgets.QWidget): # Assign the first matching look relevant for this asset # (since assigning multiple to the same nodes makes no sense) - assign_look = next((subset for subset in item["looks"] - if subset["name"] in looks), None) + assign_look = next( + ( + subset_doc + for subset_doc in item["looks"] + if subset_doc["name"] in looks + ), + None + ) if not assign_look: self.echo( "{} No matching selected look for {}".format(prefix, asset) ) continue - # Get the latest version of this asset's look subset + # Get the latest version of this asset's look product version = get_last_version_by_subset_id( project_name, assign_look["_id"], fields=["_id"] ) - subset_name = assign_look["name"] + product_name = assign_look["name"] self.echo("{} Assigning {} to {}\t".format( - prefix, subset_name, asset + prefix, product_name, asset )) nodes = item["nodes"] @@ -251,7 +257,7 @@ class MayaLookAssignerWindow(QtWidgets.QWidget): for vp in vray_proxies: if vp in nodes: - vrayproxy_assign_look(vp, subset_name) + vrayproxy_assign_look(vp, product_name) nodes = list(set(nodes).difference(vray_proxies)) else: @@ -266,7 +272,7 @@ class MayaLookAssignerWindow(QtWidgets.QWidget): for standin in arnold_standins: if standin in nodes: - arnold_standin.assign_look(standin, subset_name) + arnold_standin.assign_look(standin, product_name) nodes = list(set(nodes).difference(arnold_standins)) else: diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/arnold_standin.py b/client/ayon_core/hosts/maya/tools/mayalookassigner/arnold_standin.py index 9c6877fed8..810e1fc88c 100644 --- a/client/ayon_core/hosts/maya/tools/mayalookassigner/arnold_standin.py +++ b/client/ayon_core/hosts/maya/tools/mayalookassigner/arnold_standin.py @@ -131,8 +131,8 @@ def shading_engine_assignments(shading_engine, attribute, nodes, assignments): assignments[node].append(assignment) -def assign_look(standin, subset): - log.info("Assigning {} to {}.".format(subset, standin)) +def assign_look(standin, product_name): + log.info("Assigning {} to {}.".format(product_name, standin)) nodes_by_id = get_nodes_by_id(standin) @@ -148,13 +148,13 @@ def assign_look(standin, subset): # Get latest look version version = get_last_version_by_subset_name( project_name, - subset_name=subset, + subset_name=product_name, asset_id=asset_id, fields=["_id"] ) if not version: - log.info("Didn't find last version for subset name {}".format( - subset + log.info("Didn't find last version for product name {}".format( + product_name )) continue diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/commands.py b/client/ayon_core/hosts/maya/tools/mayalookassigner/commands.py index 4d2f1a8443..4375d38316 100644 --- a/client/ayon_core/hosts/maya/tools/mayalookassigner/commands.py +++ b/client/ayon_core/hosts/maya/tools/mayalookassigner/commands.py @@ -148,7 +148,7 @@ def create_items_from_nodes(nodes): ) continue - # Collect available look subsets for this asset + # Collect available look products for this asset looks = lib.list_looks(project_name, asset_doc["_id"]) # Collect namespaces the asset is found in diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/models.py b/client/ayon_core/hosts/maya/tools/mayalookassigner/models.py index a252f103ec..4892125954 100644 --- a/client/ayon_core/hosts/maya/tools/mayalookassigner/models.py +++ b/client/ayon_core/hosts/maya/tools/mayalookassigner/models.py @@ -88,7 +88,7 @@ class LookModel(models.TreeModel): An item exists of: { - "subset": 'name of subset', + "product": 'name of product', "asset": asset_document } @@ -102,26 +102,30 @@ class LookModel(models.TreeModel): self.beginResetModel() # Collect the assets per look name (from the items of the AssetModel) - look_subsets = defaultdict(list) + look_products = defaultdict(list) for asset_item in items: asset = asset_item["asset"] for look in asset_item["looks"]: - look_subsets[look["name"]].append(asset) + look_products[look["name"]].append(asset) - for subset in sorted(look_subsets.keys()): - assets = look_subsets[subset] + for product_name in sorted(look_products.keys()): + assets = look_products[product_name] # Define nice label without "look" prefix for readability - label = subset if not subset.startswith("look") else subset[4:] + label = ( + product_name + if not product_name.startswith("look") + else product_name[4:] + ) item_node = models.Item() item_node["label"] = label - item_node["subset"] = subset + item_node["product"] = product_name # Amount of matching assets for this look item_node["match"] = len(assets) - # Store the assets that have this subset available + # Store the assets that have this product available item_node["assets"] = assets self.add_child(item_node) diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/vray_proxies.py b/client/ayon_core/hosts/maya/tools/mayalookassigner/vray_proxies.py index cbd5f7fd5c..df74dcd217 100644 --- a/client/ayon_core/hosts/maya/tools/mayalookassigner/vray_proxies.py +++ b/client/ayon_core/hosts/maya/tools/mayalookassigner/vray_proxies.py @@ -51,13 +51,13 @@ def assign_vrayproxy_shaders(vrayproxy, assignments): index += 1 -def vrayproxy_assign_look(vrayproxy, subset="lookDefault"): +def vrayproxy_assign_look(vrayproxy, product_name="lookDefault"): # type: (str, str) -> None """Assign look to vray proxy. Args: vrayproxy (str): Name of vrayproxy to apply look to. - subset (str): Name of look subset. + product_name (str): Name of look product. Returns: None @@ -82,13 +82,13 @@ def vrayproxy_assign_look(vrayproxy, subset="lookDefault"): # Get latest look version version = get_last_version_by_subset_name( project_name, - subset_name=subset, + subset_name=product_name, asset_id=asset_id, fields=["_id"] ) if not version: - print("Didn't find last version for subset name {}".format( - subset + print("Didn't find last version for product name {}".format( + product_name )) continue From 23dbf74e519388d0b4a81afaf267d4efb7314b1b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 22 Feb 2024 14:44:45 +0100 Subject: [PATCH 237/573] max is using product name and types --- client/ayon_core/hosts/max/api/plugin.py | 42 +++++++++---------- .../hosts/max/plugins/create/create_camera.py | 2 +- .../max/plugins/create/create_maxScene.py | 2 +- .../hosts/max/plugins/create/create_model.py | 2 +- .../max/plugins/create/create_pointcache.py | 2 +- .../max/plugins/create/create_pointcloud.py | 2 +- .../plugins/create/create_redshift_proxy.py | 3 +- .../hosts/max/plugins/create/create_render.py | 6 +-- .../hosts/max/plugins/create/create_review.py | 6 +-- .../max/plugins/create/create_tycache.py | 2 +- .../max/plugins/create/create_workfile.py | 25 +++++------ .../plugins/publish/collect_frame_range.py | 2 +- .../max/plugins/publish/collect_members.py | 6 ++- .../max/plugins/publish/collect_render.py | 4 +- .../plugins/publish/validate_frame_range.py | 2 +- .../plugins/publish/validate_loaded_plugin.py | 2 +- 16 files changed, 56 insertions(+), 54 deletions(-) diff --git a/client/ayon_core/hosts/max/api/plugin.py b/client/ayon_core/hosts/max/api/plugin.py index 18f752631d..4d5d18a42d 100644 --- a/client/ayon_core/hosts/max/api/plugin.py +++ b/client/ayon_core/hosts/max/api/plugin.py @@ -163,11 +163,11 @@ class OpenPypeCreatorError(CreatorError): class MaxCreatorBase(object): @staticmethod - def cache_subsets(shared_data): - if shared_data.get("max_cached_subsets") is not None: + def cache_instance_data(shared_data): + if shared_data.get("max_cached_instances") is not None: return shared_data - shared_data["max_cached_subsets"] = {} + shared_data["max_cached_instances"] = {} cached_instances = [] for id_type in [AYON_INSTANCE_ID, AVALON_INSTANCE_ID]: @@ -175,11 +175,11 @@ class MaxCreatorBase(object): for i in cached_instances: creator_id = rt.GetUserProp(i, "creator_identifier") - if creator_id not in shared_data["max_cached_subsets"]: - shared_data["max_cached_subsets"][creator_id] = [i.name] + if creator_id not in shared_data["max_cached_instances"]: + shared_data["max_cached_instances"][creator_id] = [i.name] else: shared_data[ - "max_cached_subsets"][creator_id].append(i.name) + "max_cached_instances"][creator_id].append(i.name) return shared_data @staticmethod @@ -211,17 +211,17 @@ class MaxCreatorBase(object): class MaxCreator(Creator, MaxCreatorBase): selected_nodes = [] - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): if pre_create_data.get("use_selection"): self.selected_nodes = rt.GetCurrentSelection() - if rt.getNodeByName(subset_name): - raise CreatorError(f"'{subset_name}' is already created..") + if rt.getNodeByName(product_name): + raise CreatorError(f"'{product_name}' is already created..") - instance_node = self.create_instance_node(subset_name) + instance_node = self.create_instance_node(product_name) instance_data["instance_node"] = instance_node.name instance = CreatedInstance( - self.family, - subset_name, + self.product_type, + product_name, instance_data, self ) @@ -248,8 +248,8 @@ class MaxCreator(Creator, MaxCreatorBase): return instance def collect_instances(self): - self.cache_subsets(self.collection_shared_data) - for instance in self.collection_shared_data["max_cached_subsets"].get(self.identifier, []): # noqa + self.cache_instance_data(self.collection_shared_data) + for instance in self.collection_shared_data["max_cached_instances"].get(self.identifier, []): # noqa created_instance = CreatedInstance.from_existing( read(rt.GetNodeByName(instance)), self ) @@ -262,15 +262,15 @@ class MaxCreator(Creator, MaxCreatorBase): key: changes[key].new_value for key in changes.changed_keys } - subset = new_values.get("subset", "") - if subset and instance_node != subset: + product_name = new_values.get("productName", "") + if product_name and instance_node != product_name: node = rt.getNodeByName(instance_node) - new_subset_name = new_values["subset"] - if rt.getNodeByName(new_subset_name): + new_product_name = new_values["productName"] + if rt.getNodeByName(new_product_name): raise CreatorError( - "The subset '{}' already exists.".format( - new_subset_name)) - instance_node = new_subset_name + "The product '{}' already exists.".format( + new_product_name)) + instance_node = new_product_name created_inst["instance_node"] = instance_node node.name = instance_node diff --git a/client/ayon_core/hosts/max/plugins/create/create_camera.py b/client/ayon_core/hosts/max/plugins/create/create_camera.py index a35d5fc6b9..42f8cb716d 100644 --- a/client/ayon_core/hosts/max/plugins/create/create_camera.py +++ b/client/ayon_core/hosts/max/plugins/create/create_camera.py @@ -7,5 +7,5 @@ class CreateCamera(plugin.MaxCreator): """Creator plugin for Camera.""" identifier = "io.openpype.creators.max.camera" label = "Camera" - family = "camera" + product_type = "camera" icon = "gear" diff --git a/client/ayon_core/hosts/max/plugins/create/create_maxScene.py b/client/ayon_core/hosts/max/plugins/create/create_maxScene.py index 4b8328d38f..0e5768b267 100644 --- a/client/ayon_core/hosts/max/plugins/create/create_maxScene.py +++ b/client/ayon_core/hosts/max/plugins/create/create_maxScene.py @@ -7,5 +7,5 @@ class CreateMaxScene(plugin.MaxCreator): """Creator plugin for 3ds max scenes.""" identifier = "io.openpype.creators.max.maxScene" label = "Max Scene" - family = "maxScene" + product_type = "maxScene" icon = "gear" diff --git a/client/ayon_core/hosts/max/plugins/create/create_model.py b/client/ayon_core/hosts/max/plugins/create/create_model.py index 73f0260807..297c92067e 100644 --- a/client/ayon_core/hosts/max/plugins/create/create_model.py +++ b/client/ayon_core/hosts/max/plugins/create/create_model.py @@ -7,5 +7,5 @@ class CreateModel(plugin.MaxCreator): """Creator plugin for Model.""" identifier = "io.openpype.creators.max.model" label = "Model" - family = "model" + product_type = "model" icon = "gear" diff --git a/client/ayon_core/hosts/max/plugins/create/create_pointcache.py b/client/ayon_core/hosts/max/plugins/create/create_pointcache.py index d28f5008e5..eb0686a0c0 100644 --- a/client/ayon_core/hosts/max/plugins/create/create_pointcache.py +++ b/client/ayon_core/hosts/max/plugins/create/create_pointcache.py @@ -7,5 +7,5 @@ class CreatePointCache(plugin.MaxCreator): """Creator plugin for Point caches.""" identifier = "io.openpype.creators.max.pointcache" label = "Point Cache" - family = "pointcache" + product_type = "pointcache" icon = "gear" diff --git a/client/ayon_core/hosts/max/plugins/create/create_pointcloud.py b/client/ayon_core/hosts/max/plugins/create/create_pointcloud.py index aa6be04da4..9a58f4e624 100644 --- a/client/ayon_core/hosts/max/plugins/create/create_pointcloud.py +++ b/client/ayon_core/hosts/max/plugins/create/create_pointcloud.py @@ -7,5 +7,5 @@ class CreatePointCloud(plugin.MaxCreator): """Creator plugin for Point Clouds.""" identifier = "io.openpype.creators.max.pointcloud" label = "Point Cloud" - family = "pointcloud" + product_type = "pointcloud" icon = "gear" diff --git a/client/ayon_core/hosts/max/plugins/create/create_redshift_proxy.py b/client/ayon_core/hosts/max/plugins/create/create_redshift_proxy.py index e524e85cf6..17f5349dc1 100644 --- a/client/ayon_core/hosts/max/plugins/create/create_redshift_proxy.py +++ b/client/ayon_core/hosts/max/plugins/create/create_redshift_proxy.py @@ -1,11 +1,10 @@ # -*- coding: utf-8 -*- """Creator plugin for creating camera.""" from ayon_core.hosts.max.api import plugin -from ayon_core.pipeline import CreatedInstance class CreateRedshiftProxy(plugin.MaxCreator): identifier = "io.openpype.creators.max.redshiftproxy" label = "Redshift Proxy" - family = "redshiftproxy" + product_type = "redshiftproxy" icon = "gear" diff --git a/client/ayon_core/hosts/max/plugins/create/create_render.py b/client/ayon_core/hosts/max/plugins/create/create_render.py index 73c18bfb4b..60fe628a5e 100644 --- a/client/ayon_core/hosts/max/plugins/create/create_render.py +++ b/client/ayon_core/hosts/max/plugins/create/create_render.py @@ -10,10 +10,10 @@ class CreateRender(plugin.MaxCreator): """Creator plugin for Renders.""" identifier = "io.openpype.creators.max.render" label = "Render" - family = "maxrender" + product_type = "maxrender" icon = "gear" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): from pymxs import runtime as rt file = rt.maxFileName filename, _ = os.path.splitext(file) @@ -24,7 +24,7 @@ class CreateRender(plugin.MaxCreator): rt.batchRenderMgr.DeleteView(num_of_renderlayer) instance = super(CreateRender, self).create( - subset_name, + product_name, instance_data, pre_create_data) diff --git a/client/ayon_core/hosts/max/plugins/create/create_review.py b/client/ayon_core/hosts/max/plugins/create/create_review.py index a757b3b5bd..0a0ffd2e46 100644 --- a/client/ayon_core/hosts/max/plugins/create/create_review.py +++ b/client/ayon_core/hosts/max/plugins/create/create_review.py @@ -9,7 +9,7 @@ class CreateReview(plugin.MaxCreator): identifier = "io.openpype.creators.max.review" label = "Review" - family = "review" + product_type = "review" icon = "video-camera" review_width = 1920 @@ -38,7 +38,7 @@ class CreateReview(plugin.MaxCreator): "anti_aliasing", self.anti_aliasing) self.vp_texture = settings.get("vp_texture", self.vp_texture) - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): # Transfer settings from pre create to instance creator_attributes = instance_data.setdefault( "creator_attributes", dict()) @@ -55,7 +55,7 @@ class CreateReview(plugin.MaxCreator): creator_attributes[key] = pre_create_data[key] super(CreateReview, self).create( - subset_name, + product_name, instance_data, pre_create_data) diff --git a/client/ayon_core/hosts/max/plugins/create/create_tycache.py b/client/ayon_core/hosts/max/plugins/create/create_tycache.py index 81ccd3607c..2b3893bf13 100644 --- a/client/ayon_core/hosts/max/plugins/create/create_tycache.py +++ b/client/ayon_core/hosts/max/plugins/create/create_tycache.py @@ -7,5 +7,5 @@ class CreateTyCache(plugin.MaxCreator): """Creator plugin for TyCache.""" identifier = "io.openpype.creators.max.tycache" label = "TyCache" - family = "tycache" + product_type = "tycache" icon = "gear" diff --git a/client/ayon_core/hosts/max/plugins/create/create_workfile.py b/client/ayon_core/hosts/max/plugins/create/create_workfile.py index 27864c28d5..0af087dd39 100644 --- a/client/ayon_core/hosts/max/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/max/plugins/create/create_workfile.py @@ -11,7 +11,7 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): """Workfile auto-creator.""" identifier = "io.ayon.creators.max.workfile" label = "Workfile" - family = "workfile" + product_type = "workfile" icon = "fa5.file" default_variant = "Main" @@ -30,7 +30,7 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): if current_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( variant, task_name, asset_doc, project_name, host_name ) data = { @@ -45,10 +45,10 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): project_name, host_name, current_instance) ) self.log.info("Auto-creating workfile instance...") - instance_node = self.create_node(subset_name) + instance_node = self.create_node(product_name) data["instance_node"] = instance_node.name current_instance = CreatedInstance( - self.family, subset_name, data, self + self.product_type, product_name, data, self ) self._add_instance_to_context(current_instance) imprint(instance_node.name, current_instance.data) @@ -58,18 +58,19 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): ): # Update instance context if is not the same asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( variant, task_name, asset_doc, project_name, host_name ) asset_name = get_asset_name_identifier(asset_doc) current_instance["folderPath"] = asset_name current_instance["task"] = task_name - current_instance["subset"] = subset_name + current_instance["productName"] = product_name def collect_instances(self): - self.cache_subsets(self.collection_shared_data) - for instance in self.collection_shared_data["max_cached_subsets"].get(self.identifier, []): # noqa + self.cache_instance_data(self.collection_shared_data) + cached_instances = self.collection_shared_data["max_cached_instances"] + for instance in cached_instances.get(self.identifier, []): if not rt.getNodeByName(instance): continue created_instance = CreatedInstance.from_existing( @@ -100,10 +101,10 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): self._remove_instance_from_context(instance) - def create_node(self, subset_name): - if rt.getNodeByName(subset_name): - node = rt.getNodeByName(subset_name) + def create_node(self, product_name): + if rt.getNodeByName(product_name): + node = rt.getNodeByName(product_name) return node - node = rt.Container(name=subset_name) + node = rt.Container(name=product_name) node.isHidden = True return node diff --git a/client/ayon_core/hosts/max/plugins/publish/collect_frame_range.py b/client/ayon_core/hosts/max/plugins/publish/collect_frame_range.py index 86fb6e856c..6fc8de90d1 100644 --- a/client/ayon_core/hosts/max/plugins/publish/collect_frame_range.py +++ b/client/ayon_core/hosts/max/plugins/publish/collect_frame_range.py @@ -14,7 +14,7 @@ class CollectFrameRange(pyblish.api.InstancePlugin): "review", "redshiftproxy"] def process(self, instance): - if instance.data["family"] == "maxrender": + if instance.data["productType"] == "maxrender": instance.data["frameStartHandle"] = int(rt.rendStart) instance.data["frameEndHandle"] = int(rt.rendEnd) else: diff --git a/client/ayon_core/hosts/max/plugins/publish/collect_members.py b/client/ayon_core/hosts/max/plugins/publish/collect_members.py index f3fde00fe0..010b3cd3e1 100644 --- a/client/ayon_core/hosts/max/plugins/publish/collect_members.py +++ b/client/ayon_core/hosts/max/plugins/publish/collect_members.py @@ -12,8 +12,10 @@ class CollectMembers(pyblish.api.InstancePlugin): hosts = ['max'] def process(self, instance): - if instance.data["family"] == "workfile": - self.log.debug("Skipping Collecting Members for workfile family.") + if instance.data["productType"] == "workfile": + self.log.debug( + "Skipping Collecting Members for workfile product type." + ) return if instance.data.get("instance_node"): container = rt.GetNodeByName(instance.data["instance_node"]) diff --git a/client/ayon_core/hosts/max/plugins/publish/collect_render.py b/client/ayon_core/hosts/max/plugins/publish/collect_render.py index 66226e24fa..4515fe1a0c 100644 --- a/client/ayon_core/hosts/max/plugins/publish/collect_render.py +++ b/client/ayon_core/hosts/max/plugins/publish/collect_render.py @@ -95,11 +95,11 @@ class CollectRender(pyblish.api.InstancePlugin): # also need to get the render dir for conversion data = { "folderPath": instance.data["folderPath"], - "subset": str(instance.name), + "productName": str(instance.name), "publish": True, "maxversion": str(get_max_version()), "imageFormat": img_format, - "family": 'maxrender', + "productType": 'maxrender', "families": ['maxrender'], "renderer": renderer, "source": filepath, diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_frame_range.py b/client/ayon_core/hosts/max/plugins/publish/validate_frame_range.py index 75a83c2b05..22fda37e61 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_frame_range.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_frame_range.py @@ -81,7 +81,7 @@ class ValidateFrameRange(pyblish.api.InstancePlugin, frame_start_handle = frame_range["frameStartHandle"] frame_end_handle = frame_range["frameEndHandle"] - if instance.data["family"] == "maxrender": + if instance.data["productType"] == "maxrender": rt.rendStart = frame_start_handle rt.rendEnd = frame_end_handle else: diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_loaded_plugin.py b/client/ayon_core/hosts/max/plugins/publish/validate_loaded_plugin.py index bf5ac26fef..e278041b6b 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_loaded_plugin.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_loaded_plugin.py @@ -42,7 +42,7 @@ class ValidateLoadedPlugin(OptionalPyblishPluginMixin, invalid = [] # Find all plug-in requirements for current instance - instance_families = {instance.data["family"]} + instance_families = {instance.data["productType"]} instance_families.update(instance.data.get("families", [])) cls.log.debug("Checking plug-in validation " f"for instance families: {instance_families}") From c8c15b935fe4fedac90c9e04f0b1e281979b43f9 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 22 Feb 2024 14:54:45 +0100 Subject: [PATCH 238/573] Refactor knob value retrieval logic in ValidateNukeWriteNode - Update how knob values are collected and stored for validation. --- .../hosts/nuke/plugins/publish/validate_write_nodes.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/nuke/plugins/publish/validate_write_nodes.py b/client/ayon_core/hosts/nuke/plugins/publish/validate_write_nodes.py index 1c922f5d30..0244c1d504 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/validate_write_nodes.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/validate_write_nodes.py @@ -86,7 +86,10 @@ class ValidateNukeWriteNode( # Collect key values of same type in a list. values_by_name = defaultdict(list) for knob_data in correct_data["knobs"]: - values_by_name[knob_data["name"]].append(knob_data["value"]) + knob_type = knob_data["type"] + knob_value = knob_data[knob_type] + + values_by_name[knob_data["name"]].append(knob_value) for knob_data in correct_data["knobs"]: knob_type = knob_data["type"] From 2f5ed7c19a47d4a81c0436a87bde2344e85412b7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 22 Feb 2024 15:29:47 +0100 Subject: [PATCH 239/573] job queue modules is now addon inheriting from AYONAddon --- client/ayon_core/modules/job_queue/__init__.py | 4 ++-- client/ayon_core/modules/job_queue/module.py | 15 +++++++-------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/client/ayon_core/modules/job_queue/__init__.py b/client/ayon_core/modules/job_queue/__init__.py index 6f2cec1b97..cbf560c746 100644 --- a/client/ayon_core/modules/job_queue/__init__.py +++ b/client/ayon_core/modules/job_queue/__init__.py @@ -1,6 +1,6 @@ -from .module import JobQueueModule +from .module import JobQueueAddon __all__ = ( - "JobQueueModule", + "JobQueueAddon", ) diff --git a/client/ayon_core/modules/job_queue/module.py b/client/ayon_core/modules/job_queue/module.py index f2b022069b..bc6382d08a 100644 --- a/client/ayon_core/modules/job_queue/module.py +++ b/client/ayon_core/modules/job_queue/module.py @@ -41,16 +41,15 @@ import json import copy import platform -from ayon_core.addon import click_wrap -from ayon_core.modules import OpenPypeModule +from ayon_core.addon import AYONAddon, click_wrap from ayon_core.settings import get_system_settings -class JobQueueModule(OpenPypeModule): +class JobQueueAddon(AYONAddon): name = "job_queue" - def initialize(self, modules_settings): - module_settings = modules_settings.get(self.name) or {} + def initialize(self, studio_settings): + module_settings = studio_settings.get(self.name) or {} server_url = module_settings.get("server_url") or "" self._server_url = self.url_conversion(server_url) @@ -214,7 +213,7 @@ class JobQueueModule(OpenPypeModule): @click_wrap.group( - JobQueueModule.name, + JobQueueAddon.name, help="Application job server. Can be used as render farm." ) def cli_main(): @@ -228,7 +227,7 @@ def cli_main(): @click_wrap.option("--port", help="Server port") @click_wrap.option("--host", help="Server host (ip address)") def cli_start_server(port, host): - JobQueueModule.start_server(port, host) + JobQueueAddon.start_server(port, host) @cli_main.command( @@ -241,4 +240,4 @@ def cli_start_server(port, host): "--server_url", help="Server url which handle workers and jobs.") def cli_start_worker(app_name, server_url): - JobQueueModule.start_worker(app_name, server_url) + JobQueueAddon.start_worker(app_name, server_url) From 2e7f5e5b61ac59bbe7d1a3ec3aad218821865993 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 22 Feb 2024 15:31:41 +0100 Subject: [PATCH 240/573] renamed module.py to addon.py --- client/ayon_core/modules/job_queue/__init__.py | 2 +- .../modules/job_queue/{module.py => addon.py} | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) rename client/ayon_core/modules/job_queue/{module.py => addon.py} (94%) diff --git a/client/ayon_core/modules/job_queue/__init__.py b/client/ayon_core/modules/job_queue/__init__.py index cbf560c746..0a4c62abfb 100644 --- a/client/ayon_core/modules/job_queue/__init__.py +++ b/client/ayon_core/modules/job_queue/__init__.py @@ -1,4 +1,4 @@ -from .module import JobQueueAddon +from .addon import JobQueueAddon __all__ = ( diff --git a/client/ayon_core/modules/job_queue/module.py b/client/ayon_core/modules/job_queue/addon.py similarity index 94% rename from client/ayon_core/modules/job_queue/module.py rename to client/ayon_core/modules/job_queue/addon.py index bc6382d08a..167d27632c 100644 --- a/client/ayon_core/modules/job_queue/module.py +++ b/client/ayon_core/modules/job_queue/addon.py @@ -49,18 +49,18 @@ class JobQueueAddon(AYONAddon): name = "job_queue" def initialize(self, studio_settings): - module_settings = studio_settings.get(self.name) or {} - server_url = module_settings.get("server_url") or "" + addon_settings = studio_settings.get(self.name) or {} + server_url = addon_settings.get("server_url") or "" self._server_url = self.url_conversion(server_url) jobs_root_mapping = self._roots_mapping_conversion( - module_settings.get("jobs_root") + addon_settings.get("jobs_root") ) self._jobs_root_mapping = jobs_root_mapping # Is always enabled - # - the module does nothing until is used + # - the addon does nothing until is used self.enabled = True @classmethod @@ -126,8 +126,8 @@ class JobQueueAddon(AYONAddon): @classmethod def get_jobs_root_from_settings(cls): - module_settings = get_system_settings()["modules"] - jobs_root_mapping = module_settings.get(cls.name, {}).get("jobs_root") + studio_settings = get_system_settings() + jobs_root_mapping = studio_settings.get(cls.name, {}).get("jobs_root") converted_mapping = cls._roots_mapping_conversion(jobs_root_mapping) return converted_mapping[platform.system().lower()] @@ -156,9 +156,9 @@ class JobQueueAddon(AYONAddon): @classmethod def get_server_url_from_settings(cls): - module_settings = get_system_settings()["modules"] + studio_settings = get_system_settings() return cls.url_conversion( - module_settings + studio_settings .get(cls.name, {}) .get("server_url") ) From 73114e1119b2f24e4343db8d2dedb7fcf52df228 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 22 Feb 2024 15:35:08 +0100 Subject: [PATCH 241/573] remove system settings conversion functions --- client/ayon_core/settings/ayon_settings.py | 81 ++-------------------- client/ayon_core/settings/lib.py | 3 +- 2 files changed, 6 insertions(+), 78 deletions(-) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index e444a4dc10..cab1b52ecb 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -52,51 +52,6 @@ def _convert_color(color_value): return color_value -def _convert_general(ayon_settings, output, default_settings): - output["core"] = ayon_settings["core"] - version_check_interval = ( - default_settings["general"]["version_check_interval"] - ) - output["general"] = { - "version_check_interval": version_check_interval - } - - -def _convert_modules_system( - ayon_settings, output, addon_versions, default_settings -): - for key in { - "timers_manager", - "clockify", - "royalrender", - "deadline", - }: - if addon_versions.get(key): - output[key] = ayon_settings - else: - output.pop(key, None) - - modules_settings = output["modules"] - for module_name in ( - "sync_server", - "job_queue", - "addon_paths", - ): - settings = default_settings["modules"][module_name] - if "enabled" in settings: - settings["enabled"] = False - modules_settings[module_name] = settings - - for key, value in ayon_settings.items(): - if key not in output: - output[key] = value - - # Make sure addons have access to settings in initialization - # - AddonsManager passes only modules settings into initialization - if key not in modules_settings: - modules_settings[key] = value - - def is_dev_mode_enabled(): """Dev mode is enabled in AYON. @@ -107,31 +62,6 @@ def is_dev_mode_enabled(): return os.getenv("AYON_USE_DEV") == "1" -def convert_system_settings(ayon_settings, default_settings, addon_versions): - default_settings = copy.deepcopy(default_settings) - output = { - "modules": {} - } - if "core" in ayon_settings: - _convert_general(ayon_settings, output, default_settings) - - for key, value in ayon_settings.items(): - if key not in output: - output[key] = value - - for key, value in default_settings.items(): - if key not in output: - output[key] = value - - _convert_modules_system( - ayon_settings, - output, - addon_versions, - default_settings - ) - return output - - # --------- Project settings --------- def _convert_royalrender_project_settings(ayon_settings, output): if "royalrender" not in ayon_settings: @@ -285,13 +215,12 @@ def get_ayon_project_settings(default_values, project_name): return convert_project_settings(ayon_settings, default_values) -def get_ayon_system_settings(default_values): - addon_versions = _AyonSettingsCache.get_addon_versions() +def get_ayon_system_settings(): ayon_settings = _AyonSettingsCache.get_value_by_project(None) - - return convert_system_settings( - ayon_settings, default_values, addon_versions - ) + ayon_settings["general"] = { + "version_check_interval": 5 + } + return ayon_settings def get_ayon_settings(project_name=None): diff --git a/client/ayon_core/settings/lib.py b/client/ayon_core/settings/lib.py index 4dde488d6c..085808fbd5 100644 --- a/client/ayon_core/settings/lib.py +++ b/client/ayon_core/settings/lib.py @@ -233,8 +233,7 @@ def get_general_environments(): def get_system_settings(*args, **kwargs): - default_settings = get_default_settings()[SYSTEM_SETTINGS_KEY] - return get_ayon_system_settings(default_settings) + return get_ayon_system_settings() def get_project_settings(project_name, *args, **kwargs): From baa2359ea61ed922764891742a73d6e33c75c26a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 22 Feb 2024 15:44:57 +0100 Subject: [PATCH 242/573] removed system settings defaults loading logic --- client/ayon_core/settings/__init__.py | 2 - client/ayon_core/settings/constants.py | 6 - .../system_settings/applications.json | 1619 ----------------- .../defaults/system_settings/general.json | 26 - .../defaults/system_settings/modules.json | 200 -- .../defaults/system_settings/tools.json | 90 - client/ayon_core/settings/lib.py | 2 - 7 files changed, 1945 deletions(-) delete mode 100644 client/ayon_core/settings/defaults/system_settings/applications.json delete mode 100644 client/ayon_core/settings/defaults/system_settings/general.json delete mode 100644 client/ayon_core/settings/defaults/system_settings/modules.json delete mode 100644 client/ayon_core/settings/defaults/system_settings/tools.json diff --git a/client/ayon_core/settings/__init__.py b/client/ayon_core/settings/__init__.py index 074dbf8d03..8c3fdbee6d 100644 --- a/client/ayon_core/settings/__init__.py +++ b/client/ayon_core/settings/__init__.py @@ -1,5 +1,4 @@ from .constants import ( - SYSTEM_SETTINGS_KEY, PROJECT_SETTINGS_KEY, ) from .lib import ( @@ -12,7 +11,6 @@ from .ayon_settings import get_ayon_settings __all__ = ( - "SYSTEM_SETTINGS_KEY", "PROJECT_SETTINGS_KEY", "get_general_environments", diff --git a/client/ayon_core/settings/constants.py b/client/ayon_core/settings/constants.py index 0db3948b64..fef220bc31 100644 --- a/client/ayon_core/settings/constants.py +++ b/client/ayon_core/settings/constants.py @@ -9,11 +9,8 @@ METADATA_KEYS = frozenset([ ]) # Keys where studio's system overrides are stored -SYSTEM_SETTINGS_KEY = "system_settings" PROJECT_SETTINGS_KEY = "project_settings" -DEFAULT_PROJECT_KEY = "__default_project__" - __all__ = ( "M_OVERRIDDEN_KEY", @@ -21,8 +18,5 @@ __all__ = ( "METADATA_KEYS", - "SYSTEM_SETTINGS_KEY", "PROJECT_SETTINGS_KEY", - - "DEFAULT_PROJECT_KEY", ) diff --git a/client/ayon_core/settings/defaults/system_settings/applications.json b/client/ayon_core/settings/defaults/system_settings/applications.json deleted file mode 100644 index 2610c15315..0000000000 --- a/client/ayon_core/settings/defaults/system_settings/applications.json +++ /dev/null @@ -1,1619 +0,0 @@ -{ - "maya": { - "enabled": true, - "label": "Maya", - "icon": "{}/app_icons/maya.png", - "host_name": "maya", - "environment": { - "MAYA_DISABLE_CLIC_IPM": "Yes", - "MAYA_DISABLE_CIP": "Yes", - "MAYA_DISABLE_CER": "Yes", - "PYMEL_SKIP_MEL_INIT": "Yes", - "LC_ALL": "C" - }, - "variants": { - "2024": { - "use_python_2": false, - "executables": { - "windows": [ - "C:\\Program Files\\Autodesk\\Maya2024\\bin\\maya.exe" - ], - "darwin": [], - "linux": [ - "/usr/autodesk/maya2024/bin/maya" - ] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": { - "MAYA_VERSION": "2024" - } - }, - "2023": { - "use_python_2": false, - "executables": { - "windows": [ - "C:\\Program Files\\Autodesk\\Maya2023\\bin\\maya.exe" - ], - "darwin": [], - "linux": [ - "/usr/autodesk/maya2023/bin/maya" - ] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": { - "MAYA_VERSION": "2023" - } - }, - "2022": { - "use_python_2": false, - "executables": { - "windows": [ - "C:\\Program Files\\Autodesk\\Maya2022\\bin\\maya.exe" - ], - "darwin": [], - "linux": [ - "/usr/autodesk/maya2022/bin/maya" - ] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": { - "MAYA_VERSION": "2022" - } - } - } - }, - "mayapy": { - "enabled": true, - "label": "MayaPy", - "icon": "{}/app_icons/maya.png", - "host_name": "maya", - "environment": { - "MAYA_DISABLE_CLIC_IPM": "Yes", - "MAYA_DISABLE_CIP": "Yes", - "MAYA_DISABLE_CER": "Yes", - "PYMEL_SKIP_MEL_INIT": "Yes", - "LC_ALL": "C" - }, - "variants": { - "2024": { - "use_python_2": false, - "executables": { - "windows": [ - "C:\\Program Files\\Autodesk\\Maya2024\\bin\\mayapy.exe" - ], - "darwin": [], - "linux": [ - "/usr/autodesk/maya2024/bin/mayapy" - ] - }, - "arguments": { - "windows": [ - "-I" - ], - "darwin": [], - "linux": [ - "-I" - ] - }, - "environment": {} - }, - "2023": { - "use_python_2": false, - "executables": { - "windows": [ - "C:\\Program Files\\Autodesk\\Maya2023\\bin\\mayapy.exe" - ], - "darwin": [], - "linux": [ - "/usr/autodesk/maya2023/bin/mayapy" - ] - }, - "arguments": { - "windows": [ - "-I" - ], - "darwin": [], - "linux": [ - "-I" - ] - }, - "environment": {} - } - } - }, - "3dsmax": { - "enabled": true, - "label": "3ds max", - "icon": "{}/app_icons/3dsmax.png", - "host_name": "max", - "environment": {}, - "variants": { - "2023": { - "use_python_2": false, - "executables": { - "windows": [ - "C:\\Program Files\\Autodesk\\3ds Max 2023\\3dsmax.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": { - "3DSMAX_VERSION": "2023" - } - } - } - }, - "flame": { - "enabled": true, - "label": "Flame", - "icon": "{}/app_icons/flame.png", - "host_name": "flame", - "environment": { - "FLAME_SCRIPT_DIRS": { - "windows": "", - "darwin": "", - "linux": "" - }, - "FLAME_WIRETAP_HOSTNAME": "", - "FLAME_WIRETAP_VOLUME": "stonefs", - "FLAME_WIRETAP_GROUP": "staff" - }, - "variants": { - "2021": { - "use_python_2": true, - "executables": { - "windows": [], - "darwin": [ - "/opt/Autodesk/flame_2021/bin/flame.app/Contents/MacOS/startApp" - ], - "linux": [ - "/opt/Autodesk/flame_2021/bin/startApplication" - ] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": { - "OPENPYPE_FLAME_PYTHON_EXEC": "/opt/Autodesk/python/2021/bin/python2.7", - "OPENPYPE_FLAME_PYTHONPATH": "/opt/Autodesk/flame_2021/python", - "OPENPYPE_WIRETAP_TOOLS": "/opt/Autodesk/wiretap/tools/2021" - } - }, - "2021_1": { - "use_python_2": true, - "executables": { - "windows": [], - "darwin": [ - "/opt/Autodesk/flame_2021.1/bin/flame.app/Contents/MacOS/startApp" - ], - "linux": [ - "/opt/Autodesk/flame_2021.1/bin/startApplication" - ] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": { - "OPENPYPE_FLAME_PYTHON_EXEC": "/opt/Autodesk/python/2021.1/bin/python2.7", - "OPENPYPE_FLAME_PYTHONPATH": "/opt/Autodesk/flame_2021.1/python", - "OPENPYPE_WIRETAP_TOOLS": "/opt/Autodesk/wiretap/tools/2021.1" - } - }, - "__dynamic_keys_labels__": { - "2021": "2021", - "2021_1": "2021.1" - } - } - }, - "nuke": { - "enabled": true, - "label": "Nuke", - "icon": "{}/app_icons/nuke.png", - "host_name": "nuke", - "environment": { - "NUKE_PATH": [ - "{NUKE_PATH}", - "{OPENPYPE_STUDIO_PLUGINS}/nuke" - ] - }, - "variants": { - "13-2": { - "use_python_2": false, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke13.2v1\\Nuke13.2.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke13.2v1/Nuke13.2" - ] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "13-0": { - "use_python_2": false, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke13.0v1\\Nuke13.0.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke13.0v1/Nuke13.0" - ] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "12-2": { - "use_python_2": true, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke12.2v3\\Nuke12.2.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke12.2v3Nuke12.2" - ] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "12-0": { - "use_python_2": true, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke12.0v1\\Nuke12.0.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke12.0v1/Nuke12.0" - ] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "11-3": { - "use_python_2": true, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke11.3v1\\Nuke11.3.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke11.3v5/Nuke11.3" - ] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "11-2": { - "use_python_2": true, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke11.2v2\\Nuke11.2.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "11-0": { - "use_python_2": true, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke11.0v4\\Nuke11.0.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "__dynamic_keys_labels__": { - "13-2": "13.2", - "13-0": "13.0", - "12-2": "12.2", - "12-0": "12.0", - "11-3": "11.3", - "11-2": "11.2", - "11-0": "11.0" - } - } - }, - "nukeassist": { - "enabled": true, - "label": "Nuke Assist", - "icon": "{}/app_icons/nuke.png", - "host_name": "nuke", - "environment": { - "NUKE_PATH": [ - "{NUKE_PATH}", - "{OPENPYPE_STUDIO_PLUGINS}/nuke" - ] - }, - "variants": { - "13-2": { - "use_python_2": false, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke13.2v1\\Nuke13.2.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke13.2v1/Nuke13.2" - ] - }, - "arguments": { - "windows": [ - "--nukeassist" - ], - "darwin": [ - "--nukeassist" - ], - "linux": [ - "--nukeassist" - ] - }, - "environment": {} - }, - "13-0": { - "use_python_2": false, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke13.0v1\\Nuke13.0.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke13.0v1/Nuke13.0" - ] - }, - "arguments": { - "windows": [ - "--nukeassist" - ], - "darwin": [ - "--nukeassist" - ], - "linux": [ - "--nukeassist" - ] - }, - "environment": {} - }, - "12-2": { - "use_python_2": true, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke12.2v3\\Nuke12.2.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke12.2v3Nuke12.2" - ] - }, - "arguments": { - "windows": [ - "--nukeassist" - ], - "darwin": [ - "--nukeassist" - ], - "linux": [ - "--nukeassist" - ] - }, - "environment": {} - }, - "12-0": { - "use_python_2": true, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke12.0v1\\Nuke12.0.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke12.0v1/Nuke12.0" - ] - }, - "arguments": { - "windows": [ - "--nukeassist" - ], - "darwin": [ - "--nukeassist" - ], - "linux": [ - "--nukeassist" - ] - }, - "environment": {} - }, - "11-3": { - "use_python_2": true, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke11.3v1\\Nuke11.3.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke11.3v5/Nuke11.3" - ] - }, - "arguments": { - "windows": [ - "--nukeassist" - ], - "darwin": [ - "--nukeassist" - ], - "linux": [ - "--nukeassist" - ] - }, - "environment": {} - }, - "11-2": { - "use_python_2": true, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke11.2v2\\Nuke11.2.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [ - "--nukeassist" - ], - "darwin": [ - "--nukeassist" - ], - "linux": [ - "--nukeassist" - ] - }, - "environment": {} - }, - "__dynamic_keys_labels__": { - "13-2": "13.2", - "13-0": "13.0", - "12-2": "12.2", - "12-0": "12.0", - "11-3": "11.3", - "11-2": "11.2" - } - } - }, - "nukex": { - "enabled": true, - "label": "Nuke X", - "icon": "{}/app_icons/nukex.png", - "host_name": "nuke", - "environment": { - "NUKE_PATH": [ - "{NUKE_PATH}", - "{OPENPYPE_STUDIO_PLUGINS}/nuke" - ] - }, - "variants": { - "13-2": { - "use_python_2": false, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke13.2v1\\Nuke13.2.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke13.2v1/Nuke13.2" - ] - }, - "arguments": { - "windows": [ - "--nukex" - ], - "darwin": [ - "--nukex" - ], - "linux": [ - "--nukex" - ] - }, - "environment": {} - }, - "13-0": { - "use_python_2": false, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke13.0v1\\Nuke13.0.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke13.0v1/Nuke13.0" - ] - }, - "arguments": { - "windows": [ - "--nukex" - ], - "darwin": [ - "--nukex" - ], - "linux": [ - "--nukex" - ] - }, - "environment": {} - }, - "12-2": { - "use_python_2": true, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke12.2v3\\Nuke12.2.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke12.2v3Nuke12.2" - ] - }, - "arguments": { - "windows": [ - "--nukex" - ], - "darwin": [ - "--nukex" - ], - "linux": [ - "--nukex" - ] - }, - "environment": {} - }, - "12-0": { - "use_python_2": true, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke12.0v1\\Nuke12.0.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke12.0v1/Nuke12.0" - ] - }, - "arguments": { - "windows": [ - "--nukex" - ], - "darwin": [ - "--nukex" - ], - "linux": [ - "--nukex" - ] - }, - "environment": {} - }, - "11-3": { - "use_python_2": true, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke11.3v1\\Nuke11.3.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke11.3v5/Nuke11.3" - ] - }, - "arguments": { - "windows": [ - "--nukex" - ], - "darwin": [ - "--nukex" - ], - "linux": [ - "--nukex" - ] - }, - "environment": {} - }, - "11-2": { - "use_python_2": true, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke11.2v2\\Nuke11.2.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [ - "--nukex" - ], - "darwin": [ - "--nukex" - ], - "linux": [ - "--nukex" - ] - }, - "environment": {} - }, - "__dynamic_keys_labels__": { - "13-2": "13.2", - "13-0": "13.0", - "12-2": "12.2", - "12-0": "12.0", - "11-3": "11.3", - "11-2": "11.2" - } - } - }, - "nukestudio": { - "enabled": true, - "label": "Nuke Studio", - "icon": "{}/app_icons/nukestudio.png", - "host_name": "hiero", - "environment": { - "WORKFILES_STARTUP": "0", - "TAG_ASSETBUILD_STARTUP": "0" - }, - "variants": { - "13-2": { - "use_python_2": false, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke13.2v1\\Nuke13.2.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke13.2v1/Nuke13.2" - ] - }, - "arguments": { - "windows": [ - "--studio" - ], - "darwin": [ - "--studio" - ], - "linux": [ - "--studio" - ] - }, - "environment": {} - }, - "13-0": { - "use_python_2": false, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke13.0v1\\Nuke13.0.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke13.0v1/Nuke13.0" - ] - }, - "arguments": { - "windows": [ - "--studio" - ], - "darwin": [ - "--studio" - ], - "linux": [ - "--studio" - ] - }, - "environment": {} - }, - "12-2": { - "use_python_2": true, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke12.2v3\\Nuke12.2.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke12.2v3Nuke12.2" - ] - }, - "arguments": { - "windows": [ - "--studio" - ], - "darwin": [ - "--studio" - ], - "linux": [ - "--studio" - ] - }, - "environment": {} - }, - "12-0": { - "use_python_2": true, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke12.0v1\\Nuke12.0.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke12.0v1/Nuke12.0" - ] - }, - "arguments": { - "windows": [ - "--studio" - ], - "darwin": [ - "--studio" - ], - "linux": [ - "--studio" - ] - }, - "environment": {} - }, - "11-3": { - "use_python_2": true, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke11.3v1\\Nuke11.3.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke11.3v5/Nuke11.3" - ] - }, - "arguments": { - "windows": [ - "--studio" - ], - "darwin": [ - "--studio" - ], - "linux": [ - "--studio" - ] - }, - "environment": {} - }, - "11-2": { - "use_python_2": true, - "executables": { - "windows": [], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [ - "--studio" - ], - "darwin": [ - "--studio" - ], - "linux": [ - "--studio" - ] - }, - "environment": {} - }, - "__dynamic_keys_labels__": { - "13-2": "13.2", - "13-0": "13.0", - "12-2": "12.2", - "12-0": "12.0", - "11-3": "11.3", - "11-2": "11.2" - } - } - }, - "hiero": { - "enabled": true, - "label": "Hiero", - "icon": "{}/app_icons/hiero.png", - "host_name": "hiero", - "environment": { - "WORKFILES_STARTUP": "0", - "TAG_ASSETBUILD_STARTUP": "0" - }, - "variants": { - "13-2": { - "use_python_2": false, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke13.2v1\\Nuke13.2.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke13.2v1/Nuke13.2" - ] - }, - "arguments": { - "windows": [ - "--hiero" - ], - "darwin": [ - "--hiero" - ], - "linux": [ - "--hiero" - ] - }, - "environment": {} - }, - "13-0": { - "use_python_2": false, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke13.0v1\\Nuke13.0.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke13.0v1/Nuke13.0" - ] - }, - "arguments": { - "windows": [ - "--hiero" - ], - "darwin": [ - "--hiero" - ], - "linux": [ - "--hiero" - ] - }, - "environment": {} - }, - "12-2": { - "use_python_2": true, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke12.2v3\\Nuke12.2.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke12.2v3Nuke12.2" - ] - }, - "arguments": { - "windows": [ - "--hiero" - ], - "darwin": [ - "--hiero" - ], - "linux": [ - "--hiero" - ] - }, - "environment": {} - }, - "12-0": { - "use_python_2": true, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke12.0v1\\Nuke12.0.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke12.0v1/Nuke12.0" - ] - }, - "arguments": { - "windows": [ - "--hiero" - ], - "darwin": [ - "--hiero" - ], - "linux": [ - "--hiero" - ] - }, - "environment": {} - }, - "11-3": { - "use_python_2": true, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke11.3v1\\Nuke11.3.exe" - ], - "darwin": [], - "linux": [ - "/usr/local/Nuke11.3v5/Nuke11.3" - ] - }, - "arguments": { - "windows": [ - "--hiero" - ], - "darwin": [ - "--hiero" - ], - "linux": [ - "--hiero" - ] - }, - "environment": {} - }, - "11-2": { - "use_python_2": true, - "executables": { - "windows": [ - "C:\\Program Files\\Nuke11.2v2\\Nuke11.2.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [ - "--hiero" - ], - "darwin": [ - "--hiero" - ], - "linux": [ - "--hiero" - ] - }, - "environment": {} - }, - "__dynamic_keys_labels__": { - "13-2": "13.2", - "13-0": "13.0", - "12-2": "12.2", - "12-0": "12.0", - "11-3": "11.3", - "11-2": "11.2" - } - } - }, - "fusion": { - "enabled": true, - "label": "Fusion", - "icon": "{}/app_icons/fusion.png", - "host_name": "fusion", - "environment": { - "FUSION_PYTHON3_HOME": { - "windows": "{LOCALAPPDATA}/Programs/Python/Python36", - "darwin": "~/Library/Python/3.6/bin", - "linux": "/opt/Python/3.6/bin" - } - }, - "variants": { - "18": { - "executables": { - "windows": [ - "C:\\Program Files\\Blackmagic Design\\Fusion 18\\Fusion.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "17": { - "executables": { - "windows": [ - "C:\\Program Files\\Blackmagic Design\\Fusion 17\\Fusion.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "16": { - "executables": { - "windows": [ - "C:\\Program Files\\Blackmagic Design\\Fusion 16\\Fusion.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "9": { - "executables": { - "windows": [ - "C:\\Program Files\\Blackmagic Design\\Fusion 9\\Fusion.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - } - } - }, - "resolve": { - "enabled": true, - "label": "Resolve", - "icon": "{}/app_icons/resolve.png", - "host_name": "resolve", - "environment": { - "RESOLVE_UTILITY_SCRIPTS_SOURCE_DIR": [], - "RESOLVE_PYTHON3_HOME": { - "windows": "{LOCALAPPDATA}/Programs/Python/Python36", - "darwin": "/Library/Frameworks/Python.framework/Versions/3.6", - "linux": "/opt/Python/3.6" - } - }, - "variants": { - "stable": { - "enabled": true, - "variant_label": "stable", - "use_python_2": false, - "executables": { - "windows": [ - "C:/Program Files/Blackmagic Design/DaVinci Resolve/Resolve.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - } - } - }, - "houdini": { - "enabled": true, - "label": "Houdini", - "icon": "{}/app_icons/houdini.png", - "host_name": "houdini", - "environment": {}, - "variants": { - "18-5": { - "use_python_2": true, - "executables": { - "windows": [ - "C:\\Program Files\\Side Effects Software\\Houdini 18.5.499\\bin\\houdini.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "18": { - "use_python_2": true, - "executables": { - "windows": [], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "17": { - "use_python_2": true, - "executables": { - "windows": [], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "__dynamic_keys_labels__": { - "18-5": "18.5", - "18": "18", - "17": "17" - } - } - }, - "blender": { - "enabled": true, - "label": "Blender", - "icon": "{}/app_icons/blender.png", - "host_name": "blender", - "environment": {}, - "variants": { - "2-83": { - "executables": { - "windows": [ - "C:\\Program Files\\Blender Foundation\\Blender 2.83\\blender.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [ - "--python-use-system-env" - ], - "darwin": [ - "--python-use-system-env" - ], - "linux": [ - "--python-use-system-env" - ] - }, - "environment": {} - }, - "2-90": { - "executables": { - "windows": [ - "C:\\Program Files\\Blender Foundation\\Blender 2.90\\blender.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [ - "--python-use-system-env" - ], - "darwin": [ - "--python-use-system-env" - ], - "linux": [ - "--python-use-system-env" - ] - }, - "environment": {} - }, - "2-91": { - "executables": { - "windows": [ - "C:\\Program Files\\Blender Foundation\\Blender 2.91\\blender.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [ - "--python-use-system-env" - ], - "darwin": [ - "--python-use-system-env" - ], - "linux": [ - "--python-use-system-env" - ] - }, - "environment": {} - }, - "__dynamic_keys_labels__": { - "2-83": "2.83", - "2-90": "2.90", - "2-91": "2.91" - } - } - }, - "harmony": { - "enabled": true, - "label": "Harmony", - "icon": "{}/app_icons/harmony.png", - "host_name": "harmony", - "environment": { - "AYON_HARMONY_WORKFILES_ON_LAUNCH": "1" - }, - "variants": { - "21": { - "executables": { - "windows": [ - "c:\\Program Files (x86)\\Toon Boom Animation\\Toon Boom Harmony 21 Premium\\win64\\bin\\HarmonyPremium.exe" - ], - "darwin": [ - "/Applications/Toon Boom Harmony 21 Premium/Harmony Premium.app/Contents/MacOS/Harmony Premium" - ], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "20": { - "executables": { - "windows": [ - "c:\\Program Files (x86)\\Toon Boom Animation\\Toon Boom Harmony 20 Premium\\win64\\bin\\HarmonyPremium.exe" - ], - "darwin": [ - "/Applications/Toon Boom Harmony 20 Premium/Harmony Premium.app/Contents/MacOS/Harmony Premium" - ], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "17": { - "executables": { - "windows": [ - "c:\\Program Files (x86)\\Toon Boom Animation\\Toon Boom Harmony 17 Premium\\win64\\bin\\HarmonyPremium.exe" - ], - "darwin": [ - "/Applications/Toon Boom Harmony 17 Premium/Harmony Premium.app/Contents/MacOS/Harmony Premium" - ], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - } - } - }, - "tvpaint": { - "enabled": true, - "label": "TVPaint", - "icon": "{}/app_icons/tvpaint.png", - "host_name": "tvpaint", - "environment": {}, - "variants": { - "animation_11-64bits": { - "executables": { - "windows": [ - "C:\\Program Files\\TVPaint Developpement\\TVPaint Animation 11 (64bits)\\TVPaint Animation 11 (64bits).exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "animation_11-32bits": { - "executables": { - "windows": [ - "C:\\Program Files (x86)\\TVPaint Developpement\\TVPaint Animation 11 (32bits)\\TVPaint Animation 11 (32bits).exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "__dynamic_keys_labels__": { - "animation_11-64bits": "11 (64bits)", - "animation_11-32bits": "11 (32bits)" - } - } - }, - "photoshop": { - "enabled": true, - "label": "Photoshop", - "icon": "{}/app_icons/photoshop.png", - "host_name": "photoshop", - "environment": { - "AVALON_PHOTOSHOP_WORKFILES_ON_LAUNCH": "1", - "WORKFILES_SAVE_AS": "Yes" - }, - "variants": { - "2020": { - "executables": { - "windows": [ - "C:\\Program Files\\Adobe\\Adobe Photoshop 2020\\Photoshop.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "2021": { - "executables": { - "windows": [ - "C:\\Program Files\\Adobe\\Adobe Photoshop 2021\\Photoshop.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "2022": { - "executables": { - "windows": [ - "C:\\Program Files\\Adobe\\Adobe Photoshop 2022\\Photoshop.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - } - } - }, - "aftereffects": { - "enabled": true, - "label": "AfterEffects", - "icon": "{}/app_icons/aftereffects.png", - "host_name": "aftereffects", - "environment": { - "AVALON_AFTEREFFECTS_WORKFILES_ON_LAUNCH": "1", - "WORKFILES_SAVE_AS": "Yes" - }, - "variants": { - "2020": { - "executables": { - "windows": [ - "C:\\Program Files\\Adobe\\Adobe After Effects 2020\\Support Files\\AfterFX.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "2021": { - "executables": { - "windows": [ - "C:\\Program Files\\Adobe\\Adobe After Effects 2021\\Support Files\\AfterFX.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "2022": { - "executables": { - "windows": [ - "C:\\Program Files\\Adobe\\Adobe After Effects 2022\\Support Files\\AfterFX.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": { - "MULTIPROCESS": "No" - } - } - } - }, - "celaction": { - "enabled": true, - "label": "CelAction 2D", - "icon": "app_icons/celaction.png", - "host_name": "celaction", - "environment": { - "CELACTION_TEMPLATE": "{OPENPYPE_REPOS_ROOT}/openpype/hosts/celaction/celaction_template_scene.scn" - }, - "variants": { - "current": { - "enabled": true, - "variant_label": "Current", - "use_python_2": false, - "executables": { - "windows": [ - "C:/Program Files/CelAction/CelAction2D Studio/CelAction2D.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - } - } - }, - "substancepainter": { - "enabled": true, - "label": "Substance Painter", - "icon": "app_icons/substancepainter.png", - "host_name": "substancepainter", - "environment": {}, - "variants": { - "8-2-0": { - "executables": { - "windows": [ - "C:\\Program Files\\Adobe\\Adobe Substance 3D Painter\\Adobe Substance 3D Painter.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "__dynamic_keys_labels__": { - "8-2-0": "8.2.0" - } - } - }, - "unreal": { - "enabled": true, - "label": "Unreal Editor", - "icon": "{}/app_icons/ue4.png", - "host_name": "unreal", - "environment": { - "UE_PYTHONPATH": "{PYTHONPATH}" - }, - "variants": { - "5-0": { - "use_python_2": false, - "executables": { - "windows": [ - "C:\\Program Files\\Epic Games\\UE_5.0\\Engine\\Binaries\\Win64\\UnrealEditor.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "5-1": { - "use_python_2": false, - "executables": { - "windows": [ - "C:\\Program Files\\Epic Games\\UE_5.1\\Engine\\Binaries\\Win64\\UnrealEditor.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "__dynamic_keys_labels__": { - "5-1": "Unreal 5.1", - "5-0": "Unreal 5.0" - } - } - }, - "djvview": { - "enabled": true, - "label": "DJV View", - "icon": "{}/app_icons/djvView.png", - "host_name": "", - "environment": {}, - "variants": { - "1-1": { - "use_python_2": false, - "executables": { - "windows": [], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} - }, - "__dynamic_keys_labels__": { - "1-1": "1.1" - } - } - }, - "additional_apps": {} -} diff --git a/client/ayon_core/settings/defaults/system_settings/general.json b/client/ayon_core/settings/defaults/system_settings/general.json deleted file mode 100644 index 496c37cd4d..0000000000 --- a/client/ayon_core/settings/defaults/system_settings/general.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "studio_name": "Studio name", - "studio_code": "stu", - "admin_password": "", - "environment": {}, - "log_to_server": true, - "disk_mapping": { - "windows": [], - "linux": [], - "darwin": [] - }, - "local_env_white_list": [], - "openpype_path": { - "windows": [], - "darwin": [], - "linux": [] - }, - "local_openpype_path": { - "windows": "", - "darwin": "", - "linux": "" - }, - "production_version": "", - "staging_version": "", - "version_check_interval": 5 -} diff --git a/client/ayon_core/settings/defaults/system_settings/modules.json b/client/ayon_core/settings/defaults/system_settings/modules.json deleted file mode 100644 index 22daae3b34..0000000000 --- a/client/ayon_core/settings/defaults/system_settings/modules.json +++ /dev/null @@ -1,200 +0,0 @@ -{ - "addon_paths": { - "windows": [], - "darwin": [], - "linux": [] - }, - "avalon": { - "AVALON_TIMEOUT": 1000, - "AVALON_THUMBNAIL_ROOT": { - "windows": "", - "darwin": "", - "linux": "" - } - }, - "ftrack": { - "enabled": false, - "ftrack_server": "", - "ftrack_actions_path": { - "windows": [], - "darwin": [], - "linux": [] - }, - "ftrack_events_path": { - "windows": [], - "darwin": [], - "linux": [] - }, - "intent": { - "allow_empty_intent": true, - "empty_intent_label": "", - "items": { - "wip": "WIP", - "final": "Final", - "test": "Test" - }, - "default": "" - }, - "custom_attributes": { - "show": { - "avalon_auto_sync": { - "write_security_roles": [ - "API", - "Administrator" - ], - "read_security_roles": [ - "API", - "Administrator" - ] - }, - "library_project": { - "write_security_roles": [ - "API", - "Administrator" - ], - "read_security_roles": [ - "API", - "Administrator" - ] - }, - "applications": { - "write_security_roles": [ - "API", - "Administrator" - ], - "read_security_roles": [ - "API", - "Administrator" - ] - } - }, - "is_hierarchical": { - "tools_env": { - "write_security_roles": [ - "API", - "Administrator" - ], - "read_security_roles": [ - "API", - "Administrator" - ] - }, - "avalon_mongo_id": { - "write_security_roles": [ - "API", - "Administrator" - ], - "read_security_roles": [ - "API", - "Administrator" - ] - }, - "fps": { - "write_security_roles": [], - "read_security_roles": [] - }, - "frameStart": { - "write_security_roles": [], - "read_security_roles": [] - }, - "frameEnd": { - "write_security_roles": [], - "read_security_roles": [] - }, - "clipIn": { - "write_security_roles": [], - "read_security_roles": [] - }, - "clipOut": { - "write_security_roles": [], - "read_security_roles": [] - }, - "handleStart": { - "write_security_roles": [], - "read_security_roles": [] - }, - "handleEnd": { - "write_security_roles": [], - "read_security_roles": [] - }, - "resolutionWidth": { - "write_security_roles": [], - "read_security_roles": [] - }, - "resolutionHeight": { - "write_security_roles": [], - "read_security_roles": [] - }, - "pixelAspect": { - "write_security_roles": [], - "read_security_roles": [] - } - } - } - }, - "kitsu": { - "enabled": false, - "server": "" - }, - "shotgrid": { - "enabled": false, - "leecher_manager_url": "http://127.0.0.1:3000", - "leecher_backend_url": "http://127.0.0.1:8090", - "filter_projects_by_login": true, - "shotgrid_settings": {} - }, - "timers_manager": { - "enabled": true, - "auto_stop": true, - "full_time": 15.0, - "message_time": 0.5, - "disregard_publishing": false - }, - "clockify": { - "enabled": false, - "workspace_name": "" - }, - "sync_server": { - "enabled": false, - "sites": {} - }, - "deadline": { - "enabled": true, - "deadline_urls": { - "default": "http://127.0.0.1:8082" - } - }, - "royalrender": { - "enabled": false, - "rr_paths": { - "default": { - "windows": "C:\\RR8", - "darwin": "/Volumes/share/RR8", - "linux": "/mnt/studio/RR8" - } - } - }, - "log_viewer": { - "enabled": true - }, - "standalonepublish_tool": { - "enabled": false - }, - "project_manager": { - "enabled": true - }, - "slack": { - "enabled": false - }, - "job_queue": { - "server_url": "", - "jobs_root": { - "windows": "", - "darwin": "", - "linux": "" - } - }, - "asset_reporter": { - "enabled": false - } -} diff --git a/client/ayon_core/settings/defaults/system_settings/tools.json b/client/ayon_core/settings/defaults/system_settings/tools.json deleted file mode 100644 index 921e13af3a..0000000000 --- a/client/ayon_core/settings/defaults/system_settings/tools.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "tool_groups": { - "mtoa": { - "environment": { - "MTOA": "{STUDIO_SOFTWARE}/arnold/mtoa_{MAYA_VERSION}_{MTOA_VERSION}", - "MAYA_RENDER_DESC_PATH": "{MTOA}", - "MAYA_MODULE_PATH": "{MTOA}", - "ARNOLD_PLUGIN_PATH": "{MTOA}/shaders", - "MTOA_EXTENSIONS_PATH": { - "darwin": "{MTOA}/extensions", - "linux": "{MTOA}/extensions", - "windows": "{MTOA}/extensions" - }, - "MTOA_EXTENSIONS": { - "darwin": "{MTOA}/extensions", - "linux": "{MTOA}/extensions", - "windows": "{MTOA}/extensions" - }, - "DYLD_LIBRARY_PATH": { - "darwin": "{MTOA}/bin" - }, - "PATH": { - "windows": "{PATH};{MTOA}/bin" - } - }, - "variants": { - "3-2": { - "host_names": [], - "app_variants": [], - "environment": { - "MTOA_VERSION": "3.2" - } - }, - "3-1": { - "host_names": [], - "app_variants": [], - "environment": { - "MTOA_VERSION": "3.1" - } - }, - "__dynamic_keys_labels__": { - "3-2": "3.2", - "3-1": "3.1" - } - } - }, - "vray": { - "environment": {}, - "variants": {} - }, - "yeti": { - "environment": {}, - "variants": {} - }, - "renderman": { - "environment": {}, - "variants": { - "24-3-maya": { - "host_names": [ - "maya" - ], - "app_variants": [ - "maya/2022" - ], - "environment": { - "RFMTREE": { - "windows": "C:\\Program Files\\Pixar\\RenderManForMaya-24.3", - "darwin": "/Applications/Pixar/RenderManForMaya-24.3", - "linux": "/opt/pixar/RenderManForMaya-24.3" - }, - "RMANTREE": { - "windows": "C:\\Program Files\\Pixar\\RenderManProServer-24.3", - "darwin": "/Applications/Pixar/RenderManProServer-24.3", - "linux": "/opt/pixar/RenderManProServer-24.3" - } - } - }, - "__dynamic_keys_labels__": { - "24-3-maya": "24.3 RFM" - } - } - }, - "__dynamic_keys_labels__": { - "mtoa": "Autodesk Arnold", - "vray": "Chaos Group Vray", - "yeti": "Peregrine Labs Yeti", - "renderman": "Pixar Renderman" - } - } -} diff --git a/client/ayon_core/settings/lib.py b/client/ayon_core/settings/lib.py index 085808fbd5..8ad4dc1f93 100644 --- a/client/ayon_core/settings/lib.py +++ b/client/ayon_core/settings/lib.py @@ -8,9 +8,7 @@ from .constants import ( METADATA_KEYS, - SYSTEM_SETTINGS_KEY, PROJECT_SETTINGS_KEY, - DEFAULT_PROJECT_KEY ) from .ayon_settings import ( From 29cf1faa9f2b411d028cb1320f52267605c6cab1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 22 Feb 2024 15:45:47 +0100 Subject: [PATCH 243/573] modules settings for OpenPypeModule are empty --- client/ayon_core/addon/base.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/client/ayon_core/addon/base.py b/client/ayon_core/addon/base.py index 3b35476aed..bcd27d8029 100644 --- a/client/ayon_core/addon/base.py +++ b/client/ayon_core/addon/base.py @@ -15,13 +15,9 @@ from abc import ABCMeta, abstractmethod import six import appdirs -from ayon_core.lib import Logger +from ayon_core.lib import Logger, is_dev_mode_enabled from ayon_core.client import get_ayon_server_api_connection -from ayon_core.settings import get_system_settings -from ayon_core.settings.ayon_settings import ( - is_dev_mode_enabled, - get_ayon_settings, -) +from ayon_core.settings.ayon_settings import get_ayon_settings from .interfaces import ( IPluginPaths, @@ -648,7 +644,6 @@ class AddonsManager: def __init__(self, settings=None, initialize=True): self._settings = settings - self._system_settings = None self._addons = [] self._addons_by_id = {} @@ -740,12 +735,7 @@ class AddonsManager: if settings is None: settings = get_ayon_settings() - # OpenPype settings - system_settings = self._system_settings - if system_settings is None: - system_settings = get_system_settings() - - modules_settings = system_settings["modules"] + modules_settings = {} report = {} time_start = time.time() From 8431ab15ddd7ea2a4ed9a09c367522f7ed878f24 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 22 Feb 2024 15:46:15 +0100 Subject: [PATCH 244/573] removed helper '_convert_color' function --- client/ayon_core/settings/ayon_settings.py | 28 ---------------------- 1 file changed, 28 deletions(-) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index cab1b52ecb..00083b4c96 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -24,34 +24,6 @@ import six from ayon_core.client import get_ayon_server_api_connection -def _convert_color(color_value): - if isinstance(color_value, six.string_types): - color_value = color_value.lstrip("#") - color_value_len = len(color_value) - _color_value = [] - for idx in range(color_value_len // 2): - _color_value.append(int(color_value[idx:idx + 2], 16)) - for _ in range(4 - len(_color_value)): - _color_value.append(255) - return _color_value - - if isinstance(color_value, list): - # WARNING R,G,B can be 'int' or 'float' - # - 'float' variant is using 'int' for min: 0 and max: 1 - if len(color_value) == 3: - # Add alpha - color_value.append(255) - else: - # Convert float alha to int - alpha = int(color_value[3] * 255) - if alpha > 255: - alpha = 255 - elif alpha < 0: - alpha = 0 - color_value[3] = alpha - return color_value - - def is_dev_mode_enabled(): """Dev mode is enabled in AYON. From 38594c17dd9e7b2d8ea4d7600e94faa3a7ba572b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 22 Feb 2024 15:46:29 +0100 Subject: [PATCH 245/573] removed duplicated 'is_dev_mode_enabled' --- client/ayon_core/settings/ayon_settings.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index 00083b4c96..81c0ad99a3 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -24,16 +24,6 @@ import six from ayon_core.client import get_ayon_server_api_connection -def is_dev_mode_enabled(): - """Dev mode is enabled in AYON. - - Returns: - bool: True if dev mode is enabled. - """ - - return os.getenv("AYON_USE_DEV") == "1" - - # --------- Project settings --------- def _convert_royalrender_project_settings(ayon_settings, output): if "royalrender" not in ayon_settings: @@ -111,7 +101,7 @@ class _AyonSettingsCache: @classmethod def _get_variant(cls): if _AyonSettingsCache.variant is None: - from ayon_core.lib import is_staging_enabled + from ayon_core.lib import is_staging_enabled, is_dev_mode_enabled variant = "production" if is_dev_mode_enabled(): From a2272728169648b3d9a92ffad940dfecff2f695f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 22 Feb 2024 15:47:57 +0100 Subject: [PATCH 246/573] removed unnecessary general conversion --- client/ayon_core/settings/ayon_settings.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index 81c0ad99a3..1842805f63 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -178,11 +178,7 @@ def get_ayon_project_settings(default_values, project_name): def get_ayon_system_settings(): - ayon_settings = _AyonSettingsCache.get_value_by_project(None) - ayon_settings["general"] = { - "version_check_interval": 5 - } - return ayon_settings + return _AyonSettingsCache.get_value_by_project(None) def get_ayon_settings(project_name=None): From e9045809d25a654c2a3b625ca9f106149a48f476 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 22 Feb 2024 15:53:47 +0100 Subject: [PATCH 247/573] removed unnecessary 'enabled' value set --- client/ayon_core/modules/job_queue/addon.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/client/ayon_core/modules/job_queue/addon.py b/client/ayon_core/modules/job_queue/addon.py index 167d27632c..b28e915ac0 100644 --- a/client/ayon_core/modules/job_queue/addon.py +++ b/client/ayon_core/modules/job_queue/addon.py @@ -59,10 +59,6 @@ class JobQueueAddon(AYONAddon): self._jobs_root_mapping = jobs_root_mapping - # Is always enabled - # - the addon does nothing until is used - self.enabled = True - @classmethod def _root_conversion(cls, root_path): """Make sure root path does not end with slash.""" From ff9db0ffa1223482fc579f3f95f11f44add610cc Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 22 Feb 2024 15:58:26 +0100 Subject: [PATCH 248/573] remove royal render project settings conversion --- client/ayon_core/settings/ayon_settings.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py index e444a4dc10..32bb814c5b 100644 --- a/client/ayon_core/settings/ayon_settings.py +++ b/client/ayon_core/settings/ayon_settings.py @@ -133,24 +133,9 @@ def convert_system_settings(ayon_settings, default_settings, addon_versions): # --------- Project settings --------- -def _convert_royalrender_project_settings(ayon_settings, output): - if "royalrender" not in ayon_settings: - return - ayon_royalrender = ayon_settings["royalrender"] - rr_paths = ayon_royalrender.get("selected_rr_paths", []) - - output["royalrender"] = { - "publish": ayon_royalrender["publish"], - "rr_paths": rr_paths, - } - - def convert_project_settings(ayon_settings, default_settings): default_settings = copy.deepcopy(default_settings) output = {} - - _convert_royalrender_project_settings(ayon_settings, output) - for key, value in ayon_settings.items(): if key not in output: output[key] = value From 9d72a7bff2f5017655d5859b9aec0db4b1c1a121 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 22 Feb 2024 16:45:28 +0100 Subject: [PATCH 249/573] houdini is using product name and type --- .../hosts/houdini/api/creator_node_shelves.py | 4 +- client/ayon_core/hosts/houdini/api/plugin.py | 24 ++++---- .../houdini/plugins/create/convert_legacy.py | 58 ++++++++++--------- .../plugins/create/create_alembic_camera.py | 10 ++-- .../plugins/create/create_arnold_ass.py | 10 ++-- .../plugins/create/create_arnold_rop.py | 16 ++--- .../houdini/plugins/create/create_bgeo.py | 8 +-- .../plugins/create/create_composite.py | 8 +-- .../houdini/plugins/create/create_hda.py | 18 +++--- .../plugins/create/create_karma_rop.py | 20 +++---- .../plugins/create/create_mantra_ifd.py | 10 ++-- .../plugins/create/create_mantra_rop.py | 16 ++--- .../plugins/create/create_pointcache.py | 8 +-- .../plugins/create/create_redshift_proxy.py | 10 ++-- .../plugins/create/create_redshift_rop.py | 14 ++--- .../houdini/plugins/create/create_review.py | 13 +++-- .../plugins/create/create_staticmesh.py | 12 ++-- .../houdini/plugins/create/create_usd.py | 10 ++-- .../plugins/create/create_usdrender.py | 8 +-- .../plugins/create/create_vbd_cache.py | 8 +-- .../houdini/plugins/create/create_vray_rop.py | 24 ++++---- .../houdini/plugins/create/create_workfile.py | 10 ++-- .../hosts/houdini/plugins/load/load_fbx.py | 6 +- .../plugins/publish/collect_cache_farm.py | 2 +- .../plugins/publish/collect_instances.py | 10 +++- .../publish/collect_instances_usd_layered.py | 27 +++++---- .../plugins/publish/collect_usd_bootstrap.py | 35 +++++------ .../plugins/publish/collect_usd_layers.py | 10 ++-- .../plugins/publish/extract_usd_layered.py | 20 +++---- .../publish/validate_file_extension.py | 6 +- .../validate_houdini_license_category.py | 2 +- .../plugins/publish/validate_subset_name.py | 40 +++++++------ .../validate_unreal_staticmesh_naming.py | 2 +- .../validate_usd_shade_model_exists.py | 14 +++-- 34 files changed, 256 insertions(+), 237 deletions(-) diff --git a/client/ayon_core/hosts/houdini/api/creator_node_shelves.py b/client/ayon_core/hosts/houdini/api/creator_node_shelves.py index 567bb245db..f795918834 100644 --- a/client/ayon_core/hosts/houdini/api/creator_node_shelves.py +++ b/client/ayon_core/hosts/houdini/api/creator_node_shelves.py @@ -90,7 +90,7 @@ def create_interactive(creator_identifier, **kwargs): pane = stateutils.activePane(kwargs) if isinstance(pane, hou.NetworkEditor): pwd = pane.pwd() - subset_name = creator.get_subset_name( + product_name = creator.get_product_name( variant=variant, task_name=context.get_current_task_name(), asset_doc=get_asset_by_name( @@ -104,7 +104,7 @@ def create_interactive(creator_identifier, **kwargs): tool_fn = CATEGORY_GENERIC_TOOL.get(pwd.childTypeCategory()) if tool_fn is not None: out_null = tool_fn(kwargs, "null") - out_null.setName("OUT_{}".format(subset_name), unique_name=True) + out_null.setName("OUT_{}".format(product_name), unique_name=True) before = context.instances_by_id.copy() diff --git a/client/ayon_core/hosts/houdini/api/plugin.py b/client/ayon_core/hosts/houdini/api/plugin.py index 57fd194000..82d36b3d07 100644 --- a/client/ayon_core/hosts/houdini/api/plugin.py +++ b/client/ayon_core/hosts/houdini/api/plugin.py @@ -99,14 +99,14 @@ class Creator(LegacyCreator): class HoudiniCreatorBase(object): @staticmethod - def cache_subsets(shared_data): + def cache_instance_data(shared_data): """Cache instances for Creators to shared data. - Create `houdini_cached_subsets` key when needed in shared data and + Create `houdini_cached_instances` key when needed in shared data and fill it with all collected instances from the scene under its respective creator identifiers. - Create `houdini_cached_legacy_subsets` key for any legacy instances + Create `houdini_cached_legacy_instance` key for any legacy instances detected in the scene as instances per family. Args: @@ -116,7 +116,7 @@ class HoudiniCreatorBase(object): Dict[str, Any]: Shared data dictionary. """ - if shared_data.get("houdini_cached_subsets") is None: + if shared_data.get("houdini_cached_instances") is None: cache = dict() cache_legacy = dict() @@ -141,8 +141,8 @@ class HoudiniCreatorBase(object): family = family_parm.eval() cache_legacy.setdefault(family, []).append(node) - shared_data["houdini_cached_subsets"] = cache - shared_data["houdini_cached_legacy_subsets"] = cache_legacy + shared_data["houdini_cached_instances"] = cache + shared_data["houdini_cached_legacy_instance"] = cache_legacy return shared_data @@ -177,7 +177,7 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase): settings_name = None add_publish_button = False - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): try: self.selected_nodes = [] @@ -192,15 +192,15 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase): asset_name = instance_data["folderPath"] instance_node = self.create_instance_node( - asset_name, subset_name, "/out", node_type) + asset_name, product_name, "/out", node_type) self.customize_node_look(instance_node) instance_data["instance_node"] = instance_node.path() instance_data["instance_id"] = instance_node.path() instance = CreatedInstance( - self.family, - subset_name, + self.product_type, + product_name, instance_data, self) self._add_instance_to_context(instance) @@ -234,9 +234,9 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase): def collect_instances(self): # cache instances if missing - self.cache_subsets(self.collection_shared_data) + self.cache_instance_data(self.collection_shared_data) for instance in self.collection_shared_data[ - "houdini_cached_subsets"].get(self.identifier, []): + "houdini_cached_instances"].get(self.identifier, []): node_data = read(instance) diff --git a/client/ayon_core/hosts/houdini/plugins/create/convert_legacy.py b/client/ayon_core/hosts/houdini/plugins/create/convert_legacy.py index 6e503fba6b..008187d9c8 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/convert_legacy.py +++ b/client/ayon_core/hosts/houdini/plugins/create/convert_legacy.py @@ -1,24 +1,24 @@ # -*- coding: utf-8 -*- -"""Converter for legacy Houdini subsets.""" +"""Converter for legacy Houdini products.""" from ayon_core.pipeline.create.creator_plugins import SubsetConvertorPlugin from ayon_core.hosts.houdini.api.lib import imprint class HoudiniLegacyConvertor(SubsetConvertorPlugin): - """Find and convert any legacy subsets in the scene. + """Find and convert any legacy products in the scene. - This Converter will find all legacy subsets in the scene and will - transform them to the current system. Since the old subsets doesn't + This Converter will find all legacy products in the scene and will + transform them to the current system. Since the old products doesn't retain any information about their original creators, the only mapping we can do is based on their families. - Its limitation is that you can have multiple creators creating subset - of the same family and there is no way to handle it. This code should - nevertheless cover all creators that came with OpenPype. + Its limitation is that you can have multiple creators creating product + name of the same product type and there is no way to handle it. This code + should nevertheless cover all creators that came with AYON. """ identifier = "io.openpype.creators.houdini.legacy" - family_to_id = { + product_type_to_id = { "camera": "io.openpype.creators.houdini.camera", "ass": "io.openpype.creators.houdini.ass", "imagesequence": "io.openpype.creators.houdini.imagesequence", @@ -33,44 +33,46 @@ class HoudiniLegacyConvertor(SubsetConvertorPlugin): def __init__(self, *args, **kwargs): super(HoudiniLegacyConvertor, self).__init__(*args, **kwargs) - self.legacy_subsets = {} + self.legacy_instances = {} def find_instances(self): - """Find legacy subsets in the scene. + """Find legacy products in the scene. - Legacy subsets are the ones that doesn't have `creator_identifier` + Legacy products are the ones that doesn't have `creator_identifier` parameter on them. This is using cached entries done in - :py:meth:`~HoudiniCreatorBase.cache_subsets()` + :py:meth:`~HoudiniCreatorBase.cache_instance_data()` """ - self.legacy_subsets = self.collection_shared_data.get( - "houdini_cached_legacy_subsets") - if not self.legacy_subsets: + self.legacy_instances = self.collection_shared_data.get( + "houdini_cached_legacy_instance") + if not self.legacy_instances: return - self.add_convertor_item("Found {} incompatible subset{}.".format( - len(self.legacy_subsets), "s" if len(self.legacy_subsets) > 1 else "") - ) + self.add_convertor_item("Found {} incompatible product{}.".format( + len(self.legacy_instances), + "s" if len(self.legacy_instances) > 1 else "" + )) def convert(self): - """Convert all legacy subsets to current. + """Convert all legacy products to current. It is enough to add `creator_identifier` and `instance_node`. """ - if not self.legacy_subsets: + if not self.legacy_instances: return - for family, subsets in self.legacy_subsets.items(): - if family in self.family_to_id: - for subset in subsets: + for product_type, legacy_instances in self.legacy_instances.items(): + if product_type in self.product_type_to_id: + for instance in legacy_instances: + creator_id = self.product_type_to_id[product_type] data = { - "creator_identifier": self.family_to_id[family], - "instance_node": subset.path() + "creator_identifier": creator_id, + "instance_node": instance.path() } - if family == "pointcache": + if product_type == "pointcache": data["families"] = ["abc"] self.log.info("Converting {} to {}".format( - subset.path(), self.family_to_id[family])) - imprint(subset, data) + instance.path(), creator_id)) + imprint(instance, data) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_alembic_camera.py b/client/ayon_core/hosts/houdini/plugins/create/create_alembic_camera.py index b6661fe7e4..b61b4cbd46 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_alembic_camera.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_alembic_camera.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""Creator plugin for creating alembic camera subsets.""" +"""Creator plugin for creating alembic camera products.""" from ayon_core.hosts.houdini.api import plugin from ayon_core.pipeline import CreatedInstance, CreatorError @@ -11,24 +11,24 @@ class CreateAlembicCamera(plugin.HoudiniCreator): identifier = "io.openpype.creators.houdini.camera" label = "Camera (Abc)" - family = "camera" + product_type = "camera" icon = "camera" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): import hou instance_data.pop("active", None) instance_data.update({"node_type": "alembic"}) instance = super(CreateAlembicCamera, self).create( - subset_name, + product_name, instance_data, pre_create_data) # type: CreatedInstance instance_node = hou.node(instance.get("instance_node")) parms = { "filename": hou.text.expandString( - "$HIP/pyblish/{}.abc".format(subset_name)), + "$HIP/pyblish/{}.abc".format(product_name)), "use_sop_path": False, } diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_ass.py b/client/ayon_core/hosts/houdini/plugins/create/create_arnold_ass.py index f60f5bc42f..6d992f136a 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_ass.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_arnold_ass.py @@ -9,7 +9,7 @@ class CreateArnoldAss(plugin.HoudiniCreator): identifier = "io.openpype.creators.houdini.ass" label = "Arnold ASS" - family = "ass" + product_type = "ass" icon = "magic" # Default extension: `.ass` or `.ass.gz` @@ -17,7 +17,7 @@ class CreateArnoldAss(plugin.HoudiniCreator): # will override it by the value in the project settings ext = ".ass" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): import hou instance_data.pop("active", None) @@ -27,7 +27,7 @@ class CreateArnoldAss(plugin.HoudiniCreator): creator_attributes["farm"] = pre_create_data["farm"] instance = super(CreateArnoldAss, self).create( - subset_name, + product_name, instance_data, pre_create_data) # type: plugin.CreatedInstance @@ -41,7 +41,7 @@ class CreateArnoldAss(plugin.HoudiniCreator): filepath = "{}{}".format( hou.text.expandString("$HIP/pyblish/"), - "{}.$F4{}".format(subset_name, self.ext) + "{}.$F4{}".format(product_name, self.ext) ) parms = { # Render frame range @@ -54,7 +54,7 @@ class CreateArnoldAss(plugin.HoudiniCreator): instance_node.setParms(parms) # Lock any parameters in this list - to_lock = ["ar_ass_export_enable", "family", "id"] + to_lock = ["ar_ass_export_enable", "productType", "id"] self.lock_parameters(instance_node, to_lock) def get_instance_attr_defs(self): diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py index 590a92f56f..b7c5910a4f 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py @@ -7,7 +7,7 @@ class CreateArnoldRop(plugin.HoudiniCreator): identifier = "io.openpype.creators.houdini.arnold_rop" label = "Arnold ROP" - family = "arnold_rop" + product_type = "arnold_rop" icon = "magic" # Default extension @@ -16,7 +16,7 @@ class CreateArnoldRop(plugin.HoudiniCreator): # Default to split export and render jobs export_job = True - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): import hou # Remove the active, we are checking the bypass flag of the nodes @@ -29,7 +29,7 @@ class CreateArnoldRop(plugin.HoudiniCreator): instance_data["farm"] = pre_create_data.get("farm") instance = super(CreateArnoldRop, self).create( - subset_name, + product_name, instance_data, pre_create_data) # type: plugin.CreatedInstance @@ -37,9 +37,9 @@ class CreateArnoldRop(plugin.HoudiniCreator): ext = pre_create_data.get("image_format") - filepath = "{renders_dir}{subset_name}/{subset_name}.$F4.{ext}".format( + filepath = "{renders_dir}{product_name}/{product_name}.$F4.{ext}".format( renders_dir=hou.text.expandString("$HIP/pyblish/renders/"), - subset_name=subset_name, + product_name=product_name, ext=ext, ) parms = { @@ -53,9 +53,9 @@ class CreateArnoldRop(plugin.HoudiniCreator): if pre_create_data.get("export_job"): ass_filepath = \ - "{export_dir}{subset_name}/{subset_name}.$F4.ass".format( + "{export_dir}{product_name}/{product_name}.$F4.ass".format( export_dir=hou.text.expandString("$HIP/pyblish/ass/"), - subset_name=subset_name, + product_name=product_name, ) parms["ar_ass_export_enable"] = 1 parms["ar_ass_file"] = ass_filepath @@ -63,7 +63,7 @@ class CreateArnoldRop(plugin.HoudiniCreator): instance_node.setParms(parms) # Lock any parameters in this list - to_lock = ["family", "id"] + to_lock = ["productType", "id"] self.lock_parameters(instance_node, to_lock) def get_pre_create_attr_defs(self): diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_bgeo.py b/client/ayon_core/hosts/houdini/plugins/create/create_bgeo.py index 135c889b3e..92c89c71cb 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_bgeo.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_bgeo.py @@ -10,10 +10,10 @@ class CreateBGEO(plugin.HoudiniCreator): """BGEO pointcache creator.""" identifier = "io.openpype.creators.houdini.bgeo" label = "PointCache (Bgeo)" - family = "pointcache" + product_type = "pointcache" icon = "gears" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): instance_data.pop("active", None) @@ -23,7 +23,7 @@ class CreateBGEO(plugin.HoudiniCreator): creator_attributes["farm"] = pre_create_data["farm"] instance = super(CreateBGEO, self).create( - subset_name, + product_name, instance_data, pre_create_data) # type: CreatedInstance @@ -32,7 +32,7 @@ class CreateBGEO(plugin.HoudiniCreator): file_path = "{}{}".format( hou.text.expandString("$HIP/pyblish/"), "{}.$F4.{}".format( - subset_name, + product_name, pre_create_data.get("bgeo_type") or "bgeo.sc") ) parms = { diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_composite.py b/client/ayon_core/hosts/houdini/plugins/create/create_composite.py index b87e1fd5b1..a1104e5093 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_composite.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_composite.py @@ -11,26 +11,26 @@ class CreateCompositeSequence(plugin.HoudiniCreator): identifier = "io.openpype.creators.houdini.imagesequence" label = "Composite (Image Sequence)" - family = "imagesequence" + product_type = "imagesequence" icon = "gears" ext = ".exr" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): import hou # noqa instance_data.pop("active", None) instance_data.update({"node_type": "comp"}) instance = super(CreateCompositeSequence, self).create( - subset_name, + product_name, instance_data, pre_create_data) # type: CreatedInstance instance_node = hou.node(instance.get("instance_node")) filepath = "{}{}".format( hou.text.expandString("$HIP/pyblish/"), - "{}.$F4{}".format(subset_name, self.ext) + "{}.$F4{}".format(product_name, self.ext) ) parms = { "trange": 1, diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py index faddc11b0c..994977de7d 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py @@ -13,14 +13,14 @@ class CreateHDA(plugin.HoudiniCreator): identifier = "io.openpype.creators.houdini.hda" label = "Houdini Digital Asset (Hda)" - family = "hda" + product_type = "hda" icon = "gears" maintain_selection = False - def _check_existing(self, asset_name, subset_name): + def _check_existing(self, asset_name, product_name): # type: (str) -> bool - """Check if existing subset name versions already exists.""" - # Get all subsets of the current asset + """Check if existing product name versions already exists.""" + # Get all products of the current folder project_name = self.project_name asset_doc = get_asset_by_name( project_name, asset_name, fields=["_id"] @@ -28,11 +28,11 @@ class CreateHDA(plugin.HoudiniCreator): subset_docs = get_subsets( project_name, asset_ids=[asset_doc["_id"]], fields=["name"] ) - existing_subset_names_low = { + existing_product_names_low = { subset_doc["name"].lower() for subset_doc in subset_docs } - return subset_name.lower() in existing_subset_names_low + return product_name.lower() in existing_product_names_low def create_instance_node( self, asset_name, node_name, parent, node_type="geometry" @@ -64,7 +64,7 @@ class CreateHDA(plugin.HoudiniCreator): hda_node.layoutChildren() elif self._check_existing(asset_name, node_name): raise plugin.OpenPypeCreatorError( - ("subset {} is already published with different HDA" + ("product {} is already published with different HDA" "definition.").format(node_name)) else: hda_node = to_hda @@ -73,11 +73,11 @@ class CreateHDA(plugin.HoudiniCreator): self.customize_node_look(hda_node) return hda_node - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): instance_data.pop("active", None) instance = super(CreateHDA, self).create( - subset_name, + product_name, instance_data, pre_create_data) # type: plugin.CreatedInstance diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py index 5211044fea..9eb9d80cd3 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py @@ -9,10 +9,10 @@ class CreateKarmaROP(plugin.HoudiniCreator): """Karma ROP""" identifier = "io.openpype.creators.houdini.karma_rop" label = "Karma ROP" - family = "karma_rop" + product_type = "karma_rop" icon = "magic" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): import hou # noqa instance_data.pop("active", None) @@ -23,7 +23,7 @@ class CreateKarmaROP(plugin.HoudiniCreator): instance_data["farm"] = pre_create_data.get("farm") instance = super(CreateKarmaROP, self).create( - subset_name, + product_name, instance_data, pre_create_data) # type: CreatedInstance @@ -31,19 +31,19 @@ class CreateKarmaROP(plugin.HoudiniCreator): ext = pre_create_data.get("image_format") - filepath = "{renders_dir}{subset_name}/{subset_name}.$F4.{ext}".format( + filepath = "{renders_dir}{product_name}/{product_name}.$F4.{ext}".format( renders_dir=hou.text.expandString("$HIP/pyblish/renders/"), - subset_name=subset_name, + product_name=product_name, ext=ext, ) - checkpoint = "{cp_dir}{subset_name}.$F4.checkpoint".format( + checkpoint = "{cp_dir}{product_name}.$F4.checkpoint".format( cp_dir=hou.text.expandString("$HIP/pyblish/"), - subset_name=subset_name + product_name=product_name ) - usd_directory = "{usd_dir}{subset_name}_$RENDERID".format( + usd_directory = "{usd_dir}{product_name}_$RENDERID".format( usd_dir=hou.text.expandString("$HIP/pyblish/renders/usd_renders/"), # noqa - subset_name=subset_name + product_name=product_name ) parms = { @@ -84,7 +84,7 @@ class CreateKarmaROP(plugin.HoudiniCreator): instance_node.setParms(parms) # Lock some Avalon attributes - to_lock = ["family", "id"] + to_lock = ["productType", "id"] self.lock_parameters(instance_node, to_lock) def get_pre_create_attr_defs(self): diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_ifd.py b/client/ayon_core/hosts/houdini/plugins/create/create_mantra_ifd.py index 7f1da13d2e..bb10f3893c 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_ifd.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_mantra_ifd.py @@ -9,10 +9,10 @@ class CreateMantraIFD(plugin.HoudiniCreator): """Mantra .ifd Archive""" identifier = "io.openpype.creators.houdini.mantraifd" label = "Mantra IFD" - family = "mantraifd" + product_type = "mantraifd" icon = "gears" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): import hou instance_data.pop("active", None) instance_data.update({"node_type": "ifd"}) @@ -20,7 +20,7 @@ class CreateMantraIFD(plugin.HoudiniCreator): "creator_attributes", dict()) creator_attributes["farm"] = pre_create_data["farm"] instance = super(CreateMantraIFD, self).create( - subset_name, + product_name, instance_data, pre_create_data) # type: CreatedInstance @@ -28,7 +28,7 @@ class CreateMantraIFD(plugin.HoudiniCreator): filepath = "{}{}".format( hou.text.expandString("$HIP/pyblish/"), - "{}.$F4.ifd".format(subset_name)) + "{}.$F4.ifd".format(product_name)) parms = { # Render frame range "trange": 1, @@ -40,7 +40,7 @@ class CreateMantraIFD(plugin.HoudiniCreator): instance_node.setParms(parms) # Lock any parameters in this list - to_lock = ["soho_outputmode", "family", "id"] + to_lock = ["soho_outputmode", "productType", "id"] self.lock_parameters(instance_node, to_lock) def get_instance_attr_defs(self): diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py index 02252f35d1..f15f49f463 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py @@ -9,13 +9,13 @@ class CreateMantraROP(plugin.HoudiniCreator): """Mantra ROP""" identifier = "io.openpype.creators.houdini.mantra_rop" label = "Mantra ROP" - family = "mantra_rop" + product_type = "mantra_rop" icon = "magic" # Default to split export and render jobs export_job = True - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): import hou # noqa instance_data.pop("active", None) @@ -26,7 +26,7 @@ class CreateMantraROP(plugin.HoudiniCreator): instance_data["farm"] = pre_create_data.get("farm") instance = super(CreateMantraROP, self).create( - subset_name, + product_name, instance_data, pre_create_data) # type: CreatedInstance @@ -34,9 +34,9 @@ class CreateMantraROP(plugin.HoudiniCreator): ext = pre_create_data.get("image_format") - filepath = "{renders_dir}{subset_name}/{subset_name}.$F4.{ext}".format( + filepath = "{renders_dir}{product_name}/{product_name}.$F4.{ext}".format( renders_dir=hou.text.expandString("$HIP/pyblish/renders/"), - subset_name=subset_name, + product_name=product_name, ext=ext, ) @@ -49,9 +49,9 @@ class CreateMantraROP(plugin.HoudiniCreator): if pre_create_data.get("export_job"): ifd_filepath = \ - "{export_dir}{subset_name}/{subset_name}.$F4.ifd".format( + "{export_dir}{product_name}/{product_name}.$F4.ifd".format( export_dir=hou.text.expandString("$HIP/pyblish/ifd/"), - subset_name=subset_name, + product_name=product_name, ) parms["soho_outputmode"] = 1 parms["soho_diskfile"] = ifd_filepath @@ -75,7 +75,7 @@ class CreateMantraROP(plugin.HoudiniCreator): instance_node.setParms(parms) # Lock some Avalon attributes - to_lock = ["family", "id"] + to_lock = ["productType", "id"] self.lock_parameters(instance_node, to_lock) def get_pre_create_attr_defs(self): diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_pointcache.py b/client/ayon_core/hosts/houdini/plugins/create/create_pointcache.py index 07dcc17f25..9e0a335c3a 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_pointcache.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_pointcache.py @@ -11,10 +11,10 @@ class CreatePointCache(plugin.HoudiniCreator): """Alembic ROP to pointcache""" identifier = "io.openpype.creators.houdini.pointcache" label = "PointCache (Abc)" - family = "pointcache" + product_type = "pointcache" icon = "gears" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): instance_data.pop("active", None) instance_data.update({"node_type": "alembic"}) creator_attributes = instance_data.setdefault( @@ -22,7 +22,7 @@ class CreatePointCache(plugin.HoudiniCreator): creator_attributes["farm"] = pre_create_data["farm"] instance = super(CreatePointCache, self).create( - subset_name, + product_name, instance_data, pre_create_data) @@ -35,7 +35,7 @@ class CreatePointCache(plugin.HoudiniCreator): "format": 2, "facesets": 0, "filename": hou.text.expandString( - "$HIP/pyblish/{}.abc".format(subset_name)) + "$HIP/pyblish/{}.abc".format(product_name)) } if self.selected_nodes: diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_redshift_proxy.py b/client/ayon_core/hosts/houdini/plugins/create/create_redshift_proxy.py index fa42411a1c..6a9321b95a 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_redshift_proxy.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_redshift_proxy.py @@ -9,10 +9,10 @@ class CreateRedshiftProxy(plugin.HoudiniCreator): """Redshift Proxy""" identifier = "io.openpype.creators.houdini.redshiftproxy" label = "Redshift Proxy" - family = "redshiftproxy" + product_type = "redshiftproxy" icon = "magic" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): # Remove the active, we are checking the bypass flag of the nodes instance_data.pop("active", None) @@ -30,14 +30,14 @@ class CreateRedshiftProxy(plugin.HoudiniCreator): creator_attributes["farm"] = pre_create_data["farm"] instance = super(CreateRedshiftProxy, self).create( - subset_name, + product_name, instance_data, pre_create_data) instance_node = hou.node(instance.get("instance_node")) parms = { - "RS_archive_file": '$HIP/pyblish/{}.$F4.rs'.format(subset_name), + "RS_archive_file": '$HIP/pyblish/{}.$F4.rs'.format(product_name), } if self.selected_nodes: @@ -46,7 +46,7 @@ class CreateRedshiftProxy(plugin.HoudiniCreator): instance_node.setParms(parms) # Lock some Avalon attributes - to_lock = ["family", "id", "prim_to_detail_pattern"] + to_lock = ["productType", "id", "prim_to_detail_pattern"] self.lock_parameters(instance_node, to_lock) def get_network_categories(self): diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py index 8e88c690b9..3d6d657cf0 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py @@ -11,14 +11,14 @@ class CreateRedshiftROP(plugin.HoudiniCreator): identifier = "io.openpype.creators.houdini.redshift_rop" label = "Redshift ROP" - family = "redshift_rop" + product_type = "redshift_rop" icon = "magic" ext = "exr" # Default to split export and render jobs split_render = True - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): instance_data.pop("active", None) instance_data.update({"node_type": "Redshift_ROP"}) @@ -28,7 +28,7 @@ class CreateRedshiftROP(plugin.HoudiniCreator): instance_data["farm"] = pre_create_data.get("farm") instance = super(CreateRedshiftROP, self).create( - subset_name, + product_name, instance_data, pre_create_data) @@ -56,9 +56,9 @@ class CreateRedshiftROP(plugin.HoudiniCreator): ipr_rop.parm("linked_rop").set(instance_node.path()) ext = pre_create_data.get("image_format") - filepath = "{renders_dir}{subset_name}/{subset_name}.{fmt}".format( + filepath = "{renders_dir}{product_name}/{product_name}.{fmt}".format( renders_dir=hou.text.expandString("$HIP/pyblish/renders/"), - subset_name=subset_name, + product_name=product_name, fmt="${aov}.$F4.{ext}".format(aov="AOV", ext=ext) ) @@ -83,7 +83,7 @@ class CreateRedshiftROP(plugin.HoudiniCreator): parms["RS_renderCamera"] = camera or "" export_dir = hou.text.expandString("$HIP/pyblish/rs/") - rs_filepath = f"{export_dir}{subset_name}/{subset_name}.$F4.rs" + rs_filepath = f"{export_dir}{product_name}/{product_name}.$F4.rs" parms["RS_archive_file"] = rs_filepath if pre_create_data.get("split_render", self.split_render): @@ -92,7 +92,7 @@ class CreateRedshiftROP(plugin.HoudiniCreator): instance_node.setParms(parms) # Lock some Avalon attributes - to_lock = ["family", "id"] + to_lock = ["productType", "id"] self.lock_parameters(instance_node, to_lock) def remove_instances(self, instances): diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_review.py b/client/ayon_core/hosts/houdini/plugins/create/create_review.py index c512a61105..7dc0171b1a 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_review.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_review.py @@ -12,10 +12,10 @@ class CreateReview(plugin.HoudiniCreator): identifier = "io.openpype.creators.houdini.review" label = "Review" - family = "review" + product_type = "review" icon = "video-camera" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): instance_data.pop("active", None) instance_data.update({"node_type": "opengl"}) @@ -23,7 +23,7 @@ class CreateReview(plugin.HoudiniCreator): instance_data["keepImages"] = pre_create_data.get("keepImages") instance = super(CreateReview, self).create( - subset_name, + product_name, instance_data, pre_create_data) @@ -31,9 +31,10 @@ class CreateReview(plugin.HoudiniCreator): frame_range = hou.playbar.frameRange() - filepath = "{root}/{subset}/{subset}.$F4.{ext}".format( + filepath = "{root}/{product_name}/{product_name}.$F4.{ext}".format( root=hou.text.expandString("$HIP/pyblish"), - subset="`chs(\"subset\")`", # keep dynamic link to subset + # keep dynamic link to product name + product_name="`chs(\"productName\")`", ext=pre_create_data.get("image_format") or "png" ) @@ -89,7 +90,7 @@ class CreateReview(plugin.HoudiniCreator): if os.getenv("OCIO"): self.set_colorcorrect_to_default_view_space(instance_node) - to_lock = ["id", "family"] + to_lock = ["id", "productType"] self.lock_parameters(instance_node, to_lock) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_staticmesh.py b/client/ayon_core/hosts/houdini/plugins/create/create_staticmesh.py index 319be3568d..f96c3faabb 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_staticmesh.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_staticmesh.py @@ -11,17 +11,17 @@ class CreateStaticMesh(plugin.HoudiniCreator): identifier = "io.openpype.creators.houdini.staticmesh.fbx" label = "Static Mesh (FBX)" - family = "staticMesh" + product_type = "staticMesh" icon = "fa5s.cubes" default_variants = ["Main"] - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): instance_data.update({"node_type": "filmboxfbx"}) instance = super(CreateStaticMesh, self).create( - subset_name, + product_name, instance_data, pre_create_data) @@ -30,7 +30,7 @@ class CreateStaticMesh(plugin.HoudiniCreator): # prepare parms output_path = hou.text.expandString( - "$HIP/pyblish/{}.fbx".format(subset_name) + "$HIP/pyblish/{}.fbx".format(product_name) ) parms = { @@ -48,7 +48,7 @@ class CreateStaticMesh(plugin.HoudiniCreator): instance_node.setParms(parms) # Lock any parameters in this list - to_lock = ["family", "id"] + to_lock = ["productType", "id"] self.lock_parameters(instance_node, to_lock) def get_network_categories(self): @@ -91,7 +91,7 @@ class CreateStaticMesh(plugin.HoudiniCreator): self, variant, task_name, asset_doc, project_name, host_name, instance ): """ - The default subset name templates for Unreal include {asset} and thus + The default prodcut name templates for Unreal include {asset} and thus we should pass that along as dynamic data. """ dynamic_data = super(CreateStaticMesh, self).get_dynamic_data( diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_usd.py b/client/ayon_core/hosts/houdini/plugins/create/create_usd.py index db9c77fffe..ee05639368 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_usd.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_usd.py @@ -10,24 +10,24 @@ class CreateUSD(plugin.HoudiniCreator): """Universal Scene Description""" identifier = "io.openpype.creators.houdini.usd" label = "USD (experimental)" - family = "usd" + product_type = "usd" icon = "gears" enabled = False - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): instance_data.pop("active", None) instance_data.update({"node_type": "usd"}) instance = super(CreateUSD, self).create( - subset_name, + product_name, instance_data, pre_create_data) # type: CreatedInstance instance_node = hou.node(instance.get("instance_node")) parms = { - "lopoutput": "$HIP/pyblish/{}.usd".format(subset_name), + "lopoutput": "$HIP/pyblish/{}.usd".format(product_name), "enableoutputprocessor_simplerelativepaths": False, } @@ -40,7 +40,7 @@ class CreateUSD(plugin.HoudiniCreator): to_lock = [ "fileperframe", # Lock some Avalon attributes - "family", + "productType", "id", ] self.lock_parameters(instance_node, to_lock) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_usdrender.py b/client/ayon_core/hosts/houdini/plugins/create/create_usdrender.py index 72a2d2fc7f..0a5c8896a8 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_usdrender.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_usdrender.py @@ -8,10 +8,10 @@ class CreateUSDRender(plugin.HoudiniCreator): """USD Render ROP in /stage""" identifier = "io.openpype.creators.houdini.usdrender" label = "USD Render (experimental)" - family = "usdrender" + product_type = "usdrender" icon = "magic" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): import hou # noqa instance_data["parent"] = hou.node("/stage") @@ -21,7 +21,7 @@ class CreateUSDRender(plugin.HoudiniCreator): instance_data.update({"node_type": "usdrender"}) instance = super(CreateUSDRender, self).create( - subset_name, + product_name, instance_data, pre_create_data) # type: CreatedInstance @@ -37,5 +37,5 @@ class CreateUSDRender(plugin.HoudiniCreator): instance_node.setParms(parms) # Lock some Avalon attributes - to_lock = ["family", "id"] + to_lock = ["productType", "id"] self.lock_parameters(instance_node, to_lock) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_vbd_cache.py b/client/ayon_core/hosts/houdini/plugins/create/create_vbd_cache.py index 507917b7a5..9ac7ebdff7 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_vbd_cache.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_vbd_cache.py @@ -12,10 +12,10 @@ class CreateVDBCache(plugin.HoudiniCreator): identifier = "io.openpype.creators.houdini.vdbcache" name = "vbdcache" label = "VDB Cache" - family = "vdbcache" + product_type = "vdbcache" icon = "cloud" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): import hou instance_data.pop("active", None) @@ -24,14 +24,14 @@ class CreateVDBCache(plugin.HoudiniCreator): "creator_attributes", dict()) creator_attributes["farm"] = pre_create_data["farm"] instance = super(CreateVDBCache, self).create( - subset_name, + product_name, instance_data, pre_create_data) # type: CreatedInstance instance_node = hou.node(instance.get("instance_node")) file_path = "{}{}".format( hou.text.expandString("$HIP/pyblish/"), - "{}.$F4.vdb".format(subset_name)) + "{}.$F4.vdb".format(product_name)) parms = { "sopoutput": file_path, "initsim": True, diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py index 609828e201..739796dc7c 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py @@ -12,14 +12,14 @@ class CreateVrayROP(plugin.HoudiniCreator): identifier = "io.openpype.creators.houdini.vray_rop" label = "VRay ROP" - family = "vray_rop" + product_type = "vray_rop" icon = "magic" ext = "exr" # Default to split export and render jobs export_job = True - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): instance_data.pop("active", None) instance_data.update({"node_type": "vray_renderer"}) @@ -29,7 +29,7 @@ class CreateVrayROP(plugin.HoudiniCreator): instance_data["farm"] = pre_create_data.get("farm") instance = super(CreateVrayROP, self).create( - subset_name, + product_name, instance_data, pre_create_data) # type: CreatedInstance @@ -57,9 +57,9 @@ class CreateVrayROP(plugin.HoudiniCreator): if pre_create_data.get("export_job"): scene_filepath = \ - "{export_dir}{subset_name}/{subset_name}.$F4.vrscene".format( + "{export_dir}{product_name}/{product_name}.$F4.vrscene".format( export_dir=hou.text.expandString("$HIP/pyblish/vrscene/"), - subset_name=subset_name, + product_name=product_name, ) # Setting render_export_mode to "2" because that's for # "Export only" ("1" is for "Export & Render") @@ -81,16 +81,16 @@ class CreateVrayROP(plugin.HoudiniCreator): instance_data["RenderElement"] = pre_create_data.get("render_element_enabled") # noqa if pre_create_data.get("render_element_enabled", True): # Vray has its own tag for AOV file output - filepath = "{renders_dir}{subset_name}/{subset_name}.{fmt}".format( + filepath = "{renders_dir}{product_name}/{product_name}.{fmt}".format( renders_dir=hou.text.expandString("$HIP/pyblish/renders/"), - subset_name=subset_name, + product_name=product_name, fmt="${aov}.$F4.{ext}".format(aov="AOV", ext=ext) ) filepath = "{}{}".format( hou.text.expandString("$HIP/pyblish/renders/"), - "{}/{}.${}.$F4.{}".format(subset_name, - subset_name, + "{}/{}.${}.$F4.{}".format(product_name, + product_name, "AOV", ext) ) @@ -108,9 +108,9 @@ class CreateVrayROP(plugin.HoudiniCreator): }) else: - filepath = "{renders_dir}{subset_name}/{subset_name}.{fmt}".format( + filepath = "{renders_dir}{product_name}/{product_name}.{fmt}".format( renders_dir=hou.text.expandString("$HIP/pyblish/renders/"), - subset_name=subset_name, + product_name=product_name, fmt="$F4.{ext}".format(ext=ext) ) parms.update({ @@ -125,7 +125,7 @@ class CreateVrayROP(plugin.HoudiniCreator): instance_node.setParms(parms) # lock parameters from AVALON - to_lock = ["family", "id"] + to_lock = ["productType", "id"] self.lock_parameters(instance_node, to_lock) def remove_instances(self, instances): diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py b/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py index 19631566df..b751bce78b 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py @@ -12,7 +12,7 @@ class CreateWorkfile(plugin.HoudiniCreatorBase, AutoCreator): """Workfile auto-creator.""" identifier = "io.openpype.creators.houdini.workfile" label = "Workfile" - family = "workfile" + product_type = "workfile" icon = "fa5.file" default_variant = "Main" @@ -37,7 +37,7 @@ class CreateWorkfile(plugin.HoudiniCreatorBase, AutoCreator): if current_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( variant, task_name, asset_doc, project_name, host_name ) data = { @@ -53,7 +53,7 @@ class CreateWorkfile(plugin.HoudiniCreatorBase, AutoCreator): ) self.log.info("Auto-creating workfile instance...") current_instance = CreatedInstance( - self.family, subset_name, data, self + self.product_type, product_name, data, self ) self._add_instance_to_context(current_instance) elif ( @@ -62,12 +62,12 @@ class CreateWorkfile(plugin.HoudiniCreatorBase, AutoCreator): ): # Update instance context if is not the same asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( variant, task_name, asset_doc, project_name, host_name ) current_instance["folderPath"] = asset_name current_instance["task"] = task_name - current_instance["subset"] = subset_name + current_instance["productName"] = product_name # write workfile information to context container. op_ctx = hou.node(CONTEXT_CONTAINER) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_fbx.py b/client/ayon_core/hosts/houdini/plugins/load/load_fbx.py index cc1a746d93..c750874719 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_fbx.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_fbx.py @@ -88,7 +88,7 @@ class FbxLoader(load.LoaderPlugin): return namespace, node_name - def create_load_node_tree(self, file_path, node_name, subset_name): + def create_load_node_tree(self, file_path, node_name, product_name): """Create Load network. you can start building your tree at any obj level. @@ -118,14 +118,14 @@ class FbxLoader(load.LoaderPlugin): file_node.setParms({"file": file_path}) # Create attribute delete - attribdelete_name = "attribdelete_{}".format(subset_name) + attribdelete_name = "attribdelete_{}".format(product_name) attribdelete = parent_node.createNode("attribdelete", node_name=attribdelete_name) attribdelete.setParms({"ptdel": "fbx_*"}) attribdelete.setInput(0, file_node) # Create a Null node - null_name = "OUT_{}".format(subset_name) + null_name = "OUT_{}".format(product_name) null = parent_node.createNode("null", node_name=null_name) null.setInput(0, attribdelete) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_cache_farm.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_cache_farm.py index f91c253c25..040ad68a1a 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_cache_farm.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_cache_farm.py @@ -44,7 +44,7 @@ class CollectDataforCache(pyblish.api.InstancePlugin): # because ??? for family in instance.data["families"]: if family == "bgeo" or "abc": - instance.data["family"] = "pointcache" + instance.data["productType"] = "pointcache" break instance.data.update({ "plugin": "Houdini", diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_instances.py index 67e8f147d7..edfa78e4d9 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_instances.py @@ -17,7 +17,7 @@ class CollectInstances(pyblish.api.ContextPlugin): Specific node: The specific node is important because it dictates in which way the - subset is being exported. + product is being exported. alembic: will export Alembic file which supports cascading attributes like 'cbId' and 'path' @@ -80,7 +80,9 @@ class CollectInstances(pyblish.api.ContextPlugin): instance = context.create_instance(label) # Include `families` using `family` data - instance.data["families"] = [instance.data["family"]] + product_type = data["family"] + data["productType"] = product_type + instance.data["families"] = [product_type] instance[:] = [node] instance.data["instance_node"] = node.path() @@ -88,7 +90,9 @@ class CollectInstances(pyblish.api.ContextPlugin): def sort_by_family(instance): """Sort by family""" - return instance.data.get("families", instance.data.get("family")) + return instance.data.get( + "families", instance.data.get("productType") + ) # Sort/grouped by family (preserving local index) context[:] = sorted(context, key=sort_by_family) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_instances_usd_layered.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_instances_usd_layered.py index 7a3087c268..38d6ec733d 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_instances_usd_layered.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_instances_usd_layered.py @@ -15,10 +15,10 @@ class CollectInstancesUsdLayered(pyblish.api.ContextPlugin): As opposed to storing `ayon.create.instance` as id on the node we store `pyblish.avalon.usdlayered`. - Additionally this instance has no need for storing family, asset, subset + Additionally this instance has no need for storing family, asset, product or name on the nodes. Instead all information is retrieved solely from the output filepath, which is an Avalon URI: - avalon://{asset}/{subset}.{representation} + avalon://{asset}/{product}.{representation} Each final ROP node is considered a dependency for any of the Configured Save Path layers it sets along the way. As such, the instances shown in @@ -50,14 +50,19 @@ class CollectInstancesUsdLayered(pyblish.api.ContextPlugin): if node.evalParm("id") != "pyblish.avalon.usdlayered": continue - has_family = node.evalParm("family") - assert has_family, "'%s' is missing 'family'" % node.name() + has_product_type = node.evalParm("productType") + assert has_product_type, ( + "'%s' is missing 'productType'" % node.name() + ) self.process_node(node, context) def sort_by_family(instance): """Sort by family""" - return instance.data.get("families", instance.data.get("family")) + return instance.data.get( + "families", + instance.data.get("productType") + ) # Sort/grouped by family (preserving local index) context[:] = sorted(context, key=sort_by_family) @@ -82,8 +87,8 @@ class CollectInstancesUsdLayered(pyblish.api.ContextPlugin): # instead use the "colorbleed.usd" family to integrate. data["publishFamilies"] = ["colorbleed.usd"] - # For now group ALL of them into USD Layer subset group - # Allow this subset to be grouped into a USD Layer on creation + # For now group ALL of them into USD Layer product group + # Allow this product to be grouped into a USD Layer on creation data["subsetGroup"] = "USD Layer" instances = list() @@ -97,7 +102,7 @@ class CollectInstancesUsdLayered(pyblish.api.ContextPlugin): dependency.append(ropnode) dependency.data.update(data) dependency.data.update(dependency_save_data) - dependency.data["family"] = "colorbleed.usd.dependency" + dependency.data["productType"] = "colorbleed.usd.dependency" dependency.data["optional"] = False dependencies.append(dependency) @@ -137,9 +142,9 @@ class CollectInstancesUsdLayered(pyblish.api.ContextPlugin): self.log.warning("Non Avalon URI Layer Path: %s" % save_path) return {} - # Collect asset + subset from URI - name = "{subset} ({asset})".format(**uri_data) - fname = "{asset}_{subset}.{ext}".format(**uri_data) + # Collect asset + product from URI + name = "{product[name]} ({asset})".format(**uri_data) + fname = "{asset}_{product[name]}.{ext}".format(**uri_data) data = dict(uri_data) data["usdSavePath"] = save_path diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py index 791b530eed..380d476e0f 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py @@ -11,7 +11,7 @@ from ayon_core.pipeline import usdlib class CollectUsdBootstrap(pyblish.api.InstancePlugin): """Collect special Asset/Shot bootstrap instances if those are needed. - Some specific subsets are intended to be part of the default structure + Some specific products are intended to be part of the default structure of an "Asset" or "Shot" in our USD pipeline. For example, for an Asset we layer a Model and Shade USD file over each other and expose that in a Asset USD file, ready to use. @@ -30,13 +30,12 @@ class CollectUsdBootstrap(pyblish.api.InstancePlugin): def process(self, instance): - # Detect whether the current subset is a subset in a pipeline + # Detect whether the current product is a product in a pipeline def get_bootstrap(instance): - instance_subset = instance.data["subset"] + instance_product_name = instance.data["productName"] for name, layers in usdlib.PIPELINE.items(): - if instance_subset in set(layers): + if instance_product_name in set(layers): return name # e.g. "asset" - break else: return @@ -72,22 +71,22 @@ class CollectUsdBootstrap(pyblish.api.InstancePlugin): required += list(layers) self.log.debug("Checking required bootstrap: %s" % required) - for subset_name in required: - if self._subset_exists( - project_name, instance, subset_name, asset_doc + for product_name in required: + if self._product_exists( + project_name, instance, product_name, asset_doc ): continue self.log.debug( "Creating {0} USD bootstrap: {1} {2}".format( - bootstrap, asset_name, subset_name + bootstrap, asset_name, product_name ) ) - new = instance.context.create_instance(subset_name) - new.data["subset"] = subset_name - new.data["label"] = "{0} ({1})".format(subset_name, asset_name) - new.data["family"] = "usd.bootstrap" + new = instance.context.create_instance(product_name) + new.data["productName"] = product_name + new.data["label"] = "{0} ({1})".format(product_name, asset_name) + new.data["productType"] = "usd.bootstrap" new.data["comment"] = "Automated bootstrap USD file." new.data["publishFamilies"] = ["usd"] @@ -98,15 +97,17 @@ class CollectUsdBootstrap(pyblish.api.InstancePlugin): for key in ["folderPath"]: new.data[key] = instance.data[key] - def _subset_exists(self, project_name, instance, subset_name, asset_doc): - """Return whether subset exists in current context or in database.""" + def _product_exists( + self, project_name, instance, product_name, asset_doc + ): + """Return whether product exists in current context or in database.""" # Allow it to be created during this publish session context = instance.context asset_doc_name = get_asset_name_identifier(asset_doc) for inst in context: if ( - inst.data["subset"] == subset_name + inst.data["productName"] == product_name and inst.data["folderPath"] == asset_doc_name ): return True @@ -114,7 +115,7 @@ class CollectUsdBootstrap(pyblish.api.InstancePlugin): # Or, if they already exist in the database we can # skip them too. if get_subset_by_name( - project_name, subset_name, asset_doc["_id"], fields=["_id"] + project_name, product_name, asset_doc["_id"], fields=["_id"] ): return True return False diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_layers.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_layers.py index 70dc28e925..f4a96852ff 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_layers.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_layers.py @@ -45,15 +45,15 @@ class CollectUsdLayers(pyblish.api.InstancePlugin): # Create configured layer instances so User can disable updating # specific configured layers for publishing. context = instance.context + product_type = "usdlayer" for layer, save_path in save_layers: name = os.path.basename(save_path) label = "{0} -> {1}".format(instance.data["name"], name) layer_inst = context.create_instance(name) - family = "usdlayer" - layer_inst.data["family"] = family - layer_inst.data["families"] = [family] - layer_inst.data["subset"] = "__stub__" + layer_inst.data["productType"] = product_type + layer_inst.data["families"] = [product_type] + layer_inst.data["productName"] = "__stub__" layer_inst.data["label"] = label layer_inst.data["folderPath"] = instance.data["folderPath"] layer_inst.data["instance_node"] = instance.data["instance_node"] @@ -62,5 +62,5 @@ class CollectUsdLayers(pyblish.api.InstancePlugin): # include layer data layer_inst.append((layer, save_path)) - # Allow this subset to be grouped into a USD Layer on creation + # Allow this product to be grouped into a USD Layer on creation layer_inst.data["subsetGroup"] = "USD Layer" diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py index 56c335f50e..99c61803e6 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py @@ -284,29 +284,29 @@ class ExtractUSDLayered(publish.Extractor): # Compare this dependency with the latest published version # to detect whether we should make this into a new publish # version. If not, skip it. - asset = get_asset_by_name( + asset_doc = get_asset_by_name( project_name, dependency.data["folderPath"], fields=["_id"] ) - subset = get_subset_by_name( + subset_doc = get_subset_by_name( project_name, - dependency.data["subset"], - asset["_id"], + dependency.data["productName"], + asset_doc["_id"], fields=["_id"] ) - if not subset: + if not subset_doc: # Subset doesn't exist yet. Definitely new file - self.log.debug("No existing subset..") + self.log.debug("No existing product..") return False - version = get_last_version_by_subset_id( - project_name, subset["_id"], fields=["_id"] + version_doc = get_last_version_by_subset_id( + project_name, subset_doc["_id"], fields=["_id"] ) - if not version: + if not version_doc: self.log.debug("No existing version..") return False representation = get_representation_by_name( - project_name, ext.lstrip("."), version["_id"] + project_name, ext.lstrip("."), version_doc["_id"] ) if not representation: self.log.debug("No existing representation..") diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_file_extension.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_file_extension.py index 1134f8853a..e9a0397a58 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_file_extension.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_file_extension.py @@ -45,9 +45,9 @@ class ValidateFileExtension(pyblish.api.InstancePlugin): # Create lookup for current family in instance families = [] - family = instance.data.get("family", None) - if family: - families.append(family) + product_type = instance.data.get("productType") + if product_type: + families.append(product_type) families = set(families) # Perform extension check diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_houdini_license_category.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_houdini_license_category.py index 0df858ca4b..9a68c34405 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_houdini_license_category.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_houdini_license_category.py @@ -28,7 +28,7 @@ class ValidateHoudiniNotApprenticeLicense(pyblish.api.InstancePlugin): if hou.isApprentice(): # Find which family was matched with the plug-in - families = {instance.data["family"]} + families = {instance.data["productType"]} families.update(instance.data.get("families", [])) disallowed_families = families.intersection(self.families) families = " ".join(sorted(disallowed_families)).title() diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py index 67807b5366..c4a9951d92 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py @@ -10,7 +10,7 @@ from ayon_core.pipeline.publish import ( RepairAction, ) from ayon_core.hosts.houdini.api.action import SelectInvalidAction -from ayon_core.pipeline.create import get_subset_name +from ayon_core.pipeline.create import get_product_name import hou @@ -53,21 +53,23 @@ class ValidateSubsetName(pyblish.api.InstancePlugin, rop_node = hou.node(instance.data["instance_node"]) - # Check subset name + # Check product name asset_doc = instance.data["assetEntity"] - subset_name = get_subset_name( - family=instance.data["family"], + product_name = get_product_name( + instance.context.data["projctName"], + asset_doc, + instance.data["task"], + instance.context.data["hostName"], + instance.data["productType"], variant=instance.data["variant"], - task_name=instance.data["task"], - asset_doc=asset_doc, dynamic_data={"asset": asset_doc["name"]} ) - if instance.data.get("subset") != subset_name: + if instance.data.get("productName") != product_name: invalid.append(rop_node) cls.log.error( - "Invalid subset name on rop node '%s' should be '%s'.", - rop_node.path(), subset_name + "Invalid product name on rop node '%s' should be '%s'.", + rop_node.path(), product_name ) return invalid @@ -76,20 +78,22 @@ class ValidateSubsetName(pyblish.api.InstancePlugin, def repair(cls, instance): rop_node = hou.node(instance.data["instance_node"]) - # Check subset name + # Check product name asset_doc = instance.data["assetEntity"] - subset_name = get_subset_name( - family=instance.data["family"], + product_name = get_product_name( + instance.context.data["projectName"], + asset_doc, + instance.data["task"], + instance.context.data["hostName"], + instance.data["productType"], variant=instance.data["variant"], - task_name=instance.data["task"], - asset_doc=asset_doc, dynamic_data={"asset": asset_doc["name"]} ) - instance.data["subset"] = subset_name - rop_node.parm("subset").set(subset_name) + instance.data["productName"] = product_name + rop_node.parm("productName").set(product_name) cls.log.debug( - "Subset name on rop node '%s' has been set to '%s'.", - rop_node.path(), subset_name + "Product name on rop node '%s' has been set to '%s'.", + rop_node.path(), product_name ) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_unreal_staticmesh_naming.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_unreal_staticmesh_naming.py index dbee293074..1279dd7f33 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_unreal_staticmesh_naming.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_unreal_staticmesh_naming.py @@ -23,7 +23,7 @@ class ValidateUnrealStaticMeshName(pyblish.api.InstancePlugin, - USP - UCX - This validator also checks if subset name is correct + This validator also checks if product name is correct - {static mesh prefix}_{Asset-Name}{Variant}. """ diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_model_exists.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_model_exists.py index c8b9ed9bab..6d21b59a9c 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_model_exists.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_model_exists.py @@ -19,22 +19,24 @@ class ValidateUSDShadeModelExists(pyblish.api.InstancePlugin): def process(self, instance): project_name = instance.context.data["projectName"] asset_name = instance.data["folderPath"] - subset = instance.data["subset"] + product_name = instance.data["productName"] # Assume shading variation starts after a dot separator - shade_subset = subset.split(".", 1)[0] - model_subset = re.sub("^usdShade", "usdModel", shade_subset) + shade_product_name = product_name.split(".", 1)[0] + model_product_name = re.sub( + "^usdShade", "usdModel", shade_product_name + ) asset_doc = instance.data.get("assetEntity") if not asset_doc: raise RuntimeError("Asset document is not filled on instance.") subset_doc = get_subset_by_name( - project_name, model_subset, asset_doc["_id"], fields=["_id"] + project_name, model_product_name, asset_doc["_id"], fields=["_id"] ) if not subset_doc: raise PublishValidationError( - ("USD Model subset not found: " - "{} ({})").format(model_subset, asset_name), + ("USD Model product not found: " + "{} ({})").format(model_product_name, asset_name), title=self.label ) From cf0ac31aa63ea52663a8ad01ba01dd5cac7ab3e3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 22 Feb 2024 17:06:48 +0100 Subject: [PATCH 250/573] harmony is using product name and type --- client/ayon_core/hosts/harmony/api/README.md | 4 +-- .../harmony/js/loaders/ImageSequenceLoader.js | 6 ++--- .../harmony/js/loaders/TemplateLoader.js | 6 ++--- .../plugins/create/create_farm_render.py | 2 +- .../harmony/plugins/create/create_render.py | 2 +- .../harmony/plugins/create/create_template.py | 2 +- .../hosts/harmony/plugins/load/load_audio.py | 6 ++--- .../harmony/plugins/load/load_background.py | 6 ++--- .../plugins/load/load_imagesequence.py | 6 ++--- .../harmony/plugins/load/load_palette.py | 6 ++--- .../plugins/load/load_template_workfile.py | 6 ++--- .../plugins/publish/collect_farm_render.py | 26 ++++++++++--------- .../plugins/publish/collect_instances.py | 18 +++++++++---- .../plugins/publish/collect_palettes.py | 7 ++--- .../plugins/publish/collect_workfile.py | 24 ++++++++--------- .../plugins/publish/extract_template.py | 4 ++- .../publish/help/validate_instances.xml | 6 ++--- 17 files changed, 75 insertions(+), 62 deletions(-) diff --git a/client/ayon_core/hosts/harmony/api/README.md b/client/ayon_core/hosts/harmony/api/README.md index 457e22fb2e..5b90d45f98 100644 --- a/client/ayon_core/hosts/harmony/api/README.md +++ b/client/ayon_core/hosts/harmony/api/README.md @@ -204,7 +204,7 @@ class CreateComposite(harmony.Creator): name = "compositeDefault" label = "Composite" - family = "mindbender.template" + product_type = "mindbender.template" def __init__(self, *args, **kwargs): super(CreateComposite, self).__init__(*args, **kwargs) @@ -221,7 +221,7 @@ class CreateRender(harmony.Creator): name = "writeDefault" label = "Write" - family = "mindbender.imagesequence" + product_type = "mindbender.imagesequence" node_type = "WRITE" def __init__(self, *args, **kwargs): diff --git a/client/ayon_core/hosts/harmony/js/loaders/ImageSequenceLoader.js b/client/ayon_core/hosts/harmony/js/loaders/ImageSequenceLoader.js index 25afeff214..ebbd7163f9 100644 --- a/client/ayon_core/hosts/harmony/js/loaders/ImageSequenceLoader.js +++ b/client/ayon_core/hosts/harmony/js/loaders/ImageSequenceLoader.js @@ -88,7 +88,7 @@ ImageSequenceLoader.getUniqueColumnName = function(columnPrefix) { * var args = [ * files, // Files in file sequences. * asset, // Asset name. - * subset, // Subset name. + * productName, // Product name. * startFrame, // Sequence starting frame. * groupId // Unique group ID (uuid4). * ]; @@ -106,7 +106,7 @@ ImageSequenceLoader.prototype.importFiles = function(args) { var doc = $.scn; var files = args[0]; var asset = args[1]; - var subset = args[2]; + var productName = args[2]; var startFrame = args[3]; var groupId = args[4]; var vectorFormat = null; @@ -124,7 +124,7 @@ ImageSequenceLoader.prototype.importFiles = function(args) { var num = 0; var name = ''; do { - name = asset + '_' + (num++) + '_' + subset; + name = asset + '_' + (num++) + '_' + productName; } while (currentGroup.getNodeByName(name) != null); extension = filename.substr(pos+1).toLowerCase(); diff --git a/client/ayon_core/hosts/harmony/js/loaders/TemplateLoader.js b/client/ayon_core/hosts/harmony/js/loaders/TemplateLoader.js index 06ef1671ea..78167fcb39 100644 --- a/client/ayon_core/hosts/harmony/js/loaders/TemplateLoader.js +++ b/client/ayon_core/hosts/harmony/js/loaders/TemplateLoader.js @@ -31,7 +31,7 @@ var TemplateLoader = function() {}; * var args = [ * templatePath, // Path to tpl file. * assetName, // Asset name. - * subsetName, // Subset name. + * productName, // Product name. * groupId // unique ID (uuid4) * ]; */ @@ -39,7 +39,7 @@ TemplateLoader.prototype.loadContainer = function(args) { var doc = $.scn; var templatePath = args[0]; var assetName = args[1]; - var subset = args[2]; + var productName = args[2]; var groupId = args[3]; // Get the current group @@ -62,7 +62,7 @@ TemplateLoader.prototype.loadContainer = function(args) { var num = 0; var containerGroupName = ''; do { - containerGroupName = assetName + '_' + (num++) + '_' + subset; + containerGroupName = assetName + '_' + (num++) + '_' + productName; } while (currentGroup.getNodeByName(containerGroupName) != null); // import the template diff --git a/client/ayon_core/hosts/harmony/plugins/create/create_farm_render.py b/client/ayon_core/hosts/harmony/plugins/create/create_farm_render.py index 6b19764181..16c403de6a 100644 --- a/client/ayon_core/hosts/harmony/plugins/create/create_farm_render.py +++ b/client/ayon_core/hosts/harmony/plugins/create/create_farm_render.py @@ -9,7 +9,7 @@ class CreateFarmRender(plugin.Creator): name = "renderDefault" label = "Render on Farm" - family = "renderFarm" + product_type = "renderFarm" node_type = "WRITE" def __init__(self, *args, **kwargs): diff --git a/client/ayon_core/hosts/harmony/plugins/create/create_render.py b/client/ayon_core/hosts/harmony/plugins/create/create_render.py index 0a2cd33551..23e02bd8a5 100644 --- a/client/ayon_core/hosts/harmony/plugins/create/create_render.py +++ b/client/ayon_core/hosts/harmony/plugins/create/create_render.py @@ -9,7 +9,7 @@ class CreateRender(plugin.Creator): name = "renderDefault" label = "Render" - family = "render" + product_type = "render" node_type = "WRITE" def __init__(self, *args, **kwargs): diff --git a/client/ayon_core/hosts/harmony/plugins/create/create_template.py b/client/ayon_core/hosts/harmony/plugins/create/create_template.py index 4f3fd85f00..c16e429436 100644 --- a/client/ayon_core/hosts/harmony/plugins/create/create_template.py +++ b/client/ayon_core/hosts/harmony/plugins/create/create_template.py @@ -6,7 +6,7 @@ class CreateTemplate(plugin.Creator): name = "templateDefault" label = "Template" - family = "harmony.template" + product_type = "harmony.template" def __init__(self, *args, **kwargs): super(CreateTemplate, self).__init__(*args, **kwargs) diff --git a/client/ayon_core/hosts/harmony/plugins/load/load_audio.py b/client/ayon_core/hosts/harmony/plugins/load/load_audio.py index 14389166d7..1017d6c2a2 100644 --- a/client/ayon_core/hosts/harmony/plugins/load/load_audio.py +++ b/client/ayon_core/hosts/harmony/plugins/load/load_audio.py @@ -45,12 +45,12 @@ class ImportAudioLoader(load.LoaderPlugin): {"function": func, "args": [context["subset"]["name"], wav_file]} ) - subset_name = context["subset"]["name"] + product_name = context["subset"]["name"] return harmony.containerise( - subset_name, + product_name, namespace, - subset_name, + product_name, context, self.__class__.__name__ ) diff --git a/client/ayon_core/hosts/harmony/plugins/load/load_background.py b/client/ayon_core/hosts/harmony/plugins/load/load_background.py index 1c61cfa7a4..cc664bb24f 100644 --- a/client/ayon_core/hosts/harmony/plugins/load/load_background.py +++ b/client/ayon_core/hosts/harmony/plugins/load/load_background.py @@ -254,7 +254,7 @@ class BackgroundLoader(load.LoaderPlugin): bg_folder = os.path.dirname(path) - subset_name = context["subset"]["name"] + product_name = context["subset"]["name"] # read_node_name += "_{}".format(uuid.uuid4()) container_nodes = [] @@ -272,9 +272,9 @@ class BackgroundLoader(load.LoaderPlugin): container_nodes.append(read_node) return harmony.containerise( - subset_name, + product_name, namespace, - subset_name, + product_name, context, self.__class__.__name__, nodes=container_nodes diff --git a/client/ayon_core/hosts/harmony/plugins/load/load_imagesequence.py b/client/ayon_core/hosts/harmony/plugins/load/load_imagesequence.py index 4d87272de8..db67ff1123 100644 --- a/client/ayon_core/hosts/harmony/plugins/load/load_imagesequence.py +++ b/client/ayon_core/hosts/harmony/plugins/load/load_imagesequence.py @@ -47,7 +47,7 @@ class ImageSequenceLoader(load.LoaderPlugin): files.append(fname.parent.joinpath(remainder[0]).as_posix()) asset = context["asset"]["name"] - subset = context["subset"]["name"] + product_name = context["subset"]["name"] group_id = str(uuid.uuid4()) read_node = harmony.send( @@ -56,7 +56,7 @@ class ImageSequenceLoader(load.LoaderPlugin): "args": [ files, asset, - subset, + product_name, 1, group_id ] @@ -64,7 +64,7 @@ class ImageSequenceLoader(load.LoaderPlugin): )["result"] return harmony.containerise( - f"{asset}_{subset}", + f"{asset}_{product_name}", namespace, read_node, context, diff --git a/client/ayon_core/hosts/harmony/plugins/load/load_palette.py b/client/ayon_core/hosts/harmony/plugins/load/load_palette.py index aa5894e026..1794ffda5e 100644 --- a/client/ayon_core/hosts/harmony/plugins/load/load_palette.py +++ b/client/ayon_core/hosts/harmony/plugins/load/load_palette.py @@ -27,8 +27,8 @@ class ImportPaletteLoader(load.LoaderPlugin): ) def load_palette(self, representation): - subset_name = representation["context"]["subset"] - name = subset_name.replace("palette", "") + product_name = representation["context"]["subset"] + name = product_name.replace("palette", "") # Overwrite palette on disk. scene_path = harmony.send( @@ -44,7 +44,7 @@ class ImportPaletteLoader(load.LoaderPlugin): harmony.save_scene() - msg = "Updated {}.".format(subset_name) + msg = "Updated {}.".format(product_name) msg += " You need to reload the scene to see the changes.\n" msg += "Please save workfile when ready and use Workfiles " msg += "to reopen it." diff --git a/client/ayon_core/hosts/harmony/plugins/load/load_template_workfile.py b/client/ayon_core/hosts/harmony/plugins/load/load_template_workfile.py index 0ea46f8f67..65f4fe6d0a 100644 --- a/client/ayon_core/hosts/harmony/plugins/load/load_template_workfile.py +++ b/client/ayon_core/hosts/harmony/plugins/load/load_template_workfile.py @@ -40,12 +40,12 @@ class ImportTemplateLoader(load.LoaderPlugin): shutil.rmtree(temp_dir) - subset_name = context["subset"]["name"] + product_name = context["subset"]["name"] return harmony.containerise( - subset_name, + product_name, namespace, - subset_name, + product_name, context, self.__class__.__name__ ) diff --git a/client/ayon_core/hosts/harmony/plugins/publish/collect_farm_render.py b/client/ayon_core/hosts/harmony/plugins/publish/collect_farm_render.py index 6a9c349185..dea13ffa27 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/collect_farm_render.py +++ b/client/ayon_core/hosts/harmony/plugins/publish/collect_farm_render.py @@ -80,7 +80,7 @@ class CollectFarmRender(publish.AbstractCollectRender): for frame in range(start, end + 1): expected_files.append( path / "{}-{}.{}".format( - render_instance.subset, + render_instance.productName, str(frame).rjust(int(info[2]) + 1, "0"), ext ) @@ -89,7 +89,7 @@ class CollectFarmRender(publish.AbstractCollectRender): return expected_files def get_instances(self, context): - """Get instances per Write node in `renderFarm` family.""" + """Get instances per Write node in `renderFarm` product type.""" version = None if self.sync_workfile_version: version = context.data["version"] @@ -111,7 +111,10 @@ class CollectFarmRender(publish.AbstractCollectRender): if "container" in data["id"]: continue - if data["family"] != "renderFarm": + product_type = data.get("productType") + if product_type is None: + product_type = data.get("family") + if product_type != "renderFarm": continue # 0 - filename / 1 - type / 2 - zeros / 3 - start / 4 - enabled @@ -124,15 +127,14 @@ class CollectFarmRender(publish.AbstractCollectRender): # TODO: handle pixel aspect and frame step # TODO: set Deadline stuff (pools, priority, etc. by presets) - # because of using 'renderFarm' as a family, replace 'Farm' with - # capitalized task name - issue of avalon-core Creator app - subset_name = node.split("/")[1] - task_name = context.data["anatomyData"]["task"][ - "name"].capitalize() + # because of using 'renderFarm' as a product type, replace 'Farm' + # with capitalized task name - issue of Creator tool + product_name = node.split("/")[1] + task_name = context.data["task"].capitalize() replace_str = "" - if task_name.lower() not in subset_name.lower(): + if task_name.lower() not in product_name.lower(): replace_str = task_name - subset_name = subset_name.replace( + product_name = product_name.replace( 'Farm', replace_str) @@ -141,7 +143,7 @@ class CollectFarmRender(publish.AbstractCollectRender): time=get_formatted_current_time(), source=context.data["currentFile"], label=node.split("/")[1], - subset=subset_name, + productName=product_name, folderPath=folder_path, task=task_name, attachTo=False, @@ -151,7 +153,7 @@ class CollectFarmRender(publish.AbstractCollectRender): priority=50, name=node.split("/")[1], - family="render.farm", + productType="render.farm", families=["render.farm"], farm=True, diff --git a/client/ayon_core/hosts/harmony/plugins/publish/collect_instances.py b/client/ayon_core/hosts/harmony/plugins/publish/collect_instances.py index 9ce99a3c3d..fd394c0b43 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/collect_instances.py +++ b/client/ayon_core/hosts/harmony/plugins/publish/collect_instances.py @@ -19,7 +19,7 @@ class CollectInstances(pyblish.api.ContextPlugin): label = "Instances" order = pyblish.api.CollectorOrder hosts = ["harmony"] - families_mapping = { + product_type_mapping = { "render": ["review", "ftrack"], "harmony.template": [], "palette": ["palette", "ftrack"] @@ -49,8 +49,13 @@ class CollectInstances(pyblish.api.ContextPlugin): if "container" in data["id"]: continue - # skip render farm family as it is collected separately - if data["family"] == "renderFarm": + product_type = data.get("productType") + if product_type is None: + product_type = data["family"] + data["productType"] = product_type + + # skip render farm product type as it is collected separately + if product_type == "renderFarm": continue instance = context.create_instance(node.split("/")[-1]) @@ -59,11 +64,14 @@ class CollectInstances(pyblish.api.ContextPlugin): instance.data["publish"] = harmony.send( {"function": "node.getEnable", "args": [node]} )["result"] - instance.data["families"] = self.families_mapping[data["family"]] + + families = [product_type] + families.extend(self.product_type_mapping[product_type]) + instance.data["families"] = families # If set in plugin, pair the scene Version in ftrack with # thumbnails and review media. - if (self.pair_media and instance.data["family"] == "scene"): + if (self.pair_media and product_type == "scene"): context.data["scene_instance"] = instance # Produce diagnostic message for any graphical diff --git a/client/ayon_core/hosts/harmony/plugins/publish/collect_palettes.py b/client/ayon_core/hosts/harmony/plugins/publish/collect_palettes.py index 66b1ee6085..6e8583c340 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/collect_palettes.py +++ b/client/ayon_core/hosts/harmony/plugins/publish/collect_palettes.py @@ -33,14 +33,15 @@ class CollectPalettes(pyblish.api.ContextPlugin): return folder_path = context.data["folderPath"] + product_type = "harmony.palette" for name, id in palettes.items(): instance = context.create_instance(name) instance.data.update({ "id": id, - "family": "harmony.palette", - 'families': [], + "productType": product_type, + "families": [product_type], "folderPath": folder_path, - "subset": "{}{}".format("palette", name) + "productName": "{}{}".format("palette", name) }) self.log.info( "Created instance:\n" + json.dumps( diff --git a/client/ayon_core/hosts/harmony/plugins/publish/collect_workfile.py b/client/ayon_core/hosts/harmony/plugins/publish/collect_workfile.py index 1ea1f15124..54a2fd3b48 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/collect_workfile.py +++ b/client/ayon_core/hosts/harmony/plugins/publish/collect_workfile.py @@ -3,7 +3,7 @@ import os import pyblish.api -from ayon_core.pipeline.create import get_subset_name +from ayon_core.pipeline.create import get_product_name class CollectWorkfile(pyblish.api.ContextPlugin): @@ -15,26 +15,26 @@ class CollectWorkfile(pyblish.api.ContextPlugin): def process(self, context): """Plugin entry point.""" - family = "workfile" + product_type = "workfile" basename = os.path.basename(context.data["currentFile"]) - subset = get_subset_name( - family, - "", - context.data["anatomyData"]["task"]["name"], + product_name = get_product_name( + context.data["projectName"], context.data["assetEntity"], - context.data["anatomyData"]["project"]["name"], - host_name=context.data["hostName"], + context.data["task"], + context.data["hostName"], + product_type, + "", project_settings=context.data["project_settings"] ) # Create instance - instance = context.create_instance(subset) + instance = context.create_instance(product_name) instance.data.update({ - "subset": subset, + "productName": product_name, "label": basename, "name": basename, - "family": family, - "families": [family], + "productType": product_type, + "families": [product_type], "representations": [], "folderPath": context.data["folderPath"] }) diff --git a/client/ayon_core/hosts/harmony/plugins/publish/extract_template.py b/client/ayon_core/hosts/harmony/plugins/publish/extract_template.py index c481a34454..b2c7fa8174 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/extract_template.py +++ b/client/ayon_core/hosts/harmony/plugins/publish/extract_template.py @@ -75,7 +75,9 @@ class ExtractTemplate(publish.Extractor): instance.data["representations"] = [representation] instance.data["version_name"] = "{}_{}".format( - instance.data["subset"], instance.context.data["task"]) + instance.data["productName"], + instance.context.data["task"] + ) def get_backdrops(self, node: str) -> list: """Get backdrops for the node. diff --git a/client/ayon_core/hosts/harmony/plugins/publish/help/validate_instances.xml b/client/ayon_core/hosts/harmony/plugins/publish/help/validate_instances.xml index 3b040e8ea8..67ad7e2d21 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/help/validate_instances.xml +++ b/client/ayon_core/hosts/harmony/plugins/publish/help/validate_instances.xml @@ -3,9 +3,9 @@ Subset context -## Invalid subset context +## Invalid product context -Asset name found '{found}' in subsets, expected '{expected}'. +Asset name found '{found}' in products, expected '{expected}'. ### How to repair? @@ -19,7 +19,7 @@ If this is unwanted, close workfile and open again, that way different asset val ### __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 product "renderCompositingDefault" from asset "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing product for "Robot" asset stayed in the workfile.) \ No newline at end of file From 3edbf698a039905917b34b5e9f2c9509ce0eeb15 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 22 Feb 2024 22:17:21 +0100 Subject: [PATCH 251/573] Update issue template titles to remove prefixes. Improve clarity and consistency in issue reporting templates. --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- .github/ISSUE_TEMPLATE/enhancement_request.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 655ffe289e..5a0ed1ae1d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,6 +1,6 @@ name: Bug Report description: File a bug report -title: 'Bug: ' +title: '' labels: - 'type: bug' body: diff --git a/.github/ISSUE_TEMPLATE/enhancement_request.yml b/.github/ISSUE_TEMPLATE/enhancement_request.yml index 52b49e0481..da4d0d9319 100644 --- a/.github/ISSUE_TEMPLATE/enhancement_request.yml +++ b/.github/ISSUE_TEMPLATE/enhancement_request.yml @@ -1,6 +1,6 @@ name: Enhancement Request description: Create a report to help us enhance a particular feature -title: "Enhancement: " +title: "" labels: - "type: enhancement" body: From 800249daab548f8f93ffa517dd8011f3a3fa1bb0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 10:19:08 +0100 Subject: [PATCH 252/573] fusion is using product name and type --- client/ayon_core/hosts/fusion/api/plugin.py | 56 ++++++++++++------- .../plugins/create/create_image_saver.py | 2 +- .../fusion/plugins/create/create_saver.py | 2 +- .../fusion/plugins/create/create_workfile.py | 19 ++++--- .../plugins/publish/collect_instances.py | 10 ++-- .../fusion/plugins/publish/collect_render.py | 12 ++-- .../publish/validate_unique_subsets.py | 37 ++++++------ 7 files changed, 81 insertions(+), 57 deletions(-) diff --git a/client/ayon_core/hosts/fusion/api/plugin.py b/client/ayon_core/hosts/fusion/api/plugin.py index 3bf810ca23..95db8126e7 100644 --- a/client/ayon_core/hosts/fusion/api/plugin.py +++ b/client/ayon_core/hosts/fusion/api/plugin.py @@ -33,14 +33,16 @@ class GenericCreateSaver(Creator): # TODO: This should be renamed together with Nuke so it is aligned temp_rendering_path_template = ( - "{workdir}/renders/fusion/{subset}/{subset}.{frame}.{ext}") + "{workdir}/renders/fusion/{product[name]}/" + "{product[name]}.{frame}.{ext}" + ) - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): self.pass_pre_attributes_to_instance(instance_data, pre_create_data) instance = CreatedInstance( - family=self.family, - subset_name=subset_name, + product_type=self.product_type, + product_name=product_name, data=instance_data, creator=self, ) @@ -111,23 +113,23 @@ class GenericCreateSaver(Creator): tool.SetData(f"openpype.{key}", value) def _update_tool_with_data(self, tool, data): - """Update tool node name and output path based on subset data""" - if "subset" not in data: + """Update tool node name and output path based on product data""" + if "productName" not in data: return - original_subset = tool.GetData("openpype.subset") + original_product_name = tool.GetData("openpype.productName") original_format = tool.GetData( "openpype.creator_attributes.image_format" ) - subset = data["subset"] + product_name = data["productName"] if ( - original_subset != subset + original_product_name != product_name or original_format != data["creator_attributes"]["image_format"] ): - self._configure_saver_tool(data, tool, subset) + self._configure_saver_tool(data, tool, product_name) - def _configure_saver_tool(self, data, tool, subset): + def _configure_saver_tool(self, data, tool, product_name): formatting_data = deepcopy(data) # get frame padding from anatomy templates @@ -137,25 +139,39 @@ class GenericCreateSaver(Creator): ext = data["creator_attributes"]["image_format"] # Subset change detected + product_type = formatting_data["productType"] + f_product_name = formatting_data["productName"] + + folder_path = formatting_data["folderPath"] + folder_name = folder_path.rsplit("/", 1)[-1] + workdir = os.path.normpath(os.getenv("AYON_WORKDIR")) formatting_data.update({ "workdir": workdir, "frame": "0" * frame_padding, "ext": ext, "product": { - "name": formatting_data["subset"], - "type": formatting_data["family"], + "name": f_product_name, + "type": product_type, }, + # TODO add more variants for 'folder' and 'task' + "folder": { + "name": folder_name, + }, + "task": { + "name": data["task"], + }, + # Backwards compatibility + "asset": folder_name, + "subset": f_product_name, + "family": product_type, }) # build file path to render # TODO make sure the keys are available in 'formatting_data' temp_rendering_path_template = ( self.temp_rendering_path_template - .replace("{product[name]}", "{subset}") - .replace("{product[type]}", "{family}") - .replace("{folder[name]}", "{asset}") - .replace("{task[name]}", "{task}") + .replace("{task}", "{task[name]}") ) filepath = temp_rendering_path_template.format(**formatting_data) @@ -164,9 +180,9 @@ class GenericCreateSaver(Creator): tool["Clip"] = comp.ReverseMapPath(os.path.normpath(filepath)) # Rename tool - if tool.Name != subset: - print(f"Renaming {tool.Name} -> {subset}") - tool.SetAttrs({"TOOLS_Name": subset}) + if tool.Name != product_name: + print(f"Renaming {tool.Name} -> {product_name}") + tool.SetAttrs({"TOOLS_Name": product_name}) def get_managed_tool_data(self, tool): """Return data of the tool if it matches creator identifier""" diff --git a/client/ayon_core/hosts/fusion/plugins/create/create_image_saver.py b/client/ayon_core/hosts/fusion/plugins/create/create_image_saver.py index 856d86cff6..8110898ae9 100644 --- a/client/ayon_core/hosts/fusion/plugins/create/create_image_saver.py +++ b/client/ayon_core/hosts/fusion/plugins/create/create_image_saver.py @@ -17,7 +17,7 @@ class CreateImageSaver(GenericCreateSaver): identifier = "io.openpype.creators.fusion.imagesaver" label = "Image (saver)" name = "image" - family = "image" + product_type = "image" description = "Fusion Saver to generate image" default_frame = 0 diff --git a/client/ayon_core/hosts/fusion/plugins/create/create_saver.py b/client/ayon_core/hosts/fusion/plugins/create/create_saver.py index 1a0dad7060..b5abb2d949 100644 --- a/client/ayon_core/hosts/fusion/plugins/create/create_saver.py +++ b/client/ayon_core/hosts/fusion/plugins/create/create_saver.py @@ -12,7 +12,7 @@ class CreateSaver(GenericCreateSaver): identifier = "io.openpype.creators.fusion.saver" label = "Render (saver)" name = "render" - family = "render" + product_type = "render" description = "Fusion Saver to generate image sequence" default_frame_range_option = "asset_db" diff --git a/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py b/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py index 08d39b0145..1f49d4bbc5 100644 --- a/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py @@ -10,7 +10,7 @@ from ayon_core.pipeline import ( class FusionWorkfileCreator(AutoCreator): identifier = "workfile" - family = "workfile" + product_type = "workfile" label = "Workfile" icon = "fa5.file" @@ -27,9 +27,12 @@ class FusionWorkfileCreator(AutoCreator): if not data: return + product_name = data.get("productName") + if product_name is None: + product_name = data["subset"] instance = CreatedInstance( - family=self.family, - subset_name=data["subset"], + product_type=self.product_type, + product_name=product_name, data=data, creator=self ) @@ -59,7 +62,7 @@ class FusionWorkfileCreator(AutoCreator): existing_instance = None for instance in self.create_context.instances: - if instance.family == self.family: + if instance.product_type == self.product_type: existing_instance = instance break @@ -75,7 +78,7 @@ class FusionWorkfileCreator(AutoCreator): if existing_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( self.default_variant, task_name, asset_doc, project_name, host_name ) @@ -90,7 +93,7 @@ class FusionWorkfileCreator(AutoCreator): )) new_instance = CreatedInstance( - self.family, subset_name, data, self + self.product_type, product_name, data, self ) new_instance.transient_data["comp"] = comp self._add_instance_to_context(new_instance) @@ -100,10 +103,10 @@ class FusionWorkfileCreator(AutoCreator): or existing_instance["task"] != task_name ): asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( self.default_variant, task_name, asset_doc, project_name, host_name ) existing_instance["folderPath"] = asset_name existing_instance["task"] = task_name - existing_instance["subset"] = subset_name + existing_instance["productName"] = product_name diff --git a/client/ayon_core/hosts/fusion/plugins/publish/collect_instances.py b/client/ayon_core/hosts/fusion/plugins/publish/collect_instances.py index a0131248e8..2cbd4d82f4 100644 --- a/client/ayon_core/hosts/fusion/plugins/publish/collect_instances.py +++ b/client/ayon_core/hosts/fusion/plugins/publish/collect_instances.py @@ -26,7 +26,7 @@ class CollectInstanceData(pyblish.api.InstancePlugin): instance.data["frame_range_source"] = frame_range_source # get asset frame ranges to all instances - # render family instances `asset_db` render target + # render product type instances `asset_db` render target start = context.data["frameStart"] end = context.data["frameEnd"] handle_start = context.data["handleStart"] @@ -34,7 +34,7 @@ class CollectInstanceData(pyblish.api.InstancePlugin): start_with_handle = start - handle_start end_with_handle = end + handle_end - # conditions for render family instances + # conditions for render product type instances if frame_range_source == "render_range": # set comp render frame ranges start = context.data["renderFrameStart"] @@ -70,11 +70,11 @@ class CollectInstanceData(pyblish.api.InstancePlugin): end_with_handle = frame # Include start and end render frame in label - subset = instance.data["subset"] + product_name = instance.data["productName"] label = ( - "{subset} ({start}-{end}) [{handle_start}-{handle_end}]" + "{product_name} ({start}-{end}) [{handle_start}-{handle_end}]" ).format( - subset=subset, + product_name=product_name, start=int(start), end=int(end), handle_start=int(handle_start), diff --git a/client/ayon_core/hosts/fusion/plugins/publish/collect_render.py b/client/ayon_core/hosts/fusion/plugins/publish/collect_render.py index 0a0e4b38af..0eea061827 100644 --- a/client/ayon_core/hosts/fusion/plugins/publish/collect_render.py +++ b/client/ayon_core/hosts/fusion/plugins/publish/collect_render.py @@ -49,17 +49,17 @@ class CollectFusionRender( if not inst.data.get("active", True): continue - family = inst.data["family"] - if family not in ["render", "image"]: + product_type = inst.data["productType"] + if product_type not in ["render", "image"]: continue task_name = context.data["task"] tool = inst.data["transientData"]["tool"] instance_families = inst.data.get("families", []) - subset_name = inst.data["subset"] + product_name = inst.data["productName"] instance = FusionRenderInstance( - family=family, + productType=product_type, tool=tool, workfileComp=comp, families=instance_families, @@ -67,13 +67,13 @@ class CollectFusionRender( time="", source=current_file, label=inst.data["label"], - subset=subset_name, + productName=product_name, folderPath=inst.data["folderPath"], task=task_name, attachTo=False, setMembers='', publish=True, - name=subset_name, + name=product_name, resolutionWidth=comp_frame_format_prefs.get("Width"), resolutionHeight=comp_frame_format_prefs.get("Height"), pixelAspect=aspect_x / aspect_y, diff --git a/client/ayon_core/hosts/fusion/plugins/publish/validate_unique_subsets.py b/client/ayon_core/hosts/fusion/plugins/publish/validate_unique_subsets.py index 3131400de9..939ddbd117 100644 --- a/client/ayon_core/hosts/fusion/plugins/publish/validate_unique_subsets.py +++ b/client/ayon_core/hosts/fusion/plugins/publish/validate_unique_subsets.py @@ -7,7 +7,7 @@ from ayon_core.hosts.fusion.api.action import SelectInvalidAction class ValidateUniqueSubsets(pyblish.api.ContextPlugin): - """Ensure all instances have a unique subset name""" + """Ensure all instances have a unique product name""" order = pyblish.api.ValidatorOrder label = "Validate Unique Subsets" @@ -18,27 +18,31 @@ class ValidateUniqueSubsets(pyblish.api.ContextPlugin): @classmethod def get_invalid(cls, context): - # Collect instances per subset per asset - instances_per_subset_asset = defaultdict(lambda: defaultdict(list)) + # Collect instances per product per folder + instances_per_product_folder = defaultdict(lambda: defaultdict(list)) for instance in context: - asset = instance.data.get( - "folderPath", context.data.get("folderPath") + folder_path = instance.data["folderPath"] + product_name = instance.data["productName"] + instances_per_product_folder[folder_path][product_name].append( + instance ) - subset = instance.data.get("subset", context.data.get("subset")) - instances_per_subset_asset[asset][subset].append(instance) # Find which asset + subset combination has more than one instance # Those are considered invalid because they'd integrate to the same # destination. invalid = [] - for asset, instances_per_subset in instances_per_subset_asset.items(): - for subset, instances in instances_per_subset.items(): + for folder_path, instances_per_product in ( + instances_per_product_folder.items() + ): + for product_name, instances in instances_per_product.items(): if len(instances) > 1: cls.log.warning( - "{asset} > {subset} used by more than " - "one instance: {instances}".format( - asset=asset, - subset=subset, + ( + "{folder_path} > {product_name} used by more than " + "one instance: {instances}" + ).format( + folder_path=folder_path, + product_name=product_name, instances=instances ) ) @@ -52,6 +56,7 @@ class ValidateUniqueSubsets(pyblish.api.ContextPlugin): def process(self, context): invalid = self.get_invalid(context) if invalid: - raise PublishValidationError("Multiple instances are set to " - "the same asset > subset.", - title=self.label) + raise PublishValidationError( + "Multiple instances are set to the same folder > product.", + title=self.label + ) From 8f032901935cf3d59ea530e3b4d8377585778934 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 10:37:32 +0100 Subject: [PATCH 253/573] flame is using product name and type --- client/ayon_core/hosts/flame/api/pipeline.py | 4 +- client/ayon_core/hosts/flame/api/plugin.py | 44 +++++++++-------- .../flame/plugins/create/create_shot_clip.py | 16 +++---- .../hosts/flame/plugins/load/load_clip.py | 6 +-- .../flame/plugins/load/load_clip_batch.py | 33 +++++++------ .../plugins/publish/collect_test_selection.py | 4 +- .../publish/collect_timeline_instances.py | 48 ++++++++++--------- .../plugins/publish/collect_timeline_otio.py | 24 +++++----- .../plugins/publish/integrate_batch_group.py | 4 +- 9 files changed, 96 insertions(+), 87 deletions(-) diff --git a/client/ayon_core/hosts/flame/api/pipeline.py b/client/ayon_core/hosts/flame/api/pipeline.py index 88375f829f..532f89b5e9 100644 --- a/client/ayon_core/hosts/flame/api/pipeline.py +++ b/client/ayon_core/hosts/flame/api/pipeline.py @@ -147,8 +147,8 @@ def imprint(segment, data=None): Examples: data = { 'asset': 'sq020sh0280', - 'family': 'render', - 'subset': 'subsetMain' + 'productType': 'render', + 'productName': 'productMain' } """ data = data or {} diff --git a/client/ayon_core/hosts/flame/api/plugin.py b/client/ayon_core/hosts/flame/api/plugin.py index 720e6792b0..862dd3ce61 100644 --- a/client/ayon_core/hosts/flame/api/plugin.py +++ b/client/ayon_core/hosts/flame/api/plugin.py @@ -353,9 +353,9 @@ class PublishableClip: rename_default = False hierarchy_default = "{_folder_}/{_sequence_}/{_track_}" clip_name_default = "shot_{_trackIndex_:0>3}_{_clipIndex_:0>4}" - subset_name_default = "[ track name ]" review_track_default = "[ none ]" - subset_family_default = "plate" + base_product_name_default = "[ track name ]" + base_product_type_default = "plate" count_from_default = 10 count_steps_default = 10 vertical_sync_default = False @@ -368,7 +368,7 @@ class PublishableClip: def __init__(self, segment, **kwargs): self.rename_index = kwargs["rename_index"] - self.family = kwargs["family"] + self.product_type = kwargs["family"] self.log = kwargs["log"] # get main parent objects @@ -486,10 +486,10 @@ class PublishableClip: "countFrom", {}).get("value") or self.count_from_default self.count_steps = self.ui_inputs.get( "countSteps", {}).get("value") or self.count_steps_default - self.subset_name = self.ui_inputs.get( - "subsetName", {}).get("value") or self.subset_name_default - self.subset_family = self.ui_inputs.get( - "subsetFamily", {}).get("value") or self.subset_family_default + self.base_product_name = self.ui_inputs.get( + "productName", {}).get("value") or self.base_product_name_default + self.base_product_type = self.ui_inputs.get( + "productType", {}).get("value") or self.base_product_type_default self.vertical_sync = self.ui_inputs.get( "vSyncOn", {}).get("value") or self.vertical_sync_default self.driving_layer = self.ui_inputs.get( @@ -509,12 +509,14 @@ class PublishableClip: or self.retimed_framerange_default ) - # build subset name from layer name - if self.subset_name == "[ track name ]": - self.subset_name = self.track_name + # build product name from layer name + if self.base_product_name == "[ track name ]": + self.base_product_name = self.track_name - # create subset for publishing - self.subset = self.subset_family + self.subset_name.capitalize() + # create product for publishing + self.product_name = ( + self.base_product_type + self.base_product_name.capitalize() + ) def _replace_hash_to_expression(self, name, text): """ Replace hash with number in correct padding. """ @@ -608,14 +610,14 @@ class PublishableClip: _hero_data = deepcopy(hero_data) _hero_data.update({"heroTrack": False}) if _in <= self.clip_in and _out >= self.clip_out: - data_subset = hero_data["subset"] + data_product_name = hero_data["productName"] # add track index in case duplicity of names in hero data - if self.subset in data_subset: - _hero_data["subset"] = self.subset + str( + if self.product_name in data_product_name: + _hero_data["productName"] = self.product_name + str( self.track_index) - # in case track name and subset name is the same then add - if self.subset_name == self.track_name: - _hero_data["subset"] = self.subset + # in case track name and product name is the same then add + if self.base_product_name == self.track_name: + _hero_data["productName"] = self.product_name # assign data to return hierarchy data to tag tag_hierarchy_data = _hero_data break @@ -637,9 +639,9 @@ class PublishableClip: "hierarchy": hierarchy_filled, "parents": self.parents, "hierarchyData": hierarchy_formatting_data, - "subset": self.subset, - "family": self.subset_family, - "families": [self.family] + "productName": self.product_name, + "productType": self.base_product_type, + "families": [self.base_product_type, self.product_type] } def _convert_to_entity(self, type, template): diff --git a/client/ayon_core/hosts/flame/plugins/create/create_shot_clip.py b/client/ayon_core/hosts/flame/plugins/create/create_shot_clip.py index ee99040ca3..c73ee7510c 100644 --- a/client/ayon_core/hosts/flame/plugins/create/create_shot_clip.py +++ b/client/ayon_core/hosts/flame/plugins/create/create_shot_clip.py @@ -6,7 +6,7 @@ class CreateShotClip(opfapi.Creator): """Publishable clip""" label = "Create Publishable Clip" - family = "clip" + product_type = "clip" icon = "film" defaults = ["Main"] @@ -32,7 +32,7 @@ class CreateShotClip(opfapi.Creator): # open widget for plugins inputs results_back = self.create_widget( - "Pype publish attributes creator", + "AYON publish attributes creator", "Define sequential rename and fill hierarchy data.", gui_inputs ) @@ -62,7 +62,7 @@ class CreateShotClip(opfapi.Creator): "log": self.log, "ui_inputs": results_back, "avalon": self.data, - "family": self.data["family"] + "product_type": self.data["productType"] } for i, segment in enumerate(sorted_selected_segments): @@ -203,19 +203,19 @@ class CreateShotClip(opfapi.Creator): "target": "ui", "order": 3, "value": { - "subsetName": { + "productName": { "value": ["[ track name ]", "main", "bg", "fg", "bg", "animatic"], "type": "QComboBox", "label": "Subset Name", "target": "ui", - "toolTip": "chose subset name pattern, if [ track name ] is selected, name of track layer will be used", # noqa + "toolTip": "chose product name pattern, if [ track name ] is selected, name of track layer will be used", # noqa "order": 0}, - "subsetFamily": { + "productType": { "value": ["plate", "take"], "type": "QComboBox", "label": "Subset Family", - "target": "ui", "toolTip": "What use of this subset is for", # noqa + "target": "ui", "toolTip": "What use of this product is for", # noqa "order": 1}, "reviewTrack": { "value": ["< none >"] + gui_tracks, @@ -229,7 +229,7 @@ class CreateShotClip(opfapi.Creator): "type": "QCheckBox", "label": "Include audio", "target": "tag", - "toolTip": "Process subsets with corresponding audio", # noqa + "toolTip": "Process products with corresponding audio", # noqa "order": 3}, "sourceResolution": { "value": False, diff --git a/client/ayon_core/hosts/flame/plugins/load/load_clip.py b/client/ayon_core/hosts/flame/plugins/load/load_clip.py index 47d0331255..84f63b3177 100644 --- a/client/ayon_core/hosts/flame/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/flame/plugins/load/load_clip.py @@ -11,7 +11,7 @@ from ayon_core.lib.transcoding import ( class LoadClip(opfapi.ClipLoader): - """Load a subset to timeline as clip + """Load a product to timeline as clip Place clip to timeline on its asset origin timings collected during conforming to project @@ -31,14 +31,14 @@ class LoadClip(opfapi.ClipLoader): # settings reel_group_name = "OpenPype_Reels" reel_name = "Loaded" - clip_name_template = "{asset}_{subset}<_{output}>" + clip_name_template = "{folder[name]}_{product[name]}<_{output}>" """ Anatomy keys from version context data and dynamically added: - {layerName} - original layer name token - {layerUID} - original layer UID token - {originalBasename} - original clip name taken from file """ - layer_rename_template = "{asset}_{subset}<_{output}>" + layer_rename_template = "{folder[name]}_{product[name]}<_{output}>" layer_rename_patterns = [] def load(self, context, name, namespace, options): diff --git a/client/ayon_core/hosts/flame/plugins/load/load_clip_batch.py b/client/ayon_core/hosts/flame/plugins/load/load_clip_batch.py index cdf96bd459..9f81103cb4 100644 --- a/client/ayon_core/hosts/flame/plugins/load/load_clip_batch.py +++ b/client/ayon_core/hosts/flame/plugins/load/load_clip_batch.py @@ -10,7 +10,7 @@ from ayon_core.lib.transcoding import ( ) class LoadClipBatch(opfapi.ClipLoader): - """Load a subset to timeline as clip + """Load a product to timeline as clip Place clip to timeline on its asset origin timings collected during conforming to project @@ -29,14 +29,14 @@ class LoadClipBatch(opfapi.ClipLoader): # settings reel_name = "OP_LoadedReel" - clip_name_template = "{batch}_{asset}_{subset}<_{output}>" + clip_name_template = "{batch}_{folder[name]}_{product[name]}<_{output}>" """ Anatomy keys from version context data and dynamically added: - {layerName} - original layer name token - {layerUID} - original layer UID token - {originalBasename} - original clip name taken from file """ - layer_rename_template = "{asset}_{subset}<_{output}>" + layer_rename_template = "{folder[name]}_{product[name]}<_{output}>" layer_rename_patterns = [] def load(self, context, name, namespace, options): @@ -50,17 +50,8 @@ class LoadClipBatch(opfapi.ClipLoader): version_name = version.get("name", None) colorspace = self.get_colorspace(context) - # TODO remove '{folder[name]}' and '{product[name]}' replacement - clip_name_template = ( - self.clip_name_template - .replace("{folder[name]}", "{asset}") - .replace("{product[name]}", "{subset}") - ) - layer_rename_template = ( - self.layer_rename_template - .replace("{folder[name]}", "{asset}") - .replace("{product[name]}", "{subset}") - ) + clip_name_template = self.clip_name_template + layer_rename_template = self.layer_rename_template # in case output is not in context replace key to representation if not context["representation"]["context"].get("output"): clip_name_template = clip_name_template.replace( @@ -68,8 +59,22 @@ class LoadClipBatch(opfapi.ClipLoader): layer_rename_template = layer_rename_template.replace( "output", "representation") + asset_doc = context["asset"] + subset_doc = context["subset"] formatting_data = deepcopy(context["representation"]["context"]) formatting_data["batch"] = self.batch.name.get_value() + formatting_data.update({ + "asset": asset_doc["name"], + "folder": { + "name": asset_doc["name"], + }, + "subset": subset_doc["name"], + "family": subset_doc["data"]["family"], + "product": { + "name": subset_doc["name"], + "type": subset_doc["data"]["family"], + } + }) clip_name = StringTemplate(clip_name_template).format( formatting_data) diff --git a/client/ayon_core/hosts/flame/plugins/publish/collect_test_selection.py b/client/ayon_core/hosts/flame/plugins/publish/collect_test_selection.py index 0fb41eab78..7442e7df48 100644 --- a/client/ayon_core/hosts/flame/plugins/publish/collect_test_selection.py +++ b/client/ayon_core/hosts/flame/plugins/publish/collect_test_selection.py @@ -59,6 +59,6 @@ class CollectTestSelection(pyblish.api.ContextPlugin): opfapi.imprint(segment, { 'asset': segment.name.get_value(), - 'family': 'render', - 'subset': 'subsetMain' + 'productType': 'render', + 'productName': 'productMain' }) diff --git a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_instances.py b/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_instances.py index 3819537010..60fe221441 100644 --- a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_instances.py +++ b/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_instances.py @@ -110,24 +110,25 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): # add ocio_data to instance data inst_data.update(otio_data) - asset = marker_data["asset"] - subset = marker_data["subset"] + folder_path = marker_data["folderPath"] + folder_name = folder_path.rsplit("/")[-1] + product_name = marker_data["productName"] - # insert family into families - family = marker_data["family"] + # insert product type into families + product_type = marker_data["productType"] families = [str(f) for f in marker_data["families"]] - families.insert(0, str(family)) + families.insert(0, str(product_type)) # form label - label = asset - if asset != clip_name: + label = folder_name + if folder_name != clip_name: label += " ({})".format(clip_name) - label += " {} [{}]".format(subset, ", ".join(families)) + label += " {} [{}]".format(product_name, ", ".join(families)) inst_data.update({ - "name": "{}_{}".format(asset, subset), + "name": "{}_{}".format(folder_name, product_name), "label": label, - "asset": asset, + "folderPath": folder_path, "item": segment, "families": families, "publish": marker_data["publish"], @@ -335,26 +336,27 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): if not hierarchy_data: return - asset = data["asset"] - subset = "shotMain" + folder_path = data["folderPath"] + folder_name = folder_path.rsplit("/")[-1] + product_name = "shotMain" - # insert family into families - family = "shot" + # insert product type into families + product_type = "shot" # form label - label = asset - if asset != clip_name: + label = folder_name + if folder_name != clip_name: label += " ({}) ".format(clip_name) - label += " {}".format(subset) - label += " [{}]".format(family) + label += " {}".format(product_name) + label += " [{}]".format(product_type) data.update({ - "name": "{}_{}".format(asset, subset), + "name": "{}_{}".format(folder_name, product_name), "label": label, - "subset": subset, - "asset": asset, - "family": family, - "families": [] + "productName": product_name, + "folderPath": folder_path, + "productType": product_type, + "families": [product_type] }) instance = context.create_instance(**data) diff --git a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_otio.py b/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_otio.py index 2fcfb55e7c..c095c1d333 100644 --- a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_otio.py +++ b/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_otio.py @@ -3,7 +3,7 @@ import pyblish.api from ayon_core.client import get_asset_name_identifier import ayon_core.hosts.flame.api as opfapi from ayon_core.hosts.flame.otio import flame_export -from ayon_core.pipeline.create import get_subset_name +from ayon_core.pipeline.create import get_product_name class CollecTimelineOTIO(pyblish.api.ContextPlugin): @@ -14,7 +14,7 @@ class CollecTimelineOTIO(pyblish.api.ContextPlugin): def process(self, context): # plugin defined - family = "workfile" + product_type = "workfile" variant = "otioTimeline" # main @@ -23,14 +23,14 @@ class CollecTimelineOTIO(pyblish.api.ContextPlugin): project = opfapi.get_current_project() sequence = opfapi.get_current_sequence(opfapi.CTX.selection) - # create subset name - subset_name = get_subset_name( - family, - variant, - task_name, - asset_doc, + # create product name + product_name = get_product_name( context.data["projectName"], + asset_doc, + task_name, context.data["hostName"], + product_type, + variant, project_settings=context.data["project_settings"] ) @@ -41,11 +41,11 @@ class CollecTimelineOTIO(pyblish.api.ContextPlugin): otio_timeline = flame_export.create_otio_timeline(sequence) instance_data = { - "name": subset_name, + "name": product_name, "folderPath": folder_path, - "subset": subset_name, - "family": "workfile", - "families": [] + "productName": product_name, + "productType": product_type, + "families": [product_type] } # create instance with workfile diff --git a/client/ayon_core/hosts/flame/plugins/publish/integrate_batch_group.py b/client/ayon_core/hosts/flame/plugins/publish/integrate_batch_group.py index e36d2a22d5..a66ee9f2c0 100644 --- a/client/ayon_core/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/client/ayon_core/hosts/flame/plugins/publish/integrate_batch_group.py @@ -44,8 +44,8 @@ class IntegrateBatchGroup(pyblish.api.InstancePlugin): )) # load plate to batch group - self.log.info("Loading subset `{}` into batch `{}`".format( - instance.data["subset"], bgroup.name.get_value() + self.log.info("Loading product `{}` into batch `{}`".format( + instance.data["productName"], bgroup.name.get_value() )) self._load_clip_to_context(instance, bgroup) From d19a9cc24c2e030339fcab1c555611cf1919fbf1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 10:39:57 +0100 Subject: [PATCH 254/573] celaction is using product name and type --- .../publish/collect_celaction_instances.py | 25 ++++++++++--------- .../plugins/publish/collect_render_path.py | 4 ++- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/client/ayon_core/hosts/celaction/plugins/publish/collect_celaction_instances.py b/client/ayon_core/hosts/celaction/plugins/publish/collect_celaction_instances.py index ef471dbd05..538f1c28c4 100644 --- a/client/ayon_core/hosts/celaction/plugins/publish/collect_celaction_instances.py +++ b/client/ayon_core/hosts/celaction/plugins/publish/collect_celaction_instances.py @@ -46,17 +46,17 @@ class CollectCelactionInstances(pyblish.api.ContextPlugin): shared_instance_data.update(celaction_kwargs) # workfile instance - family = "workfile" - subset = family + task.capitalize() + product_type = "workfile" + product_name = product_type + task.capitalize() # Create instance - instance = context.create_instance(subset) + instance = context.create_instance(product_name) # creating instance data instance.data.update({ - "subset": subset, "label": scene_file, - "family": family, - "families": [], + "productName": product_name, + "productType": product_type, + "families": [product_type], "representations": [] }) @@ -76,17 +76,18 @@ class CollectCelactionInstances(pyblish.api.ContextPlugin): self.log.info('Publishing Celaction workfile') # render instance - subset = f"render{task}Main" - instance = context.create_instance(name=subset) + product_name = f"render{task}Main" + product_type = "render.farm" + instance = context.create_instance(name=product_name) # getting instance state instance.data["publish"] = True # add assetEntity data into instance instance.data.update({ - "label": "{} - farm".format(subset), - "family": "render.farm", - "families": [], - "subset": subset + "label": "{} - farm".format(product_name), + "productType": product_type, + "families": [product_type], + "productName": product_name }) # adding basic script data diff --git a/client/ayon_core/hosts/celaction/plugins/publish/collect_render_path.py b/client/ayon_core/hosts/celaction/plugins/publish/collect_render_path.py index f6db6c000d..abe670b691 100644 --- a/client/ayon_core/hosts/celaction/plugins/publish/collect_render_path.py +++ b/client/ayon_core/hosts/celaction/plugins/publish/collect_render_path.py @@ -19,12 +19,14 @@ class CollectRenderPath(pyblish.api.InstancePlugin): anatomy = instance.context.data["anatomy"] anatomy_data = copy.deepcopy(instance.data["anatomyData"]) padding = anatomy.templates.get("frame_padding", 4) + product_type = "render" anatomy_data.update({ "frame": f"%0{padding}d", - "family": "render", + "family": product_type, "representation": self.output_extension, "ext": self.output_extension }) + anatomy_data["product"]["type"] = product_type anatomy_filled = anatomy.format(anatomy_data) From 36129a12bd56ff67ab187140886ea3667abe1fa1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 11:16:04 +0100 Subject: [PATCH 255/573] blender is using product name and type --- client/ayon_core/hosts/blender/api/plugin.py | 83 ++++++++++--------- .../blender/plugins/create/convert_legacy.py | 48 +++++------ .../blender/plugins/create/create_action.py | 8 +- .../plugins/create/create_animation.py | 6 +- .../plugins/create/create_blendScene.py | 6 +- .../blender/plugins/create/create_camera.py | 10 +-- .../blender/plugins/create/create_layout.py | 6 +- .../blender/plugins/create/create_model.py | 6 +- .../plugins/create/create_pointcache.py | 6 +- .../blender/plugins/create/create_render.py | 6 +- .../blender/plugins/create/create_review.py | 6 +- .../blender/plugins/create/create_rig.py | 6 +- .../blender/plugins/create/create_workfile.py | 10 +-- .../blender/plugins/load/import_workfile.py | 6 +- .../hosts/blender/plugins/load/load_abc.py | 17 ++-- .../hosts/blender/plugins/load/load_action.py | 8 +- .../hosts/blender/plugins/load/load_audio.py | 16 ++-- .../hosts/blender/plugins/load/load_blend.py | 24 +++--- .../blender/plugins/load/load_blendscene.py | 34 +++++--- .../blender/plugins/load/load_camera_abc.py | 16 ++-- .../blender/plugins/load/load_camera_fbx.py | 16 ++-- .../hosts/blender/plugins/load/load_fbx.py | 16 ++-- .../blender/plugins/load/load_layout_json.py | 37 +++++---- .../hosts/blender/plugins/load/load_look.py | 14 ++-- .../plugins/publish/collect_instance.py | 2 +- .../blender/plugins/publish/extract_abc.py | 6 +- .../plugins/publish/extract_abc_animation.py | 6 +- .../blender/plugins/publish/extract_blend.py | 6 +- .../publish/extract_blend_animation.py | 6 +- .../plugins/publish/extract_camera_abc.py | 6 +- .../plugins/publish/extract_camera_fbx.py | 6 +- .../blender/plugins/publish/extract_fbx.py | 6 +- .../plugins/publish/extract_fbx_animation.py | 6 +- .../blender/plugins/publish/extract_layout.py | 17 ++-- .../plugins/publish/extract_playblast.py | 6 +- .../plugins/publish/extract_thumbnail.py | 10 +-- .../plugins/publish/integrate_animation.py | 14 ++-- .../plugins/publish/validate_file_saved.py | 7 +- 38 files changed, 275 insertions(+), 240 deletions(-) diff --git a/client/ayon_core/hosts/blender/api/plugin.py b/client/ayon_core/hosts/blender/api/plugin.py index d72754f148..447585e0a8 100644 --- a/client/ayon_core/hosts/blender/api/plugin.py +++ b/client/ayon_core/hosts/blender/api/plugin.py @@ -30,13 +30,13 @@ VALID_EXTENSIONS = [".blend", ".json", ".abc", ".fbx"] def prepare_scene_name( - asset: str, subset: str, namespace: Optional[str] = None + folder_name: str, product_name: str, namespace: Optional[str] = None ) -> str: """Return a consistent name for an asset.""" - name = f"{asset}" + name = f"{folder_name}" if namespace: name = f"{name}_{namespace}" - name = f"{name}_{subset}" + name = f"{name}_{product_name}" # Blender name for a collection or object cannot be longer than 63 # characters. If the name is longer, it will raise an error. @@ -47,7 +47,7 @@ def prepare_scene_name( def get_unique_number( - asset: str, subset: str + folder_name: str, product_name: str ) -> str: """Return a unique number based on the asset name.""" avalon_container = bpy.data.collections.get(AVALON_CONTAINERS) @@ -64,10 +64,10 @@ def get_unique_number( if c.get(AVALON_PROPERTY)} container_names = obj_group_names.union(coll_group_names) count = 1 - name = f"{asset}_{count:0>2}_{subset}" + name = f"{folder_name}_{count:0>2}_{product_name}" while name in container_names: count += 1 - name = f"{asset}_{count:0>2}_{subset}" + name = f"{folder_name}_{count:0>2}_{product_name}" return f"{count:0>2}" @@ -161,24 +161,24 @@ class BaseCreator(Creator): create_as_asset_group = False @staticmethod - def cache_subsets(shared_data): + def cache_instance_data(shared_data): """Cache instances for Creators shared data. - Create `blender_cached_subsets` key when needed in shared data and + Create `blender_cached_instances` key when needed in shared data and fill it with all collected instances from the scene under its respective creator identifiers. If legacy instances are detected in the scene, create - `blender_cached_legacy_subsets` key and fill it with - all legacy subsets from this family as a value. # key or value? + `blender_cached_legacy_instances` key and fill it with + all legacy products from this family as a value. # key or value? Args: shared_data(Dict[str, Any]): Shared data. Return: - Dict[str, Any]: Shared data with cached subsets. + Dict[str, Any]: Shared data with cached products. """ - if not shared_data.get('blender_cached_subsets'): + if not shared_data.get('blender_cached_instances'): cache = {} cache_legacy = {} @@ -210,19 +210,19 @@ class BaseCreator(Creator): # Legacy creator instance cache_legacy.setdefault(family, []).append(obj_or_col) - shared_data["blender_cached_subsets"] = cache - shared_data["blender_cached_legacy_subsets"] = cache_legacy + shared_data["blender_cached_instances"] = cache + shared_data["blender_cached_legacy_instances"] = cache_legacy return shared_data def create( - self, subset_name: str, instance_data: dict, pre_create_data: dict + self, product_name: str, instance_data: dict, pre_create_data: dict ): """Override abstract method from Creator. Create new instance and store it. Args: - subset_name(str): Subset name of created instance. + product_name(str): Subset name of created instance. instance_data(dict): Instance base data. pre_create_data(dict): Data based on pre creation attributes. Those may affect how creator works. @@ -236,7 +236,7 @@ class BaseCreator(Creator): # Create asset group asset_name = instance_data["folderPath"].split("/")[-1] - name = prepare_scene_name(asset_name, subset_name) + name = prepare_scene_name(asset_name, product_name) if self.create_as_asset_group: # Create instance as empty instance_node = bpy.data.objects.new(name=name, object_data=None) @@ -247,10 +247,10 @@ class BaseCreator(Creator): instance_node = bpy.data.collections.new(name=name) instances.children.link(instance_node) - self.set_instance_data(subset_name, instance_data) + self.set_instance_data(product_name, instance_data) instance = CreatedInstance( - self.family, subset_name, instance_data, self + self.product_type, product_name, instance_data, self ) instance.transient_data["instance_node"] = instance_node self._add_instance_to_context(instance) @@ -263,18 +263,18 @@ class BaseCreator(Creator): """Override abstract method from BaseCreator. Collect existing instances related to this creator plugin.""" - # Cache subsets in shared data - self.cache_subsets(self.collection_shared_data) + # Cache instances in shared data + self.cache_instance_data(self.collection_shared_data) - # Get cached subsets - cached_subsets = self.collection_shared_data.get( - "blender_cached_subsets" + # Get cached instances + cached_instances = self.collection_shared_data.get( + "blender_cached_instances" ) - if not cached_subsets: + if not cached_instances: return # Process only instances that were created by this creator - for instance_node in cached_subsets.get(self.identifier, []): + for instance_node in cached_instances.get(self.identifier, []): property = instance_node.get(AVALON_PROPERTY) # Create instance object from existing data instance = CreatedInstance.from_existing( @@ -306,16 +306,17 @@ class BaseCreator(Creator): ) return - # Rename the instance node in the scene if subset or asset changed. + # Rename the instance node in the scene if product + # or folder changed. # Do not rename the instance if the family is workfile, as the # workfile instance is included in the AVALON_CONTAINER collection. if ( - "subset" in changes.changed_keys + "productName" in changes.changed_keys or "folderPath" in changes.changed_keys - ) and created_instance.family != "workfile": + ) and created_instance.product_type != "workfile": asset_name = data["folderPath"].split("/")[-1] name = prepare_scene_name( - asset=asset_name, subset=data["subset"] + asset_name, data["productName"] ) node.name = name @@ -341,13 +342,13 @@ class BaseCreator(Creator): def set_instance_data( self, - subset_name: str, + product_name: str, instance_data: dict ): """Fill instance data with required items. Args: - subset_name(str): Subset name of created instance. + product_name(str): Subset name of created instance. instance_data(dict): Instance base data. instance_node(bpy.types.ID): Instance node in blender scene. """ @@ -358,7 +359,7 @@ class BaseCreator(Creator): { "id": AVALON_INSTANCE_ID, "creator_identifier": self.identifier, - "subset": subset_name, + "productName": product_name, } ) @@ -466,14 +467,14 @@ class AssetLoader(LoaderPlugin): filepath = self.filepath_from_context(context) assert Path(filepath).exists(), f"{filepath} doesn't exist." - asset = context["asset"]["name"] - subset = context["subset"]["name"] + folder_name = context["asset"]["name"] + product_name = context["subset"]["name"] unique_number = get_unique_number( - asset, subset + folder_name, product_name ) - namespace = namespace or f"{asset}_{unique_number}" + namespace = namespace or f"{folder_name}_{unique_number}" name = name or prepare_scene_name( - asset, subset, unique_number + folder_name, product_name, unique_number ) nodes = self.process_asset( @@ -499,10 +500,10 @@ class AssetLoader(LoaderPlugin): # loader=self.__class__.__name__, # ) - # asset = context["asset"]["name"] - # subset = context["subset"]["name"] + # folder_name = context["asset"]["name"] + # product_name = context["subset"]["name"] # instance_name = prepare_scene_name( - # asset, subset, unique_number + # folder_name, product_name, unique_number # ) + '_CON' # return self._get_instance_collection(instance_name, nodes) diff --git a/client/ayon_core/hosts/blender/plugins/create/convert_legacy.py b/client/ayon_core/hosts/blender/plugins/create/convert_legacy.py index fcd4a7a26e..65a5a4a9b6 100644 --- a/client/ayon_core/hosts/blender/plugins/create/convert_legacy.py +++ b/client/ayon_core/hosts/blender/plugins/create/convert_legacy.py @@ -1,24 +1,24 @@ # -*- coding: utf-8 -*- -"""Converter for legacy Houdini subsets.""" +"""Converter for legacy Houdini products.""" from ayon_core.pipeline.create.creator_plugins import SubsetConvertorPlugin from ayon_core.hosts.blender.api.lib import imprint class BlenderLegacyConvertor(SubsetConvertorPlugin): - """Find and convert any legacy subsets in the scene. + """Find and convert any legacy products in the scene. - This Converter will find all legacy subsets in the scene and will - transform them to the current system. Since the old subsets doesn't + This Converter will find all legacy products in the scene and will + transform them to the current system. Since the old products doesn't retain any information about their original creators, the only mapping - we can do is based on their families. + we can do is based on their product types. - Its limitation is that you can have multiple creators creating subset - of the same family and there is no way to handle it. This code should - nevertheless cover all creators that came with OpenPype. + Its limitation is that you can have multiple creators creating product + of the same product type and there is no way to handle it. This code + should nevertheless cover all creators that came with OpenPype. """ identifier = "io.openpype.creators.blender.legacy" - family_to_id = { + product_type_to_id = { "action": "io.openpype.creators.blender.action", "camera": "io.openpype.creators.blender.camera", "animation": "io.openpype.creators.blender.animation", @@ -33,42 +33,42 @@ class BlenderLegacyConvertor(SubsetConvertorPlugin): def __init__(self, *args, **kwargs): super(BlenderLegacyConvertor, self).__init__(*args, **kwargs) - self.legacy_subsets = {} + self.legacy_instances = {} def find_instances(self): - """Find legacy subsets in the scene. + """Find legacy products in the scene. - Legacy subsets are the ones that doesn't have `creator_identifier` + Legacy products are the ones that doesn't have `creator_identifier` parameter on them. This is using cached entries done in - :py:meth:`~BaseCreator.cache_subsets()` + :py:meth:`~BaseCreator.cache_instance_data()` """ - self.legacy_subsets = self.collection_shared_data.get( - "blender_cached_legacy_subsets") - if not self.legacy_subsets: + self.legacy_instances = self.collection_shared_data.get( + "blender_cached_legacy_instances") + if not self.legacy_instances: return self.add_convertor_item( - "Found {} incompatible subset{}".format( - len(self.legacy_subsets), - "s" if len(self.legacy_subsets) > 1 else "" + "Found {} incompatible product{}".format( + len(self.legacy_instances), + "s" if len(self.legacy_instances) > 1 else "" ) ) def convert(self): - """Convert all legacy subsets to current. + """Convert all legacy products to current. It is enough to add `creator_identifier` and `instance_node`. """ - if not self.legacy_subsets: + if not self.legacy_instances: return - for family, instance_nodes in self.legacy_subsets.items(): - if family in self.family_to_id: + for product_type, instance_nodes in self.legacy_instances.items(): + if product_type in self.product_type_to_id: for instance_node in instance_nodes: - creator_identifier = self.family_to_id[family] + creator_identifier = self.product_type_to_id[product_type] self.log.info( "Converting {} to {}".format(instance_node.name, creator_identifier) diff --git a/client/ayon_core/hosts/blender/plugins/create/create_action.py b/client/ayon_core/hosts/blender/plugins/create/create_action.py index 82047fb5c6..070b9843c3 100644 --- a/client/ayon_core/hosts/blender/plugins/create/create_action.py +++ b/client/ayon_core/hosts/blender/plugins/create/create_action.py @@ -10,20 +10,20 @@ class CreateAction(plugin.BaseCreator): identifier = "io.openpype.creators.blender.action" label = "Action" - family = "action" + product_type = "action" icon = "male" def create( - self, subset_name: str, instance_data: dict, pre_create_data: dict + self, product_name: str, instance_data: dict, pre_create_data: dict ): # Run parent create method collection = super().create( - subset_name, instance_data, pre_create_data + product_name, instance_data, pre_create_data ) # Get instance name name = plugin.prepare_scene_name( - instance_data["folderPath"], subset_name + instance_data["folderPath"], product_name ) if pre_create_data.get("use_selection"): diff --git a/client/ayon_core/hosts/blender/plugins/create/create_animation.py b/client/ayon_core/hosts/blender/plugins/create/create_animation.py index 8671d3bfdb..b806a5a7ca 100644 --- a/client/ayon_core/hosts/blender/plugins/create/create_animation.py +++ b/client/ayon_core/hosts/blender/plugins/create/create_animation.py @@ -8,15 +8,15 @@ class CreateAnimation(plugin.BaseCreator): identifier = "io.openpype.creators.blender.animation" label = "Animation" - family = "animation" + product_type = "animation" icon = "male" def create( - self, subset_name: str, instance_data: dict, pre_create_data: dict + self, product_name: str, instance_data: dict, pre_create_data: dict ): # Run parent create method collection = super().create( - subset_name, instance_data, pre_create_data + product_name, instance_data, pre_create_data ) if pre_create_data.get("use_selection"): diff --git a/client/ayon_core/hosts/blender/plugins/create/create_blendScene.py b/client/ayon_core/hosts/blender/plugins/create/create_blendScene.py index 6afb2ca6a0..51250bf18b 100644 --- a/client/ayon_core/hosts/blender/plugins/create/create_blendScene.py +++ b/client/ayon_core/hosts/blender/plugins/create/create_blendScene.py @@ -10,16 +10,16 @@ class CreateBlendScene(plugin.BaseCreator): identifier = "io.openpype.creators.blender.blendscene" label = "Blender Scene" - family = "blendScene" + product_type = "blendScene" icon = "cubes" maintain_selection = False def create( - self, subset_name: str, instance_data: dict, pre_create_data: dict + self, product_name: str, instance_data: dict, pre_create_data: dict ): - instance_node = super().create(subset_name, + instance_node = super().create(product_name, instance_data, pre_create_data) diff --git a/client/ayon_core/hosts/blender/plugins/create/create_camera.py b/client/ayon_core/hosts/blender/plugins/create/create_camera.py index c63a294cf9..cd82bec236 100644 --- a/client/ayon_core/hosts/blender/plugins/create/create_camera.py +++ b/client/ayon_core/hosts/blender/plugins/create/create_camera.py @@ -11,16 +11,16 @@ class CreateCamera(plugin.BaseCreator): identifier = "io.openpype.creators.blender.camera" label = "Camera" - family = "camera" + product_type = "camera" icon = "video-camera" create_as_asset_group = True def create( - self, subset_name: str, instance_data: dict, pre_create_data: dict + self, product_name: str, instance_data: dict, pre_create_data: dict ): - asset_group = super().create(subset_name, + asset_group = super().create(product_name, instance_data, pre_create_data) @@ -30,8 +30,8 @@ class CreateCamera(plugin.BaseCreator): obj.parent = asset_group else: plugin.deselect_all() - camera = bpy.data.cameras.new(subset_name) - camera_obj = bpy.data.objects.new(subset_name, camera) + camera = bpy.data.cameras.new(product_name) + camera_obj = bpy.data.objects.new(product_name, camera) instances = bpy.data.collections.get(AVALON_INSTANCES) instances.objects.link(camera_obj) diff --git a/client/ayon_core/hosts/blender/plugins/create/create_layout.py b/client/ayon_core/hosts/blender/plugins/create/create_layout.py index 3da3916aef..289c39fc38 100644 --- a/client/ayon_core/hosts/blender/plugins/create/create_layout.py +++ b/client/ayon_core/hosts/blender/plugins/create/create_layout.py @@ -10,16 +10,16 @@ class CreateLayout(plugin.BaseCreator): identifier = "io.openpype.creators.blender.layout" label = "Layout" - family = "layout" + product_type = "layout" icon = "cubes" create_as_asset_group = True def create( - self, subset_name: str, instance_data: dict, pre_create_data: dict + self, product_name: str, instance_data: dict, pre_create_data: dict ): - asset_group = super().create(subset_name, + asset_group = super().create(product_name, instance_data, pre_create_data) diff --git a/client/ayon_core/hosts/blender/plugins/create/create_model.py b/client/ayon_core/hosts/blender/plugins/create/create_model.py index b6d89c8862..837ba47417 100644 --- a/client/ayon_core/hosts/blender/plugins/create/create_model.py +++ b/client/ayon_core/hosts/blender/plugins/create/create_model.py @@ -10,15 +10,15 @@ class CreateModel(plugin.BaseCreator): identifier = "io.openpype.creators.blender.model" label = "Model" - family = "model" + product_type = "model" icon = "cube" create_as_asset_group = True def create( - self, subset_name: str, instance_data: dict, pre_create_data: dict + self, product_name: str, instance_data: dict, pre_create_data: dict ): - asset_group = super().create(subset_name, + asset_group = super().create(product_name, instance_data, pre_create_data) diff --git a/client/ayon_core/hosts/blender/plugins/create/create_pointcache.py b/client/ayon_core/hosts/blender/plugins/create/create_pointcache.py index 20ef3fbde4..0aa2d62c17 100644 --- a/client/ayon_core/hosts/blender/plugins/create/create_pointcache.py +++ b/client/ayon_core/hosts/blender/plugins/create/create_pointcache.py @@ -8,15 +8,15 @@ class CreatePointcache(plugin.BaseCreator): identifier = "io.openpype.creators.blender.pointcache" label = "Point Cache" - family = "pointcache" + product_type = "pointcache" icon = "gears" def create( - self, subset_name: str, instance_data: dict, pre_create_data: dict + self, product_name: str, instance_data: dict, pre_create_data: dict ): # Run parent create method collection = super().create( - subset_name, instance_data, pre_create_data + product_name, instance_data, pre_create_data ) if pre_create_data.get("use_selection"): diff --git a/client/ayon_core/hosts/blender/plugins/create/create_render.py b/client/ayon_core/hosts/blender/plugins/create/create_render.py index 3f07f37d2f..bf3d1e62b3 100644 --- a/client/ayon_core/hosts/blender/plugins/create/create_render.py +++ b/client/ayon_core/hosts/blender/plugins/create/create_render.py @@ -12,16 +12,16 @@ class CreateRenderlayer(plugin.BaseCreator): identifier = "io.openpype.creators.blender.render" label = "Render" - family = "render" + product_type = "render" icon = "eye" def create( - self, subset_name: str, instance_data: dict, pre_create_data: dict + self, product_name: str, instance_data: dict, pre_create_data: dict ): try: # Run parent create method collection = super().create( - subset_name, instance_data, pre_create_data + product_name, instance_data, pre_create_data ) prepare_rendering(collection) diff --git a/client/ayon_core/hosts/blender/plugins/create/create_review.py b/client/ayon_core/hosts/blender/plugins/create/create_review.py index cf94819b3e..b478ec59f4 100644 --- a/client/ayon_core/hosts/blender/plugins/create/create_review.py +++ b/client/ayon_core/hosts/blender/plugins/create/create_review.py @@ -8,15 +8,15 @@ class CreateReview(plugin.BaseCreator): identifier = "io.openpype.creators.blender.review" label = "Review" - family = "review" + product_type = "review" icon = "video-camera" def create( - self, subset_name: str, instance_data: dict, pre_create_data: dict + self, product_name: str, instance_data: dict, pre_create_data: dict ): # Run parent create method collection = super().create( - subset_name, instance_data, pre_create_data + product_name, instance_data, pre_create_data ) if pre_create_data.get("use_selection"): diff --git a/client/ayon_core/hosts/blender/plugins/create/create_rig.py b/client/ayon_core/hosts/blender/plugins/create/create_rig.py index 07b33fe4ba..10b6b20d36 100644 --- a/client/ayon_core/hosts/blender/plugins/create/create_rig.py +++ b/client/ayon_core/hosts/blender/plugins/create/create_rig.py @@ -10,15 +10,15 @@ class CreateRig(plugin.BaseCreator): identifier = "io.openpype.creators.blender.rig" label = "Rig" - family = "rig" + product_type = "rig" icon = "wheelchair" create_as_asset_group = True def create( - self, subset_name: str, instance_data: dict, pre_create_data: dict + self, product_name: str, instance_data: dict, pre_create_data: dict ): - asset_group = super().create(subset_name, + asset_group = super().create(product_name, instance_data, pre_create_data) diff --git a/client/ayon_core/hosts/blender/plugins/create/create_workfile.py b/client/ayon_core/hosts/blender/plugins/create/create_workfile.py index 09947f85d1..63d35e6da8 100644 --- a/client/ayon_core/hosts/blender/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/blender/plugins/create/create_workfile.py @@ -19,7 +19,7 @@ class CreateWorkfile(BaseCreator, AutoCreator): """ identifier = "io.openpype.creators.blender.workfile" label = "Workfile" - family = "workfile" + product_type = "workfile" icon = "fa5.file" def create(self): @@ -43,7 +43,7 @@ class CreateWorkfile(BaseCreator, AutoCreator): if not workfile_instance: asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( task_name, task_name, asset_doc, project_name, host_name ) data = { @@ -63,7 +63,7 @@ class CreateWorkfile(BaseCreator, AutoCreator): ) self.log.info("Auto-creating workfile instance...") workfile_instance = CreatedInstance( - self.family, subset_name, data, self + self.product_type, product_name, data, self ) self._add_instance_to_context(workfile_instance) @@ -73,13 +73,13 @@ class CreateWorkfile(BaseCreator, AutoCreator): ): # Update instance context if it's different asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( task_name, task_name, asset_doc, project_name, host_name ) workfile_instance["folderPath"] = asset_name workfile_instance["task"] = task_name - workfile_instance["subset"] = subset_name + workfile_instance["productName"] = product_name instance_node = bpy.data.collections.get(AVALON_CONTAINERS) if not instance_node: diff --git a/client/ayon_core/hosts/blender/plugins/load/import_workfile.py b/client/ayon_core/hosts/blender/plugins/load/import_workfile.py index 061c6108ad..5a801da848 100644 --- a/client/ayon_core/hosts/blender/plugins/load/import_workfile.py +++ b/client/ayon_core/hosts/blender/plugins/load/import_workfile.py @@ -4,10 +4,10 @@ from ayon_core.hosts.blender.api import plugin def append_workfile(context, fname, do_import): - asset = context['asset']['name'] - subset = context['subset']['name'] + folder_name = context['asset']['name'] + product_name = context['subset']['name'] - group_name = plugin.prepare_scene_name(asset, subset) + group_name = plugin.prepare_scene_name(folder_name, product_name) # We need to preserve the original names of the scenes, otherwise, # if there are duplicate names in the current workfile, the imported diff --git a/client/ayon_core/hosts/blender/plugins/load/load_abc.py b/client/ayon_core/hosts/blender/plugins/load/load_abc.py index b25f4eb277..4fa9881376 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_abc.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_abc.py @@ -134,13 +134,15 @@ class CacheModelLoader(plugin.AssetLoader): """ libpath = self.filepath_from_context(context) - asset = context["asset"]["name"] - subset = context["subset"]["name"] + folder_name = context["asset"]["name"] + product_name = context["subset"]["name"] - asset_name = plugin.prepare_scene_name(asset, subset) - unique_number = plugin.get_unique_number(asset, subset) - group_name = plugin.prepare_scene_name(asset, subset, unique_number) - namespace = namespace or f"{asset}_{unique_number}" + asset_name = plugin.prepare_scene_name(folder_name, product_name) + unique_number = plugin.get_unique_number(folder_name, product_name) + group_name = plugin.prepare_scene_name( + folder_name, product_name, unique_number + ) + namespace = namespace or f"{folder_name}_{unique_number}" containers = bpy.data.collections.get(AVALON_CONTAINERS) if not containers: @@ -159,6 +161,7 @@ class CacheModelLoader(plugin.AssetLoader): self._link_objects(objects, asset_group, containers, asset_group) + product_type = context["subset"]["data"]["family"] asset_group[AVALON_PROPERTY] = { "schema": "openpype:container-2.0", "id": AVALON_CONTAINER_ID, @@ -169,7 +172,7 @@ class CacheModelLoader(plugin.AssetLoader): "libpath": libpath, "asset_name": asset_name, "parent": str(context["representation"]["parent"]), - "family": context["representation"]["context"]["family"], + "productType": product_type, "objectName": group_name } diff --git a/client/ayon_core/hosts/blender/plugins/load/load_action.py b/client/ayon_core/hosts/blender/plugins/load/load_action.py index 5c8ba0df44..61ea996b2a 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_action.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_action.py @@ -44,11 +44,11 @@ class BlendActionLoader(plugin.AssetLoader): """ libpath = self.filepath_from_context(context) - asset = context["asset"]["name"] - subset = context["subset"]["name"] - lib_container = plugin.prepare_scene_name(asset, subset) + folder_name = context["asset"]["name"] + product_name = context["subset"]["name"] + lib_container = plugin.prepare_scene_name(folder_name, product_name) container_name = plugin.prepare_scene_name( - asset, subset, namespace + folder_name, product_name, namespace ) container = bpy.data.collections.new(lib_container) diff --git a/client/ayon_core/hosts/blender/plugins/load/load_audio.py b/client/ayon_core/hosts/blender/plugins/load/load_audio.py index 007889f6f6..023a987d63 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_audio.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_audio.py @@ -39,13 +39,15 @@ class AudioLoader(plugin.AssetLoader): options: Additional settings dictionary """ libpath = self.filepath_from_context(context) - asset = context["asset"]["name"] - subset = context["subset"]["name"] + folder_name = context["asset"]["name"] + product_name = context["subset"]["name"] - asset_name = plugin.prepare_scene_name(asset, subset) - unique_number = plugin.get_unique_number(asset, subset) - group_name = plugin.prepare_scene_name(asset, subset, unique_number) - namespace = namespace or f"{asset}_{unique_number}" + asset_name = plugin.prepare_scene_name(folder_name, product_name) + unique_number = plugin.get_unique_number(folder_name, product_name) + group_name = plugin.prepare_scene_name( + folder_name, product_name, unique_number + ) + namespace = namespace or f"{folder_name}_{unique_number}" avalon_container = bpy.data.collections.get(AVALON_CONTAINERS) if not avalon_container: @@ -85,7 +87,7 @@ class AudioLoader(plugin.AssetLoader): "libpath": libpath, "asset_name": asset_name, "parent": str(context["representation"]["parent"]), - "family": context["representation"]["context"]["family"], + "productType": context["subset"]["data"]["family"], "objectName": group_name, "audio": audio } diff --git a/client/ayon_core/hosts/blender/plugins/load/load_blend.py b/client/ayon_core/hosts/blender/plugins/load/load_blend.py index c9862f9841..84a4bd4398 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_blend.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_blend.py @@ -127,20 +127,22 @@ class BlendLoader(plugin.AssetLoader): options: Additional settings dictionary """ libpath = self.filepath_from_context(context) - asset = context["asset"]["name"] - subset = context["subset"]["name"] + folder_name = context["asset"]["name"] + product_name = context["subset"]["name"] try: - family = context["representation"]["context"]["family"] + product_type = context["subset"]["data"]["family"] except ValueError: - family = "model" + product_type = "model" representation = str(context["representation"]["_id"]) - asset_name = plugin.prepare_scene_name(asset, subset) - unique_number = plugin.get_unique_number(asset, subset) - group_name = plugin.prepare_scene_name(asset, subset, unique_number) - namespace = namespace or f"{asset}_{unique_number}" + asset_name = plugin.prepare_scene_name(folder_name, product_name) + unique_number = plugin.get_unique_number(folder_name, product_name) + group_name = plugin.prepare_scene_name( + folder_name, product_name, unique_number + ) + namespace = namespace or f"{folder_name}_{unique_number}" avalon_container = bpy.data.collections.get(AVALON_CONTAINERS) if not avalon_container: @@ -149,8 +151,8 @@ class BlendLoader(plugin.AssetLoader): container, members = self._process_data(libpath, group_name) - if family == "layout": - self._post_process_layout(container, asset, representation) + if product_type == "layout": + self._post_process_layout(container, folder_name, representation) avalon_container.objects.link(container) @@ -164,7 +166,7 @@ class BlendLoader(plugin.AssetLoader): "libpath": libpath, "asset_name": asset_name, "parent": str(context["representation"]["parent"]), - "family": context["representation"]["context"]["family"], + "productType": context["subset"]["data"]["family"], "objectName": group_name, "members": members, } diff --git a/client/ayon_core/hosts/blender/plugins/load/load_blendscene.py b/client/ayon_core/hosts/blender/plugins/load/load_blendscene.py index 248bf5a901..ed9dcdeb09 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_blendscene.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_blendscene.py @@ -34,7 +34,7 @@ class BlendSceneLoader(plugin.AssetLoader): return None - def _process_data(self, libpath, group_name, family): + def _process_data(self, libpath, group_name, product_type): # Append all the data from the .blend file with bpy.data.libraries.load( libpath, link=False, relative=False @@ -82,25 +82,29 @@ class BlendSceneLoader(plugin.AssetLoader): options: Additional settings dictionary """ libpath = self.filepath_from_context(context) - asset = context["asset"]["name"] - subset = context["subset"]["name"] + folder_name = context["asset"]["name"] + product_name = context["subset"]["name"] try: - family = context["representation"]["context"]["family"] + product_type = context["subset"]["data"]["family"] except ValueError: - family = "model" + product_type = "model" - asset_name = plugin.prepare_scene_name(asset, subset) - unique_number = plugin.get_unique_number(asset, subset) - group_name = plugin.prepare_scene_name(asset, subset, unique_number) - namespace = namespace or f"{asset}_{unique_number}" + asset_name = plugin.prepare_scene_name(folder_name, product_name) + unique_number = plugin.get_unique_number(folder_name, product_name) + group_name = plugin.prepare_scene_name( + folder_name, product_name, unique_number + ) + namespace = namespace or f"{folder_name}_{unique_number}" avalon_container = bpy.data.collections.get(AVALON_CONTAINERS) if not avalon_container: avalon_container = bpy.data.collections.new(name=AVALON_CONTAINERS) bpy.context.scene.collection.children.link(avalon_container) - container, members = self._process_data(libpath, group_name, family) + container, members = self._process_data( + libpath, group_name, product_type + ) avalon_container.children.link(container) @@ -114,7 +118,7 @@ class BlendSceneLoader(plugin.AssetLoader): "libpath": libpath, "asset_name": asset_name, "parent": str(context["representation"]["parent"]), - "family": context["representation"]["context"]["family"], + "productType": context["subset"]["data"]["family"], "objectName": group_name, "members": members, } @@ -167,8 +171,12 @@ class BlendSceneLoader(plugin.AssetLoader): self.exec_remove(container) - family = container["family"] - asset_group, members = self._process_data(libpath, group_name, family) + product_type = container.get("productType") + if product_type is None: + product_type = container["family"] + asset_group, members = self._process_data( + libpath, group_name, product_type + ) for member in members: if member.name in collection_parents: diff --git a/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py b/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py index 8f0bd6741d..65c73b4168 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py @@ -84,13 +84,15 @@ class AbcCameraLoader(plugin.AssetLoader): libpath = self.filepath_from_context(context) - asset = context["asset"]["name"] - subset = context["subset"]["name"] + folder_name = context["asset"]["name"] + product_name = context["subset"]["name"] - asset_name = plugin.prepare_scene_name(asset, subset) - unique_number = plugin.get_unique_number(asset, subset) - group_name = plugin.prepare_scene_name(asset, subset, unique_number) - namespace = namespace or f"{asset}_{unique_number}" + asset_name = plugin.prepare_scene_name(folder_name, product_name) + unique_number = plugin.get_unique_number(folder_name, product_name) + group_name = plugin.prepare_scene_name( + folder_name, product_name, unique_number + ) + namespace = namespace or f"{folder_name}_{unique_number}" avalon_container = bpy.data.collections.get(AVALON_CONTAINERS) if not avalon_container: @@ -121,7 +123,7 @@ class AbcCameraLoader(plugin.AssetLoader): "libpath": libpath, "asset_name": asset_name, "parent": str(context["representation"]["parent"]), - "family": context["representation"]["context"]["family"], + "productType": context["subset"]["data"]["family"], "objectName": group_name, } diff --git a/client/ayon_core/hosts/blender/plugins/load/load_camera_fbx.py b/client/ayon_core/hosts/blender/plugins/load/load_camera_fbx.py index 7642871dc7..3e5a4e6e75 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_camera_fbx.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_camera_fbx.py @@ -87,13 +87,15 @@ class FbxCameraLoader(plugin.AssetLoader): options: Additional settings dictionary """ libpath = self.filepath_from_context(context) - asset = context["asset"]["name"] - subset = context["subset"]["name"] + folder_name = context["asset"]["name"] + product_name = context["subset"]["name"] - asset_name = plugin.prepare_scene_name(asset, subset) - unique_number = plugin.get_unique_number(asset, subset) - group_name = plugin.prepare_scene_name(asset, subset, unique_number) - namespace = namespace or f"{asset}_{unique_number}" + asset_name = plugin.prepare_scene_name(folder_name, product_name) + unique_number = plugin.get_unique_number(folder_name, product_name) + group_name = plugin.prepare_scene_name( + folder_name, product_name, unique_number + ) + namespace = namespace or f"{folder_name}_{unique_number}" avalon_container = bpy.data.collections.get(AVALON_CONTAINERS) if not avalon_container: @@ -124,7 +126,7 @@ class FbxCameraLoader(plugin.AssetLoader): "libpath": libpath, "asset_name": asset_name, "parent": str(context["representation"]["parent"]), - "family": context["representation"]["context"]["family"], + "productType": context["subset"]["data"]["family"], "objectName": group_name } diff --git a/client/ayon_core/hosts/blender/plugins/load/load_fbx.py b/client/ayon_core/hosts/blender/plugins/load/load_fbx.py index 03993c9f5e..e9d5522568 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_fbx.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_fbx.py @@ -131,13 +131,15 @@ class FbxModelLoader(plugin.AssetLoader): options: Additional settings dictionary """ libpath = self.filepath_from_context(context) - asset = context["asset"]["name"] - subset = context["subset"]["name"] + folder_name = context["asset"]["name"] + product_name = context["subset"]["name"] - asset_name = plugin.prepare_scene_name(asset, subset) - unique_number = plugin.get_unique_number(asset, subset) - group_name = plugin.prepare_scene_name(asset, subset, unique_number) - namespace = namespace or f"{asset}_{unique_number}" + asset_name = plugin.prepare_scene_name(folder_name, product_name) + unique_number = plugin.get_unique_number(folder_name, product_name) + group_name = plugin.prepare_scene_name( + folder_name, product_name, unique_number + ) + namespace = namespace or f"{folder_name}_{unique_number}" avalon_container = bpy.data.collections.get(AVALON_CONTAINERS) if not avalon_container: @@ -168,7 +170,7 @@ class FbxModelLoader(plugin.AssetLoader): "libpath": libpath, "asset_name": asset_name, "parent": str(context["representation"]["parent"]), - "family": context["representation"]["context"]["family"], + "productType": context["subset"]["data"]["family"], "objectName": group_name } diff --git a/client/ayon_core/hosts/blender/plugins/load/load_layout_json.py b/client/ayon_core/hosts/blender/plugins/load/load_layout_json.py index f48862a803..126291464b 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_layout_json.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_layout_json.py @@ -50,11 +50,11 @@ class JsonLayoutLoader(plugin.AssetLoader): if anim_collection: bpy.data.collections.remove(anim_collection) - def _get_loader(self, loaders, family): + def _get_loader(self, loaders, product_type): name = "" - if family == 'rig': + if product_type == 'rig': name = "BlendRigLoader" - elif family == 'model': + elif product_type == 'model': name = "BlendModelLoader" if name == "": @@ -76,10 +76,12 @@ class JsonLayoutLoader(plugin.AssetLoader): for element in data: reference = element.get('reference') - family = element.get('family') + product_type = element.get("product_type") + if product_type is None: + product_type = element.get("family") loaders = loaders_from_representation(all_loaders, reference) - loader = self._get_loader(loaders, family) + loader = self._get_loader(loaders, product_type) if not loader: continue @@ -95,7 +97,7 @@ class JsonLayoutLoader(plugin.AssetLoader): 'parent': asset_group, 'transform': element.get('transform'), 'action': action, - 'create_animation': True if family == 'rig' else False, + 'create_animation': True if product_type == 'rig' else False, 'animation_asset': asset } @@ -127,7 +129,7 @@ class JsonLayoutLoader(plugin.AssetLoader): # legacy_create( # creator_plugin, # name="camera", - # # name=f"{unique_number}_{subset}_animation", + # # name=f"{unique_number}_{product[name]}_animation", # asset=asset, # options={"useSelection": False} # # data={"dependencies": str(context["representation"]["_id"])} @@ -146,13 +148,15 @@ class JsonLayoutLoader(plugin.AssetLoader): options: Additional settings dictionary """ libpath = self.filepath_from_context(context) - asset = context["asset"]["name"] - subset = context["subset"]["name"] + folder_name = context["asset"]["name"] + product_name = context["subset"]["name"] - asset_name = plugin.prepare_scene_name(asset, subset) - unique_number = plugin.get_unique_number(asset, subset) - group_name = plugin.prepare_scene_name(asset, subset, unique_number) - namespace = namespace or f"{asset}_{unique_number}" + asset_name = plugin.prepare_scene_name(folder_name, product_name) + unique_number = plugin.get_unique_number(folder_name, product_name) + group_name = plugin.prepare_scene_name( + folder_name, product_name, unique_number + ) + namespace = namespace or f"{folder_name}_{unique_number}" avalon_container = bpy.data.collections.get(AVALON_CONTAINERS) if not avalon_container: @@ -177,7 +181,7 @@ class JsonLayoutLoader(plugin.AssetLoader): "libpath": libpath, "asset_name": asset_name, "parent": str(context["representation"]["parent"]), - "family": context["representation"]["context"]["family"], + "productType": context["subset"]["data"]["family"], "objectName": group_name } @@ -239,7 +243,10 @@ class JsonLayoutLoader(plugin.AssetLoader): for obj in asset_group.children: obj_meta = obj.get(AVALON_PROPERTY) - if obj_meta.get('family') == 'rig': + product_type = obj_meta.get("productType") + if product_type is None: + product_type = obj_meta.get("family") + if product_type == "rig": rig = None for child in obj.children: if child.type == 'ARMATURE': diff --git a/client/ayon_core/hosts/blender/plugins/load/load_look.py b/client/ayon_core/hosts/blender/plugins/load/load_look.py index f9ebf98912..27632f5705 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_look.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_look.py @@ -93,18 +93,18 @@ class BlendLookLoader(plugin.AssetLoader): """ libpath = self.filepath_from_context(context) - asset = context["asset"]["name"] - subset = context["subset"]["name"] + folder_name = context["asset"]["name"] + product_name = context["subset"]["name"] lib_container = plugin.prepare_scene_name( - asset, subset + folder_name, product_name ) unique_number = plugin.get_unique_number( - asset, subset + folder_name, product_name ) - namespace = namespace or f"{asset}_{unique_number}" + namespace = namespace or f"{folder_name}_{unique_number}" container_name = plugin.prepare_scene_name( - asset, subset, unique_number + folder_name, product_name, unique_number ) container = bpy.data.collections.new(lib_container) @@ -131,7 +131,7 @@ class BlendLookLoader(plugin.AssetLoader): metadata["materials"] = materials metadata["parent"] = str(context["representation"]["parent"]) - metadata["family"] = context["representation"]["context"]["family"] + metadata["product_type"] = context["subset"]["data"]["family"] nodes = list(container.objects) nodes.append(container) diff --git a/client/ayon_core/hosts/blender/plugins/publish/collect_instance.py b/client/ayon_core/hosts/blender/plugins/publish/collect_instance.py index f9338cd30a..d47c69a270 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/collect_instance.py +++ b/client/ayon_core/hosts/blender/plugins/publish/collect_instance.py @@ -25,7 +25,7 @@ class CollectBlenderInstanceData(pyblish.api.InstancePlugin): members.extend(instance_node.children) # Special case for animation instances, include armatures - if instance.data["family"] == "animation": + if instance.data["productType"] == "animation": for obj in instance_node.objects: if obj.type == 'EMPTY' and obj.get(AVALON_PROPERTY): members.extend( diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_abc.py b/client/ayon_core/hosts/blender/plugins/publish/extract_abc.py index 2f89426e56..cf753637ea 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_abc.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_abc.py @@ -19,9 +19,9 @@ class ExtractABC(publish.Extractor, publish.OptionalPyblishPluginMixin): # Define extract output file path stagingdir = self.staging_dir(instance) - asset_name = instance.data["assetEntity"]["name"] - subset = instance.data["subset"] - instance_name = f"{asset_name}_{subset}" + folder_name = instance.data["assetEntity"]["name"] + product_name = instance.data["productName"] + instance_name = f"{folder_name}_{product_name}" filename = f"{instance_name}.abc" filepath = os.path.join(stagingdir, filename) diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_abc_animation.py b/client/ayon_core/hosts/blender/plugins/publish/extract_abc_animation.py index 41ad2a99b8..1aea8988e2 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_abc_animation.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_abc_animation.py @@ -23,9 +23,9 @@ class ExtractAnimationABC( # Define extract output file path stagingdir = self.staging_dir(instance) - asset_name = instance.data["assetEntity"]["name"] - subset = instance.data["subset"] - instance_name = f"{asset_name}_{subset}" + folder_name = instance.data["assetEntity"]["name"] + product_name = instance.data["prouctName"] + instance_name = f"{folder_name}_{product_name}" filename = f"{instance_name}.abc" filepath = os.path.join(stagingdir, filename) diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_blend.py b/client/ayon_core/hosts/blender/plugins/publish/extract_blend.py index 00e4074f55..2973df74b4 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_blend.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_blend.py @@ -20,9 +20,9 @@ class ExtractBlend(publish.Extractor, publish.OptionalPyblishPluginMixin): # Define extract output file path stagingdir = self.staging_dir(instance) - asset_name = instance.data["assetEntity"]["name"] - subset = instance.data["subset"] - instance_name = f"{asset_name}_{subset}" + folder_name = instance.data["assetEntity"]["name"] + product_name = instance.data["prouctName"] + instance_name = f"{folder_name}_{product_name}" filename = f"{instance_name}.blend" filepath = os.path.join(stagingdir, filename) diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_blend_animation.py b/client/ayon_core/hosts/blender/plugins/publish/extract_blend_animation.py index 6b0d6195b6..b30990ab15 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_blend_animation.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_blend_animation.py @@ -23,9 +23,9 @@ class ExtractBlendAnimation( # Define extract output file path stagingdir = self.staging_dir(instance) - asset_name = instance.data["assetEntity"]["name"] - subset = instance.data["subset"] - instance_name = f"{asset_name}_{subset}" + folder_name = instance.data["assetEntity"]["name"] + product_name = instance.data["prouctName"] + instance_name = f"{folder_name}_{product_name}" filename = f"{instance_name}.blend" filepath = os.path.join(stagingdir, filename) diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_camera_abc.py b/client/ayon_core/hosts/blender/plugins/publish/extract_camera_abc.py index efa1faa87c..ff14d70696 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_camera_abc.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_camera_abc.py @@ -21,9 +21,9 @@ class ExtractCameraABC(publish.Extractor, publish.OptionalPyblishPluginMixin): # Define extract output file path stagingdir = self.staging_dir(instance) - asset_name = instance.data["assetEntity"]["name"] - subset = instance.data["subset"] - instance_name = f"{asset_name}_{subset}" + folder_name = instance.data["assetEntity"]["name"] + product_name = instance.data["productName"] + instance_name = f"{folder_name}_{product_name}" filename = f"{instance_name}.abc" filepath = os.path.join(stagingdir, filename) diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_camera_fbx.py b/client/ayon_core/hosts/blender/plugins/publish/extract_camera_fbx.py index b2b6cd602d..21dfa457be 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_camera_fbx.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_camera_fbx.py @@ -20,9 +20,9 @@ class ExtractCamera(publish.Extractor, publish.OptionalPyblishPluginMixin): # Define extract output file path stagingdir = self.staging_dir(instance) - asset_name = instance.data["assetEntity"]["name"] - subset = instance.data["subset"] - instance_name = f"{asset_name}_{subset}" + folder_name = instance.data["assetEntity"]["name"] + product_name = instance.data["prouctName"] + instance_name = f"{folder_name}_{product_name}" filename = f"{instance_name}.fbx" filepath = os.path.join(stagingdir, filename) diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_fbx.py b/client/ayon_core/hosts/blender/plugins/publish/extract_fbx.py index 7e8c13fea8..6b9dc01f98 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_fbx.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_fbx.py @@ -21,9 +21,9 @@ class ExtractFBX(publish.Extractor, publish.OptionalPyblishPluginMixin): # Define extract output file path stagingdir = self.staging_dir(instance) - asset_name = instance.data["assetEntity"]["name"] - subset = instance.data["subset"] - instance_name = f"{asset_name}_{subset}" + folder_name = instance.data["assetEntity"]["name"] + product_name = instance.data["prouctName"] + instance_name = f"{folder_name}_{product_name}" filename = f"{instance_name}.fbx" filepath = os.path.join(stagingdir, filename) diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_fbx_animation.py b/client/ayon_core/hosts/blender/plugins/publish/extract_fbx_animation.py index 3ad4cc3aa9..4b65c58879 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_fbx_animation.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_fbx_animation.py @@ -145,9 +145,9 @@ class ExtractAnimationFBX( root.select_set(True) armature.select_set(True) - asset_name = instance.data["assetEntity"]["name"] - subset = instance.data["subset"] - instance_name = f"{asset_name}_{subset}" + folder_name = instance.data["assetEntity"]["name"] + product_name = instance.data["prouctName"] + instance_name = f"{folder_name}_{product_name}" fbx_filename = f"{instance_name}_{armature.name}.fbx" filepath = os.path.join(stagingdir, fbx_filename) diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_layout.py b/client/ayon_core/hosts/blender/plugins/publish/extract_layout.py index f868db3e74..8409014533 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_layout.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_layout.py @@ -133,7 +133,7 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin): fbx_count = 0 - project_name = instance.context.data["projectEntity"]["name"] + project_name = instance.context.data["projectName"] for asset in asset_group.children: metadata = asset.get(AVALON_PROPERTY) if not metadata: @@ -147,7 +147,9 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin): continue version_id = metadata["parent"] - family = metadata["family"] + product_type = metadata.get("product_type") + if product_type is None: + product_type = metadata["family"] self.log.debug("Parent: {}".format(version_id)) # Get blend reference @@ -179,7 +181,8 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin): json_element["reference_fbx"] = str(fbx_id) if abc_id: json_element["reference_abc"] = str(abc_id) - json_element["family"] = family + json_element["family"] = product_type + json_element["product_type"] = product_type json_element["instance_name"] = asset.name json_element["asset_name"] = metadata["asset_name"] json_element["file_path"] = metadata["libpath"] @@ -215,7 +218,7 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin): ] # Extract the animation as well - if family == "rig": + if product_type == "rig": f, n = self._export_animation( asset, instance, stagingdir, fbx_count) if f: @@ -225,9 +228,9 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin): json_data.append(json_element) - asset_name = instance.data["assetEntity"]["name"] - subset = instance.data["subset"] - instance_name = f"{asset_name}_{subset}" + folder_name = instance.data["assetEntity"]["name"] + product_name = instance.data["productName"] + instance_name = f"{folder_name}_{product_name}" json_filename = f"{instance_name}.json" json_path = os.path.join(stagingdir, json_filename) diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_playblast.py b/client/ayon_core/hosts/blender/plugins/publish/extract_playblast.py index 04fc2c5c39..638c9f6f80 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_playblast.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_playblast.py @@ -55,9 +55,9 @@ class ExtractPlayblast(publish.Extractor, publish.OptionalPyblishPluginMixin): # get output path stagingdir = self.staging_dir(instance) - asset_name = instance.data["assetEntity"]["name"] - subset = instance.data["subset"] - filename = f"{asset_name}_{subset}" + folder_name = instance.data["assetEntity"]["name"] + product_name = instance.data["prouctName"] + filename = f"{folder_name}_{product_name}" path = os.path.join(stagingdir, filename) diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_thumbnail.py b/client/ayon_core/hosts/blender/plugins/publish/extract_thumbnail.py index ec701610ce..88293dde96 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_thumbnail.py @@ -32,9 +32,9 @@ class ExtractThumbnail(publish.Extractor): return stagingdir = self.staging_dir(instance) - asset_name = instance.data["assetEntity"]["name"] - subset = instance.data["subset"] - filename = f"{asset_name}_{subset}" + folder_name = instance.data["assetEntity"]["name"] + product_name = instance.data["prouctName"] + filename = f"{folder_name}_{product_name}" path = os.path.join(stagingdir, filename) @@ -42,11 +42,11 @@ class ExtractThumbnail(publish.Extractor): camera = instance.data.get("review_camera", "AUTO") start = instance.data.get("frameStart", bpy.context.scene.frame_start) - family = instance.data.get("family") + product_type = instance.data["productType"] isolate = instance.data("isolate", None) presets = json.loads(self.presets) - preset = presets.get(family, {}) + preset = presets.get(product_type, {}) preset.update({ "camera": camera, diff --git a/client/ayon_core/hosts/blender/plugins/publish/integrate_animation.py b/client/ayon_core/hosts/blender/plugins/publish/integrate_animation.py index c461d56e7c..35f7fb4496 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/integrate_animation.py +++ b/client/ayon_core/hosts/blender/plugins/publish/integrate_animation.py @@ -28,25 +28,27 @@ class IntegrateAnimation( # Update the json file for the setdress to add the published # representations of the animations for json_dict in data: + json_product_name = json_dict["productName"] i = None for elem in instance.context: - if elem.data.get('subset') == json_dict['subset']: + if elem.data["productName"] == json_product_name: i = elem break if not i: continue rep = None - pub_repr = i.data.get('published_representations') + pub_repr = i.data["published_representations"] for elem in pub_repr: - if pub_repr.get(elem).get('representation').get('name') == "fbx": - rep = pub_repr.get(elem) + if pub_repr[elem]["representation"]["name"] == "fbx": + rep = pub_repr[elem] break if not rep: continue - obj_id = rep.get('representation').get('_id') + obj_id = rep["representation"]["_id"] if obj_id: - json_dict['_id'] = str(obj_id) + json_dict["_id"] = str(obj_id) + json_dict["representation_id"] = str(obj_id) with open(json_path, "w") as file: json.dump(data, fp=file, indent=2) diff --git a/client/ayon_core/hosts/blender/plugins/publish/validate_file_saved.py b/client/ayon_core/hosts/blender/plugins/publish/validate_file_saved.py index 0b8762fed5..6a053eb47b 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/validate_file_saved.py +++ b/client/ayon_core/hosts/blender/plugins/publish/validate_file_saved.py @@ -26,6 +26,7 @@ class ValidateFileSaved(pyblish.api.ContextPlugin, hosts = ["blender"] label = "Validate File Saved" optional = False + # TODO rename to 'exclude_product_types' exclude_families = [] actions = [SaveWorkfileAction] @@ -41,8 +42,8 @@ class ValidateFileSaved(pyblish.api.ContextPlugin, # Do not validate workfile has unsaved changes if only instances # present of families that should be excluded - families = { - instance.data["family"] for instance in context + product_types = { + instance.data["productType"] for instance in context # Consider only enabled instances if instance.data.get("publish", True) and instance.data.get("active", True) @@ -52,7 +53,7 @@ class ValidateFileSaved(pyblish.api.ContextPlugin, return any(family in exclude_family for exclude_family in self.exclude_families) - if all(is_excluded(family) for family in families): + if all(is_excluded(product_type) for product_type in product_types): self.log.debug("Only excluded families found, skipping workfile " "unsaved changes validation..") return From 7ca9d35fb3c939c2e341edf04c0bb987dca2b894 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 11:19:37 +0100 Subject: [PATCH 256/573] remove deprecated pre collect plugin --- .../plugins/publish/pre_collect_render.py | 54 ------------------- 1 file changed, 54 deletions(-) delete mode 100644 client/ayon_core/hosts/aftereffects/plugins/publish/pre_collect_render.py diff --git a/client/ayon_core/hosts/aftereffects/plugins/publish/pre_collect_render.py b/client/ayon_core/hosts/aftereffects/plugins/publish/pre_collect_render.py deleted file mode 100644 index de3c935dff..0000000000 --- a/client/ayon_core/hosts/aftereffects/plugins/publish/pre_collect_render.py +++ /dev/null @@ -1,54 +0,0 @@ -import json -import pyblish.api -from ayon_core.hosts.aftereffects.api import AfterEffectsHost - - -class PreCollectRender(pyblish.api.ContextPlugin): - """ - Checks if render instance is of old type, adds to families to both - existing collectors work same way. - - Could be removed in the future when no one uses old publish. - """ - - label = "PreCollect Render" - order = pyblish.api.CollectorOrder + 0.400 - hosts = ["aftereffects"] - - family_remapping = { - "render": ("render.farm", "farm"), # (family, label) - "renderLocal": ("render.local", "local") - } - - def process(self, context): - if context.data.get("newPublishing"): - self.log.debug("Not applicable for New Publisher, skip") - return - - for inst in AfterEffectsHost().list_instances(): - if inst.get("creator_attributes"): - raise ValueError("Instance created in New publisher, " - "cannot be published in Pyblish.\n" - "Please publish in New Publisher " - "or recreate instances with legacy Creators") - - if inst["family"] not in self.family_remapping.keys(): - continue - - if not inst["members"]: - raise ValueError("Couldn't find id, unable to publish. " + - "Please recreate instance.") - - instance = context.create_instance(inst["subset"]) - inst["families"] = [self.family_remapping[inst["family"]][0]] - instance.data.update(inst) - - self._debug_log(instance) - - def _debug_log(self, instance): - def _default_json(value): - return str(value) - - self.log.info( - json.dumps(instance.data, indent=4, default=_default_json) - ) From 1c1758eebbe732e6bc99459e09c6859048ddc18f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 11:20:03 +0100 Subject: [PATCH 257/573] aftereffects using product name and type --- .../plugins/create/create_render.py | 67 +++++++------- .../plugins/create/workfile_creator.py | 16 ++-- .../plugins/load/load_background.py | 2 +- .../plugins/publish/collect_render.py | 21 ++--- .../plugins/publish/collect_review.py | 2 +- .../plugins/publish/collect_workfile.py | 87 +++---------------- .../publish/help/validate_instance_asset.xml | 6 +- 7 files changed, 73 insertions(+), 128 deletions(-) diff --git a/client/ayon_core/hosts/aftereffects/plugins/create/create_render.py b/client/ayon_core/hosts/aftereffects/plugins/create/create_render.py index bd005f5d4f..a795d61b39 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/create/create_render.py +++ b/client/ayon_core/hosts/aftereffects/plugins/create/create_render.py @@ -22,7 +22,7 @@ class RenderCreator(Creator): """ identifier = "render" label = "Render" - family = "render" + product_type = "render" description = "Render creator" create_allow_context_change = True @@ -31,7 +31,7 @@ class RenderCreator(Creator): mark_for_review = True force_setting_values = True - def create(self, subset_name_from_ui, data, pre_create_data): + def create(self, product_name, data, pre_create_data): stub = api.get_stub() # only after After Effects is up try: @@ -63,28 +63,32 @@ class RenderCreator(Creator): comp.name ) if use_composition_name: - if "{composition}" not in subset_name_from_ui.lower(): - subset_name_from_ui += "{Composition}" + if "{composition}" not in product_name.lower(): + product_name += "{Composition}" dynamic_fill = prepare_template_data({"composition": composition_name}) - subset_name = subset_name_from_ui.format(**dynamic_fill) + comp_product_name = product_name.format(**dynamic_fill) data["composition_name"] = composition_name else: - subset_name = subset_name_from_ui - subset_name = re.sub(r"\{composition\}", '', subset_name, - flags=re.IGNORECASE) + comp_product_name = re.sub( + r"\{composition\}", + "", + product_name, + flags=re.IGNORECASE + ) for inst in self.create_context.instances: - if subset_name == inst.subset_name: + if comp_product_name == inst.product_name: raise CreatorError("{} already exists".format( - inst.subset_name)) + inst.product_name)) data["members"] = [comp.id] data["orig_comp_name"] = composition_name - new_instance = CreatedInstance(self.family, subset_name, data, - self) + new_instance = CreatedInstance( + self.product_type, comp_product_name, data, self + ) if "farm" in pre_create_data: use_farm = pre_create_data["farm"] new_instance.creator_attributes["farm"] = use_farm @@ -96,7 +100,7 @@ class RenderCreator(Creator): new_instance.data_to_store()) self._add_instance_to_context(new_instance) - stub.rename_item(comp.id, subset_name) + stub.rename_item(comp.id, comp_product_name) if self.force_setting_values: set_settings(True, True, [comp.id], print_msg=False) @@ -107,7 +111,7 @@ class RenderCreator(Creator): "selected by default.", default=True, label="Use selection"), BoolDef("use_composition_name", - label="Use composition name in subset"), + label="Use composition name in product"), UISeparatorDef(), BoolDef("farm", label="Render on farm"), BoolDef( @@ -133,9 +137,14 @@ class RenderCreator(Creator): def collect_instances(self): for instance_data in cache_and_get_instances(self): - # legacy instances have family=='render' or 'renderLocal', use them - creator_id = (instance_data.get("creator_identifier") or - instance_data.get("family", '').replace("Local", '')) + # legacy instances have product_type=='render' or 'renderLocal', use them + creator_id = instance_data.get("creator_identifier") + if not creator_id: + # NOTE this is for backwards compatibility but probably can be + # removed + creator_id = instance_data.get("family", "") + creator_id = creator_id.replace("Local", "") + if creator_id == self.identifier: instance_data = self._handle_legacy(instance_data) instance = CreatedInstance.from_existing( @@ -147,10 +156,10 @@ class RenderCreator(Creator): for created_inst, _changes in update_list: api.get_stub().imprint(created_inst.get("instance_id"), created_inst.data_to_store()) - subset_change = _changes.get("subset") - if subset_change: + name_change = _changes.get("productName") + if name_change: api.get_stub().rename_item(created_inst.data["members"][0], - subset_change.new_value) + name_change.new_value) def remove_instances(self, instances): """Removes metadata and renames to original comp name if available.""" @@ -183,15 +192,15 @@ class RenderCreator(Creator): def get_detail_description(self): return """Creator for Render instances - Main publishable item in AfterEffects will be of `render` family. + Main publishable item in AfterEffects will be of `render` product type. Result of this item (instance) is picture sequence or video that could be a final delivery product or loaded and used in another DCCs. - Select single composition and create instance of 'render' family or - turn off 'Use selection' to create instance for all compositions. + Select single composition and create instance of 'render' product type + or turn off 'Use selection' to create instance for all compositions. - 'Use composition name in subset' allows to explicitly add composition - name into created subset name. + 'Use composition name in product' allows to explicitly add composition + name into created product name. Position of composition name could be set in `project_settings/global/tools/creator/product_name_profiles` with @@ -201,8 +210,8 @@ class RenderCreator(Creator): be handled at same time. If {composition} placeholder is not us 'product_name_profiles' - composition name will be capitalized and set at the end of subset name - if necessary. + composition name will be capitalized and set at the end of + product name if necessary. If composition name should be used, it will be cleaned up of characters that would cause an issue in published file names. @@ -234,9 +243,9 @@ class RenderCreator(Creator): instance_data["task"] = self.create_context.get_current_task_name() if not instance_data.get("creator_attributes"): - is_old_farm = instance_data["family"] != "renderLocal" + is_old_farm = instance_data.get("family") != "renderLocal" instance_data["creator_attributes"] = {"farm": is_old_farm} - instance_data["family"] = self.family + instance_data["productType"] = self.product_type if instance_data["creator_attributes"].get("mark_for_review") is None: instance_data["creator_attributes"]["mark_for_review"] = True diff --git a/client/ayon_core/hosts/aftereffects/plugins/create/workfile_creator.py b/client/ayon_core/hosts/aftereffects/plugins/create/workfile_creator.py index 49f965800d..5645881975 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/create/workfile_creator.py +++ b/client/ayon_core/hosts/aftereffects/plugins/create/workfile_creator.py @@ -9,7 +9,7 @@ from ayon_core.hosts.aftereffects.api.pipeline import cache_and_get_instances class AEWorkfileCreator(AutoCreator): identifier = "workfile" - family = "workfile" + product_type = "workfile" default_variant = "Main" @@ -20,9 +20,9 @@ class AEWorkfileCreator(AutoCreator): for instance_data in cache_and_get_instances(self): creator_id = instance_data.get("creator_identifier") if creator_id == self.identifier: - subset_name = instance_data["subset"] + product_name = instance_data["productName"] instance = CreatedInstance( - self.family, subset_name, instance_data, self + self.product_type, product_name, instance_data, self ) self._add_instance_to_context(instance) @@ -33,7 +33,7 @@ class AEWorkfileCreator(AutoCreator): def create(self, options=None): existing_instance = None for instance in self.create_context.instances: - if instance.family == self.family: + if instance.product_type == self.product_type: existing_instance = instance break @@ -49,7 +49,7 @@ class AEWorkfileCreator(AutoCreator): if existing_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( self.default_variant, task_name, asset_doc, project_name, host_name ) @@ -64,7 +64,7 @@ class AEWorkfileCreator(AutoCreator): )) new_instance = CreatedInstance( - self.family, subset_name, data, self + self.product_type, product_name, data, self ) self._add_instance_to_context(new_instance) @@ -76,10 +76,10 @@ class AEWorkfileCreator(AutoCreator): or existing_instance["task"] != task_name ): asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( self.default_variant, task_name, asset_doc, project_name, host_name ) existing_instance["folderPath"] = asset_name existing_instance["task"] = task_name - existing_instance["subset"] = subset_name + existing_instance["productName"] = product_name diff --git a/client/ayon_core/hosts/aftereffects/plugins/load/load_background.py b/client/ayon_core/hosts/aftereffects/plugins/load/load_background.py index f23d7ec0bd..c7f743fce4 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/load/load_background.py +++ b/client/ayon_core/hosts/aftereffects/plugins/load/load_background.py @@ -11,7 +11,7 @@ from ayon_core.hosts.aftereffects.api.lib import ( class BackgroundLoader(api.AfterEffectsLoader): """ - Load images from Background family + Load images from Background product type Creates for each background separate folder with all imported images from background json AND automatically created composition with layers, each layer for separate image. diff --git a/client/ayon_core/hosts/aftereffects/plugins/publish/collect_render.py b/client/ayon_core/hosts/aftereffects/plugins/publish/collect_render.py index 32e3b4f3c3..8a03717668 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/publish/collect_render.py +++ b/client/ayon_core/hosts/aftereffects/plugins/publish/collect_render.py @@ -60,8 +60,8 @@ class CollectAERender(publish.AbstractCollectRender): if not inst.data.get("active", True): continue - family = inst.data["family"] - if family not in ["render", "renderLocal"]: # legacy + product_type = inst.data["productType"] + if product_type not in ["render", "renderLocal"]: # legacy continue comp_id = int(inst.data["members"][0]) @@ -81,7 +81,7 @@ class CollectAERender(publish.AbstractCollectRender): fps = comp_info.frameRate # TODO add resolution when supported by extension - task_name = inst.data.get("task") # legacy + task_name = inst.data.get("task") render_q = CollectAERender.get_stub().get_render_info(comp_id) if not render_q: @@ -89,21 +89,22 @@ class CollectAERender(publish.AbstractCollectRender): render_item = render_q[0] instance_families = inst.data.get("families", []) - subset_name = inst.data["subset"] + instance_families.append("render") + product_name = inst.data["productName"] instance = AERenderInstance( - family="render", + productType="render", families=instance_families, version=version, time="", source=current_file, - label="{} - {}".format(subset_name, family), - subset=subset_name, + label="{} - {}".format(product_name, product_type), + productName=product_name, folderPath=inst.data["folderPath"], task=task_name, attachTo=False, setMembers='', publish=True, - name=subset_name, + name=product_name, resolutionWidth=render_item.width, resolutionHeight=render_item.height, pixelAspect=1, @@ -176,7 +177,7 @@ class CollectAERender(publish.AbstractCollectRender): if "#" not in file_name: # single frame (mov)W path = os.path.join(base_dir, "{}_{}_{}.{}".format( render_instance.folderPath, - render_instance.subset, + render_instance.productName, version_str, ext )) @@ -185,7 +186,7 @@ class CollectAERender(publish.AbstractCollectRender): for frame in range(start, end + 1): path = os.path.join(base_dir, "{}_{}_{}.{}.{}".format( render_instance.folderPath, - render_instance.subset, + render_instance.productName, version_str, str(frame).zfill(self.padding_width), ext diff --git a/client/ayon_core/hosts/aftereffects/plugins/publish/collect_review.py b/client/ayon_core/hosts/aftereffects/plugins/publish/collect_review.py index a933b9fed2..667e9cf8b9 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/publish/collect_review.py +++ b/client/ayon_core/hosts/aftereffects/plugins/publish/collect_review.py @@ -3,7 +3,7 @@ Requires: None Provides: - instance -> family ("review") + instance -> families ("review") """ import pyblish.api diff --git a/client/ayon_core/hosts/aftereffects/plugins/publish/collect_workfile.py b/client/ayon_core/hosts/aftereffects/plugins/publish/collect_workfile.py index 107643f56c..cd8e102500 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/publish/collect_workfile.py +++ b/client/ayon_core/hosts/aftereffects/plugins/publish/collect_workfile.py @@ -2,9 +2,6 @@ import os import pyblish.api -from ayon_core.client import get_asset_name_identifier -from ayon_core.pipeline.create import get_subset_name - class CollectWorkfile(pyblish.api.ContextPlugin): """ Adds the AE render instances """ @@ -15,86 +12,24 @@ class CollectWorkfile(pyblish.api.ContextPlugin): default_variant = "Main" def process(self, context): - existing_instance = None + workfile_instance = None for instance in context: - if instance.data["family"] == "workfile": - self.log.debug("Workfile instance found, won't create new") - existing_instance = instance + if instance.data["productType"] == "workfile": + self.log.debug("Workfile instance found") + workfile_instance = instance break current_file = context.data["currentFile"] staging_dir = os.path.dirname(current_file) scene_file = os.path.basename(current_file) - if existing_instance is None: # old publish - instance = self._get_new_instance(context, scene_file) - else: - instance = existing_instance + if workfile_instance is None: + self.log.debug("Workfile instance not found. Skipping") + return # creating representation - representation = { - 'name': 'aep', - 'ext': 'aep', - 'files': scene_file, + workfile_instance.data["representations"].append({ + "name": "aep", + "ext": "aep", + "files": scene_file, "stagingDir": staging_dir, - } - - if not instance.data.get("representations"): - instance.data["representations"] = [] - instance.data["representations"].append(representation) - - instance.data["publish"] = instance.data["active"] # for DL - - def _get_new_instance(self, context, scene_file): - task = context.data["task"] - version = context.data["version"] - asset_entity = context.data["assetEntity"] - project_entity = context.data["projectEntity"] - - folder_path = get_asset_name_identifier(asset_entity) - - instance_data = { - "active": True, - "folderPath": folder_path, - "task": task, - "frameStart": context.data['frameStart'], - "frameEnd": context.data['frameEnd'], - "handleStart": context.data['handleStart'], - "handleEnd": context.data['handleEnd'], - "fps": asset_entity["data"]["fps"], - "resolutionWidth": asset_entity["data"].get( - "resolutionWidth", - project_entity["data"]["resolutionWidth"]), - "resolutionHeight": asset_entity["data"].get( - "resolutionHeight", - project_entity["data"]["resolutionHeight"]), - "pixelAspect": 1, - "step": 1, - "version": version - } - - # workfile instance - family = "workfile" - subset = get_subset_name( - family, - self.default_variant, - context.data["anatomyData"]["task"]["name"], - context.data["assetEntity"], - context.data["anatomyData"]["project"]["name"], - host_name=context.data["hostName"], - project_settings=context.data["project_settings"] - ) - # Create instance - instance = context.create_instance(subset) - - # creating instance data - instance.data.update({ - "subset": subset, - "label": scene_file, - "family": family, - "families": [family], - "representations": list() }) - - instance.data.update(instance_data) - - return instance diff --git a/client/ayon_core/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml b/client/ayon_core/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml index edf62a5141..d89a851c64 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml +++ b/client/ayon_core/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml @@ -3,9 +3,9 @@ Subset context -## Invalid subset context +## Invalid product context -Context of the given subset doesn't match your current scene. +Context of the given product doesn't match your current scene. ### How to repair? @@ -15,7 +15,7 @@ You can fix this with "repair" button on the right and refresh Publish at the bo ### __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 product name "renderCompositingDefault" from folder "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing product for "Robot" asset stayed in the workfile.) \ No newline at end of file From 00436cf904e732c0b57c3441002cd568fd7b7ecf Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 11:40:46 +0100 Subject: [PATCH 258/573] nuke is using product name and type --- client/ayon_core/hosts/nuke/api/lib.py | 69 +++++++------ client/ayon_core/hosts/nuke/api/pipeline.py | 52 +++++----- client/ayon_core/hosts/nuke/api/plugin.py | 99 +++++++++++-------- .../nuke/plugins/create/create_backdrop.py | 10 +- .../nuke/plugins/create/create_camera.py | 10 +- .../hosts/nuke/plugins/create/create_gizmo.py | 10 +- .../hosts/nuke/plugins/create/create_model.py | 10 +- .../nuke/plugins/create/create_source.py | 16 +-- .../nuke/plugins/create/create_write_image.py | 22 ++--- .../plugins/create/create_write_prerender.py | 20 ++-- .../plugins/create/create_write_render.py | 20 ++-- .../nuke/plugins/create/workfile_creator.py | 6 +- .../nuke/plugins/publish/collect_gizmo.py | 9 +- .../nuke/plugins/publish/collect_model.py | 10 +- .../publish/collect_nuke_instance_data.py | 8 +- .../nuke/plugins/publish/collect_reads.py | 4 +- .../nuke/plugins/publish/collect_writes.py | 8 +- .../nuke/plugins/publish/extract_camera.py | 4 +- .../nuke/plugins/publish/extract_model.py | 4 +- .../plugins/publish/extract_render_local.py | 28 +++--- .../publish/extract_review_intermediates.py | 2 +- .../plugins/publish/extract_slate_frame.py | 2 +- .../plugins/publish/validate_exposed_knobs.py | 8 +- .../nuke/plugins/publish/validate_knobs.py | 2 +- .../hosts/nuke/startup/custom_write_node.py | 7 +- 25 files changed, 240 insertions(+), 200 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index e952606bea..ce6f66e3e0 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -436,9 +436,9 @@ def set_avalon_knob_data(node, data=None, prefix="avalon:"): Examples: data = { - 'asset': 'sq020sh0280', - 'family': 'render', - 'subset': 'subsetMain' + 'folderPath': 'sq020sh0280', + 'productType': 'render', + 'productName': 'productMain' } """ data = data or dict() @@ -662,7 +662,7 @@ def get_nuke_imageio_settings(): return get_project_settings(Context.project_name)["nuke"]["imageio"] -def get_imageio_node_setting(node_class, plugin_name, subset): +def get_imageio_node_setting(node_class, plugin_name, product_name): ''' Get preset data for dataflow (fileType, compression, bitDepth) ''' imageio_nodes = get_nuke_imageio_settings()["nodes"] @@ -687,7 +687,7 @@ def get_imageio_node_setting(node_class, plugin_name, subset): get_imageio_node_override_setting( node_class, plugin_name, - subset, + product_name, imageio_node["knobs"] ) @@ -696,7 +696,7 @@ def get_imageio_node_setting(node_class, plugin_name, subset): def get_imageio_node_override_setting( - node_class, plugin_name, subset, knobs_settings + node_class, plugin_name, product_name, knobs_settings ): ''' Get imageio node overrides from settings ''' @@ -707,7 +707,7 @@ def get_imageio_node_override_setting( override_imageio_node = None for onode in override_nodes: log.debug("__ onode: {}".format(onode)) - log.debug("__ subset: {}".format(subset)) + log.debug("__ productName: {}".format(product_name)) if node_class not in onode["nuke_node_class"]: continue @@ -717,7 +717,7 @@ def get_imageio_node_override_setting( if ( onode["subsets"] and not any( - re.search(s.lower(), subset.lower()) + re.search(s.lower(), product_name.lower()) for s in onode["subsets"] ) ): @@ -868,16 +868,16 @@ def check_inventory_versions(): version_docs = get_versions( project_name, version_ids, fields=["_id", "name", "parent"] ) - # Store versions by id and collect subset ids + # Store versions by id and collect product ids version_docs_by_id = {} - subset_ids = set() + product_ids = set() for version_doc in version_docs: version_docs_by_id[version_doc["_id"]] = version_doc - subset_ids.add(version_doc["parent"]) + product_ids.add(version_doc["parent"]) - # Query last versions based on subset ids - last_versions_by_subset_id = get_last_versions( - project_name, subset_ids=subset_ids, fields=["_id", "parent"] + # Query last versions based on product ids + last_versions_by_product_id = get_last_versions( + project_name, subset_ids=product_ids, fields=["_id", "parent"] ) # Loop through collected container nodes and their representation ids @@ -900,9 +900,9 @@ def check_inventory_versions(): ).format(node.name())) continue - # Get last version based on subset id - subset_id = version_doc["parent"] - last_version = last_versions_by_subset_id[subset_id] + # Get last version based on product id + product_id = version_doc["parent"] + last_version = last_versions_by_product_id[product_id] # Check if last version is same as current version if last_version["_id"] == version_doc["_id"]: color_value = "0x4ecd25ff" @@ -959,19 +959,19 @@ def version_up_script(): nukescripts.script_and_write_nodes_version_up() -def check_subsetname_exists(nodes, subset_name): +def check_product_name_exists(nodes, product_name): """ Checking if node is not already created to secure there is no duplicity Arguments: nodes (list): list of nuke.Node objects - subset_name (str): name we try to find + product_name (str): name we try to find Returns: bool: True of False """ return next((True for n in nodes - if subset_name in read_avalon_data(n).get("subset", "")), + if product_name in read_avalon_data(n).get("productName", "")), False) @@ -1012,8 +1012,12 @@ def format_anatomy(data): ) data.update(context_data) data.update({ - "subset": data["subset"], - "family": data["family"], + "subset": data["productName"], + "family": data["productType"], + "product": { + "name": data["productName"], + "type": data["productType"], + }, "frame": "#" * padding, }) return anatomy.format(data) @@ -1048,7 +1052,7 @@ def create_prenodes( prev_node, nodes_setting, plugin_name=None, - subset=None, + product_name=None, **kwargs ): last_node = None @@ -1072,12 +1076,12 @@ def create_prenodes( "dependent": node["dependent"] } - if all([plugin_name, subset]): + if all([plugin_name, product_name]): # find imageio overrides get_imageio_node_override_setting( now_node.Class(), plugin_name, - subset, + product_name, knobs ) @@ -1151,13 +1155,13 @@ def create_write_node( # filtering variables plugin_name = data["creator"] - subset = data["subset"] + product_name = data["productName"] # get knob settings for write node imageio_writes = get_imageio_node_setting( node_class="Write", plugin_name=plugin_name, - subset=subset + product_name=product_name ) for knob in imageio_writes["knobs"]: @@ -1206,7 +1210,7 @@ def create_write_node( prev_node, prenodes, plugin_name, - subset, + product_name, **kwargs ) if last_prenode: @@ -1839,14 +1843,17 @@ Reopening Nuke should synchronize these paths and resolve any discrepancies. nuke_imageio_writes = None if avalon_knob_data: # establish families - families = [avalon_knob_data["family"]] + product_type = avalon_knob_data.get("productType") + if product_type is None: + product_type = avalon_knob_data["family"] + families = [product_type] if avalon_knob_data.get("families"): families.append(avalon_knob_data.get("families")) nuke_imageio_writes = get_imageio_node_setting( node_class=avalon_knob_data["families"], plugin_name=avalon_knob_data["creator"], - subset=avalon_knob_data["subset"] + product_name=avalon_knob_data["productName"] ) elif node_data: nuke_imageio_writes = get_write_node_template_attr(node) @@ -2148,7 +2155,7 @@ def get_write_node_template_attr(node): return get_imageio_node_setting( node_class="Write", plugin_name=plugin_names_mapping[identifier], - subset=node_data["subset"] + product_name=node_data["productName"] ) diff --git a/client/ayon_core/hosts/nuke/api/pipeline.py b/client/ayon_core/hosts/nuke/api/pipeline.py index c747996d9d..582df952d3 100644 --- a/client/ayon_core/hosts/nuke/api/pipeline.py +++ b/client/ayon_core/hosts/nuke/api/pipeline.py @@ -530,7 +530,7 @@ def list_instances(creator_id=None): (list) of dictionaries matching instances format """ instances_by_order = defaultdict(list) - subset_instances = [] + product_instances = [] instance_ids = set() for node in nuke.allNodes(recurseGroups=True): @@ -568,51 +568,59 @@ def list_instances(creator_id=None): else: instance_ids.add(instance_id) - # node name could change, so update subset name data - _update_subset_name_data(instance_data, node) + # node name could change, so update product name data + _update_product_name_data(instance_data, node) if "render_order" not in node.knobs(): - subset_instances.append((node, instance_data)) + product_instances.append((node, instance_data)) continue order = int(node["render_order"].value()) instances_by_order[order].append((node, instance_data)) - # Sort instances based on order attribute or subset name. + # Sort instances based on order attribute or product name. # TODO: remove in future Publisher enhanced with sorting ordered_instances = [] for key in sorted(instances_by_order.keys()): - instances_by_subset = defaultdict(list) + instances_by_product = defaultdict(list) for node, data_ in instances_by_order[key]: - instances_by_subset[data_["subset"]].append((node, data_)) - for subkey in sorted(instances_by_subset.keys()): - ordered_instances.extend(instances_by_subset[subkey]) + product_name = data_.get("productName") + if product_name is None: + product_name = data_.get("subset") + instances_by_product[product_name].append((node, data_)) + for subkey in sorted(instances_by_product.keys()): + ordered_instances.extend(instances_by_product[subkey]) - instances_by_subset = defaultdict(list) - for node, data_ in subset_instances: - instances_by_subset[data_["subset"]].append((node, data_)) - for key in sorted(instances_by_subset.keys()): - ordered_instances.extend(instances_by_subset[key]) + instances_by_product = defaultdict(list) + for node, data_ in product_instances: + product_name = data_.get("productName") + if product_name is None: + product_name = data_.get("subset") + instances_by_product[product_name].append((node, data_)) + for key in sorted(instances_by_product.keys()): + ordered_instances.extend(instances_by_product[key]) return ordered_instances -def _update_subset_name_data(instance_data, node): - """Update subset name data in instance data. +def _update_product_name_data(instance_data, node): + """Update product name data in instance data. Args: instance_data (dict): instance creator data node (nuke.Node): nuke node """ - # make sure node name is subset name - old_subset_name = instance_data["subset"] + # make sure node name is product name + old_product_name = instance_data.get("productName") + if old_product_name is None: + old_product_name = instance_data.get("subset") old_variant = instance_data["variant"] - subset_name_root = old_subset_name.replace(old_variant, "") + product_name_root = old_product_name.replace(old_variant, "") - new_subset_name = node.name() - new_variant = new_subset_name.replace(subset_name_root, "") + new_product_name = node.name() + new_variant = new_product_name.replace(product_name_root, "") - instance_data["subset"] = new_subset_name + instance_data["productName"] = new_product_name instance_data["variant"] = new_variant diff --git a/client/ayon_core/hosts/nuke/api/plugin.py b/client/ayon_core/hosts/nuke/api/plugin.py index ee486c57ab..b36dfc56e6 100644 --- a/client/ayon_core/hosts/nuke/api/plugin.py +++ b/client/ayon_core/hosts/nuke/api/plugin.py @@ -34,7 +34,7 @@ from ayon_core.lib.transcoding import ( from .lib import ( INSTANCE_DATA_KNOB, Knobby, - check_subsetname_exists, + check_product_name_exists, maintained_selection, get_avalon_knob_data, set_avalon_knob_data, @@ -87,15 +87,15 @@ class NukeCreator(NewCreator): for pass_key in keys: creator_attrs[pass_key] = pre_create_data[pass_key] - def check_existing_subset(self, subset_name): - """Make sure subset name is unique. + def check_existing_product(self, product_name): + """Make sure product name is unique. It search within all nodes recursively - and checks if subset name is found in + and checks if product name is found in any node having instance data knob. Arguments: - subset_name (str): Subset name + product_name (str): Subset name """ for node in nuke.allNodes(recurseGroups=True): @@ -108,14 +108,14 @@ class NukeCreator(NewCreator): # a node has no instance data continue - # test if subset name is matching - if node_data.get("subset") == subset_name: + # test if product name is matching + if node_data.get("productType") == product_name: raise NukeCreatorError( ( "A publish instance for '{}' already exists " "in nodes! Please change the variant " "name to ensure unique output." - ).format(subset_name) + ).format(product_name) ) def create_instance_node( @@ -167,22 +167,22 @@ class NukeCreator(NewCreator): else: self.selected_nodes = [] - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): # make sure selected nodes are added self.set_selected_nodes(pre_create_data) - # make sure subset name is unique - self.check_existing_subset(subset_name) + # make sure product name is unique + self.check_existing_product(product_name) try: instance_node = self.create_instance_node( - subset_name, + product_name, node_type=instance_data.pop("node_type", None) ) instance = CreatedInstance( - self.family, - subset_name, + self.product_type, + product_name, instance_data, self ) @@ -227,10 +227,10 @@ class NukeCreator(NewCreator): for created_inst, changes in update_list: instance_node = created_inst.transient_data["node"] - # update instance node name if subset name changed - if "subset" in changes.changed_keys: + # update instance node name if product name changed + if "productName" in changes.changed_keys: instance_node["name"].setValue( - changes["subset"].new_value + changes["productName"].new_value ) # in case node is not existing anymore (user erased it manually) @@ -271,7 +271,7 @@ class NukeWriteCreator(NukeCreator): identifier = "create_write" label = "Create Write" - family = "write" + product_type = "write" icon = "sign-out" def get_linked_knobs(self): @@ -355,22 +355,22 @@ class NukeWriteCreator(NukeCreator): label="Review" ) - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): # make sure selected nodes are added self.set_selected_nodes(pre_create_data) - # make sure subset name is unique - self.check_existing_subset(subset_name) + # make sure product name is unique + self.check_existing_product(product_name) instance_node = self.create_instance_node( - subset_name, + product_name, instance_data ) try: instance = CreatedInstance( - self.family, - subset_name, + self.product_type, + product_name, instance_data, self ) @@ -871,8 +871,8 @@ class ExporterReviewMov(ExporterReview): self.log.info( "__ add_custom_tags: `{0}`".format(add_custom_tags)) - subset = self.instance.data["subset"] - self._temp_nodes[subset] = [] + product_name = self.instance.data["productName"] + self._temp_nodes[product_name] = [] # Read node r_node = nuke.createNode("Read") @@ -892,7 +892,9 @@ class ExporterReviewMov(ExporterReview): r_node["raw"].setValue(1) # connect to Read node - self._shift_to_previous_node_and_temp(subset, r_node, "Read... `{}`") + self._shift_to_previous_node_and_temp( + product_name, r_node, "Read... `{}`" + ) # add reformat node reformat_nodes_config = kwargs["reformat_nodes_config"] @@ -906,7 +908,7 @@ class ExporterReviewMov(ExporterReview): # connect in order self._connect_to_above_nodes( - node, subset, "Reposition node... `{}`" + node, product_name, "Reposition node... `{}`" ) # append reformated tag add_tags.append("reformated") @@ -918,7 +920,9 @@ class ExporterReviewMov(ExporterReview): ipn = get_view_process_node() if ipn is not None: # connect to ViewProcess node - self._connect_to_above_nodes(ipn, subset, "ViewProcess... `{}`") + self._connect_to_above_nodes( + ipn, product_name, "ViewProcess... `{}`" + ) if not self.viewer_lut_raw: # OCIODisplay @@ -942,7 +946,9 @@ class ExporterReviewMov(ExporterReview): view=viewer ) - self._connect_to_above_nodes(dag_node, subset, "OCIODisplay... `{}`") + self._connect_to_above_nodes( + dag_node, product_name, "OCIODisplay... `{}`" + ) # Write node write_node = nuke.createNode("Write") self.log.debug("Path: {}".format(self.path)) @@ -969,8 +975,10 @@ class ExporterReviewMov(ExporterReview): write_node["raw"].setValue(1) # connect write_node.setInput(0, self.previous_node) - self._temp_nodes[subset].append(write_node) - self.log.debug("Write... `{}`".format(self._temp_nodes[subset])) + self._temp_nodes[product_name].append(write_node) + self.log.debug("Write... `{}`".format( + self._temp_nodes[product_name]) + ) # ---------- end nodes creation # ---------- render or save to nk @@ -995,19 +1003,19 @@ class ExporterReviewMov(ExporterReview): self.log.debug("Representation... `{}`".format(self.data)) - self.clean_nodes(subset) + self.clean_nodes(product_name) nuke.scriptSave() return self.data - def _shift_to_previous_node_and_temp(self, subset, node, message): - self._temp_nodes[subset].append(node) + def _shift_to_previous_node_and_temp(self, product_name, node, message): + self._temp_nodes[product_name].append(node) self.previous_node = node - self.log.debug(message.format(self._temp_nodes[subset])) + self.log.debug(message.format(self._temp_nodes[product_name])) - def _connect_to_above_nodes(self, node, subset, message): + def _connect_to_above_nodes(self, node, product_name, message): node.setInput(0, self.previous_node) - self._shift_to_previous_node_and_temp(subset, node, message) + self._shift_to_previous_node_and_temp(product_name, node, message) def convert_to_valid_instaces(): @@ -1015,7 +1023,7 @@ def convert_to_valid_instaces(): Also save as new minor version of workfile. """ - def family_to_identifier(family): + def product_type_to_identifier(product_type): mapping = { "render": "create_write_render", "prerender": "create_write_prerender", @@ -1027,7 +1035,7 @@ def convert_to_valid_instaces(): "source": "create_source" } - return mapping[family] + return mapping[product_type] from ayon_core.hosts.nuke.api import workio @@ -1086,7 +1094,10 @@ def convert_to_valid_instaces(): transfer_data["task"] = task_name - family = avalon_knob_data["family"] + product_type = avalon_knob_data.get("productType") + if product_type is None: + product_type = avalon_knob_data["family"] + # establish families families_ak = avalon_knob_data.get("families", []) @@ -1104,11 +1115,13 @@ def convert_to_valid_instaces(): node["publish"].value()) # add idetifier - transfer_data["creator_identifier"] = family_to_identifier(family) + transfer_data["creator_identifier"] = product_type_to_identifier( + product_type + ) # Add all nodes in group instances. if node.Class() == "Group": - # only alter families for render family + # only alter families for render product type if families_ak and "write" in families_ak.lower(): target = node["render"].value() if target == "Use existing frames": diff --git a/client/ayon_core/hosts/nuke/plugins/create/create_backdrop.py b/client/ayon_core/hosts/nuke/plugins/create/create_backdrop.py index 530392c635..cefd9501ec 100644 --- a/client/ayon_core/hosts/nuke/plugins/create/create_backdrop.py +++ b/client/ayon_core/hosts/nuke/plugins/create/create_backdrop.py @@ -12,7 +12,7 @@ class CreateBackdrop(NukeCreator): identifier = "create_backdrop" label = "Nukenodes (backdrop)" - family = "nukenodes" + product_type = "nukenodes" icon = "file-archive-o" maintain_selection = True @@ -38,12 +38,12 @@ class CreateBackdrop(NukeCreator): return created_node - def create(self, subset_name, instance_data, pre_create_data): - # make sure subset name is unique - self.check_existing_subset(subset_name) + def create(self, product_name, instance_data, pre_create_data): + # make sure product name is unique + self.check_existing_product(product_name) instance = super(CreateBackdrop, self).create( - subset_name, + product_name, instance_data, pre_create_data ) diff --git a/client/ayon_core/hosts/nuke/plugins/create/create_camera.py b/client/ayon_core/hosts/nuke/plugins/create/create_camera.py index 7ade19d846..764de84dcf 100644 --- a/client/ayon_core/hosts/nuke/plugins/create/create_camera.py +++ b/client/ayon_core/hosts/nuke/plugins/create/create_camera.py @@ -14,7 +14,7 @@ class CreateCamera(NukeCreator): identifier = "create_camera" label = "Camera (3d)" - family = "camera" + product_type = "camera" icon = "camera" # plugin attributes @@ -44,12 +44,12 @@ class CreateCamera(NukeCreator): return created_node - def create(self, subset_name, instance_data, pre_create_data): - # make sure subset name is unique - self.check_existing_subset(subset_name) + def create(self, product_name, instance_data, pre_create_data): + # make sure product name is unique + self.check_existing_product(product_name) instance = super(CreateCamera, self).create( - subset_name, + product_name, instance_data, pre_create_data ) diff --git a/client/ayon_core/hosts/nuke/plugins/create/create_gizmo.py b/client/ayon_core/hosts/nuke/plugins/create/create_gizmo.py index 51c5b1931b..ccc6aa13bd 100644 --- a/client/ayon_core/hosts/nuke/plugins/create/create_gizmo.py +++ b/client/ayon_core/hosts/nuke/plugins/create/create_gizmo.py @@ -11,7 +11,7 @@ class CreateGizmo(NukeCreator): identifier = "create_gizmo" label = "Gizmo (group)" - family = "gizmo" + product_type = "gizmo" icon = "file-archive-o" default_variants = ["ViewerInput", "Lut", "Effect"] @@ -42,12 +42,12 @@ class CreateGizmo(NukeCreator): return created_node - def create(self, subset_name, instance_data, pre_create_data): - # make sure subset name is unique - self.check_existing_subset(subset_name) + def create(self, product_name, instance_data, pre_create_data): + # make sure product name is unique + self.check_existing_product(product_name) instance = super(CreateGizmo, self).create( - subset_name, + product_name, instance_data, pre_create_data ) diff --git a/client/ayon_core/hosts/nuke/plugins/create/create_model.py b/client/ayon_core/hosts/nuke/plugins/create/create_model.py index db927171cd..507b7a1b57 100644 --- a/client/ayon_core/hosts/nuke/plugins/create/create_model.py +++ b/client/ayon_core/hosts/nuke/plugins/create/create_model.py @@ -11,7 +11,7 @@ class CreateModel(NukeCreator): identifier = "create_model" label = "Model (3d)" - family = "model" + product_type = "model" icon = "cube" default_variants = ["Main"] @@ -42,12 +42,12 @@ class CreateModel(NukeCreator): return created_node - def create(self, subset_name, instance_data, pre_create_data): - # make sure subset name is unique - self.check_existing_subset(subset_name) + def create(self, product_name, instance_data, pre_create_data): + # make sure product name is unique + self.check_existing_product(product_name) instance = super(CreateModel, self).create( - subset_name, + product_name, instance_data, pre_create_data ) diff --git a/client/ayon_core/hosts/nuke/plugins/create/create_source.py b/client/ayon_core/hosts/nuke/plugins/create/create_source.py index be9fa44929..ac6b8f694b 100644 --- a/client/ayon_core/hosts/nuke/plugins/create/create_source.py +++ b/client/ayon_core/hosts/nuke/plugins/create/create_source.py @@ -17,7 +17,7 @@ class CreateSource(NukeCreator): identifier = "create_source" label = "Source (read)" - family = "source" + product_type = "source" icon = "film" default_variants = ["Effect", "Backplate", "Fire", "Smoke"] @@ -35,7 +35,7 @@ class CreateSource(NukeCreator): return read_node - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): # make sure selected nodes are added self.set_selected_nodes(pre_create_data) @@ -46,18 +46,18 @@ class CreateSource(NukeCreator): continue node_name = read_node.name() - _subset_name = subset_name + node_name + _product_name = product_name + node_name - # make sure subset name is unique - self.check_existing_subset(_subset_name) + # make sure product name is unique + self.check_existing_product(_product_name) instance_node = self.create_instance_node( - _subset_name, + _product_name, read_node ) instance = CreatedInstance( - self.family, - _subset_name, + self.product_type, + _product_name, instance_data, self ) diff --git a/client/ayon_core/hosts/nuke/plugins/create/create_write_image.py b/client/ayon_core/hosts/nuke/plugins/create/create_write_image.py index 125cf057f8..770726e34f 100644 --- a/client/ayon_core/hosts/nuke/plugins/create/create_write_image.py +++ b/client/ayon_core/hosts/nuke/plugins/create/create_write_image.py @@ -18,7 +18,7 @@ from ayon_core.hosts.nuke.api.plugin import exposed_write_knobs class CreateWriteImage(napi.NukeWriteCreator): identifier = "create_write_image" label = "Image (write)" - family = "image" + product_type = "image" icon = "sign-out" instance_attributes = [ @@ -64,18 +64,18 @@ class CreateWriteImage(napi.NukeWriteCreator): default=nuke.frame() ) - def create_instance_node(self, subset_name, instance_data): + def create_instance_node(self, product_name, instance_data): # add fpath_template write_data = { "creator": self.__class__.__name__, - "subset": subset_name, + "productName": product_name, "fpath_template": self.temp_rendering_path_template } write_data.update(instance_data) created_node = napi.create_write_node( - subset_name, + product_name, write_data, input=self.selected_node, prenodes=self.prenodes, @@ -91,8 +91,8 @@ class CreateWriteImage(napi.NukeWriteCreator): return created_node - def create(self, subset_name, instance_data, pre_create_data): - subset_name = subset_name.format(**pre_create_data) + def create(self, product_name, instance_data, pre_create_data): + product_name = product_name.format(**pre_create_data) # pass values from precreate to instance self.pass_pre_attributes_to_instance( @@ -107,18 +107,18 @@ class CreateWriteImage(napi.NukeWriteCreator): # make sure selected nodes are added self.set_selected_nodes(pre_create_data) - # make sure subset name is unique - self.check_existing_subset(subset_name) + # make sure product name is unique + self.check_existing_product(product_name) instance_node = self.create_instance_node( - subset_name, + product_name, instance_data, ) try: instance = CreatedInstance( - self.family, - subset_name, + self.product_type, + product_name, instance_data, self ) diff --git a/client/ayon_core/hosts/nuke/plugins/create/create_write_prerender.py b/client/ayon_core/hosts/nuke/plugins/create/create_write_prerender.py index 371ef85a15..96ac2fac9c 100644 --- a/client/ayon_core/hosts/nuke/plugins/create/create_write_prerender.py +++ b/client/ayon_core/hosts/nuke/plugins/create/create_write_prerender.py @@ -15,7 +15,7 @@ from ayon_core.hosts.nuke.api.plugin import exposed_write_knobs class CreateWritePrerender(napi.NukeWriteCreator): identifier = "create_write_prerender" label = "Prerender (write)" - family = "prerender" + product_type = "prerender" icon = "sign-out" instance_attributes = [ @@ -45,11 +45,11 @@ class CreateWritePrerender(napi.NukeWriteCreator): ] return attr_defs - def create_instance_node(self, subset_name, instance_data): + def create_instance_node(self, product_name, instance_data): # add fpath_template write_data = { "creator": self.__class__.__name__, - "subset": subset_name, + "productName": product_name, "fpath_template": self.temp_rendering_path_template } @@ -64,7 +64,7 @@ class CreateWritePrerender(napi.NukeWriteCreator): width, height = (actual_format.width(), actual_format.height()) created_node = napi.create_write_node( - subset_name, + product_name, write_data, input=self.selected_node, prenodes=self.prenodes, @@ -81,7 +81,7 @@ class CreateWritePrerender(napi.NukeWriteCreator): return created_node - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): # pass values from precreate to instance self.pass_pre_attributes_to_instance( instance_data, @@ -94,18 +94,18 @@ class CreateWritePrerender(napi.NukeWriteCreator): # make sure selected nodes are added self.set_selected_nodes(pre_create_data) - # make sure subset name is unique - self.check_existing_subset(subset_name) + # make sure product name is unique + self.check_existing_product(product_name) instance_node = self.create_instance_node( - subset_name, + product_name, instance_data ) try: instance = CreatedInstance( - self.family, - subset_name, + self.product_type, + product_name, instance_data, self ) diff --git a/client/ayon_core/hosts/nuke/plugins/create/create_write_render.py b/client/ayon_core/hosts/nuke/plugins/create/create_write_render.py index c5f4d5003a..24bddb3d26 100644 --- a/client/ayon_core/hosts/nuke/plugins/create/create_write_render.py +++ b/client/ayon_core/hosts/nuke/plugins/create/create_write_render.py @@ -15,7 +15,7 @@ from ayon_core.hosts.nuke.api.plugin import exposed_write_knobs class CreateWriteRender(napi.NukeWriteCreator): identifier = "create_write_render" label = "Render (write)" - family = "render" + product_type = "render" icon = "sign-out" instance_attributes = [ @@ -39,11 +39,11 @@ class CreateWriteRender(napi.NukeWriteCreator): ] return attr_defs - def create_instance_node(self, subset_name, instance_data): + def create_instance_node(self, product_name, instance_data): # add fpath_template write_data = { "creator": self.__class__.__name__, - "subset": subset_name, + "productName": product_name, "fpath_template": self.temp_rendering_path_template } @@ -61,7 +61,7 @@ class CreateWriteRender(napi.NukeWriteCreator): self.log.debug(">>>>>>> : {}".format(self.get_linked_knobs())) created_node = napi.create_write_node( - subset_name, + product_name, write_data, input=self.selected_node, prenodes=self.prenodes, @@ -76,7 +76,7 @@ class CreateWriteRender(napi.NukeWriteCreator): return created_node - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): # pass values from precreate to instance self.pass_pre_attributes_to_instance( instance_data, @@ -88,18 +88,18 @@ class CreateWriteRender(napi.NukeWriteCreator): # make sure selected nodes are added self.set_selected_nodes(pre_create_data) - # make sure subset name is unique - self.check_existing_subset(subset_name) + # make sure product name is unique + self.check_existing_product(product_name) instance_node = self.create_instance_node( - subset_name, + product_name, instance_data ) try: instance = CreatedInstance( - self.family, - subset_name, + self.product_type, + product_name, instance_data, self ) diff --git a/client/ayon_core/hosts/nuke/plugins/create/workfile_creator.py b/client/ayon_core/hosts/nuke/plugins/create/workfile_creator.py index 87f62b011e..bf376559b3 100644 --- a/client/ayon_core/hosts/nuke/plugins/create/workfile_creator.py +++ b/client/ayon_core/hosts/nuke/plugins/create/workfile_creator.py @@ -13,7 +13,7 @@ import nuke class WorkfileCreator(AutoCreator): identifier = "workfile" - family = "workfile" + product_type = "workfile" default_variant = "Main" @@ -32,7 +32,7 @@ class WorkfileCreator(AutoCreator): host_name = self.create_context.host_name asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( self.default_variant, task_name, asset_doc, project_name, host_name ) @@ -47,7 +47,7 @@ class WorkfileCreator(AutoCreator): )) instance = CreatedInstance( - self.family, subset_name, instance_data, self + self.product_type, product_name, instance_data, self ) instance.transient_data["node"] = root_node self._add_instance_to_context(instance) diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_gizmo.py b/client/ayon_core/hosts/nuke/plugins/publish/collect_gizmo.py index c410de7c32..fda1c7ac31 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/collect_gizmo.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/collect_gizmo.py @@ -15,8 +15,8 @@ class CollectGizmo(pyblish.api.InstancePlugin): gizmo_node = instance.data["transientData"]["node"] - # add family to familiess - instance.data["families"].insert(0, instance.data["family"]) + # add product type to familiess + instance.data["families"].insert(0, instance.data["productType"]) # make label nicer instance.data["label"] = gizmo_node.name() @@ -25,6 +25,7 @@ class CollectGizmo(pyblish.api.InstancePlugin): handle_end = instance.context.data["handleEnd"] first_frame = int(nuke.root()["first_frame"].getValue()) last_frame = int(nuke.root()["last_frame"].getValue()) + families = [instance.data["productType"]] + instance.data["families"] # Add version data to instance version_data = { @@ -33,8 +34,8 @@ class CollectGizmo(pyblish.api.InstancePlugin): "frameStart": first_frame + handle_start, "frameEnd": last_frame - handle_end, "colorspace": nuke.root().knob('workingSpaceLUT').value(), - "families": [instance.data["family"]] + instance.data["families"], - "subset": instance.data["subset"], + "families": families, + "productName": instance.data["productName"], "fps": instance.context.data["fps"] } diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_model.py b/client/ayon_core/hosts/nuke/plugins/publish/collect_model.py index a099f06be0..1a2bc9c019 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/collect_model.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/collect_model.py @@ -15,8 +15,8 @@ class CollectModel(pyblish.api.InstancePlugin): geo_node = instance.data["transientData"]["node"] - # add family to familiess - instance.data["families"].insert(0, instance.data["family"]) + # add product type to familiess + instance.data["families"].insert(0, instance.data["productType"]) # make label nicer instance.data["label"] = geo_node.name() @@ -25,7 +25,7 @@ class CollectModel(pyblish.api.InstancePlugin): handle_end = instance.context.data["handleEnd"] first_frame = int(nuke.root()["first_frame"].getValue()) last_frame = int(nuke.root()["last_frame"].getValue()) - + families = [instance.data["productType"]] + instance.data["families"] # Add version data to instance version_data = { "handleStart": handle_start, @@ -33,8 +33,8 @@ class CollectModel(pyblish.api.InstancePlugin): "frameStart": first_frame + handle_start, "frameEnd": last_frame - handle_end, "colorspace": nuke.root().knob('workingSpaceLUT').value(), - "families": [instance.data["family"]] + instance.data["families"], - "subset": instance.data["subset"], + "families": families, + "productName": instance.data["productName"], "fps": instance.context.data["fps"] } diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_nuke_instance_data.py b/client/ayon_core/hosts/nuke/plugins/publish/collect_nuke_instance_data.py index 75380bf409..951072ff3f 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/collect_nuke_instance_data.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/collect_nuke_instance_data.py @@ -12,10 +12,10 @@ class CollectInstanceData(pyblish.api.InstancePlugin): hosts = ["nuke", "nukeassist"] # presets - sync_workfile_version_on_product_types = [] + sync_workfile_version_on_families = [] def process(self, instance): - family = instance.data["family"] + product_type = instance.data["productType"] # Get format root = nuke.root() @@ -25,10 +25,10 @@ class CollectInstanceData(pyblish.api.InstancePlugin): pixel_aspect = format_.pixelAspect() # sync workfile version - if family in self.sync_workfile_version_on_product_types: + if product_type in self.sync_workfile_version_on_families: self.log.debug( "Syncing version with workfile for '{}'".format( - family + product_type ) ) # get version to instance for integration diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_reads.py b/client/ayon_core/hosts/nuke/plugins/publish/collect_reads.py index 38938a3dda..af17933eb1 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/collect_reads.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/collect_reads.py @@ -99,8 +99,8 @@ class CollectNukeReads(pyblish.api.InstancePlugin): "frameStart": first_frame + handle_start, "frameEnd": last_frame - handle_end, "colorspace": colorspace, - "families": [instance.data["family"]], - "subset": instance.data["subset"], + "families": [instance.data["productType"]], + "productName": instance.data["productName"], "fps": instance.context.data["fps"] } diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_writes.py b/client/ayon_core/hosts/nuke/plugins/publish/collect_writes.py index 84dc7992a5..58afb2cd1f 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/collect_writes.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/collect_writes.py @@ -138,14 +138,14 @@ class CollectNukeWrites(pyblish.api.InstancePlugin, render_target (str): render target colorspace (str): colorspace """ - family = instance.data["family"] + product_type = instance.data["productType"] # add targeted family to families instance.data["families"].append( - "{}.{}".format(family, render_target) + "{}.{}".format(product_type, render_target) ) self.log.debug("Appending render target to families: {}.{}".format( - family, render_target) + product_type, render_target) ) write_node = self._write_node_helper(instance) @@ -175,7 +175,7 @@ class CollectNukeWrites(pyblish.api.InstancePlugin, "colorspace": colorspace }) - if family == "render": + if product_type == "render": instance.data.update({ "handleStart": handle_start, "handleEnd": handle_end, diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_camera.py b/client/ayon_core/hosts/nuke/plugins/publish/extract_camera.py index 810a2e0a76..1f5a8c73e1 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/extract_camera.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/extract_camera.py @@ -38,7 +38,7 @@ class ExtractCamera(publish.Extractor): rm_nodes = [] self.log.debug("Creating additional nodes for 3D Camera Extractor") - subset = instance.data["subset"] + product_name = instance.data["productName"] staging_dir = self.staging_dir(instance) # get extension form preset @@ -50,7 +50,7 @@ class ExtractCamera(publish.Extractor): "Talk to your supervisor or pipeline admin") # create file name and path - filename = subset + ".{}".format(extension) + filename = product_name + ".{}".format(extension) file_path = os.path.join(staging_dir, filename).replace("\\", "/") with maintained_selection(): diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_model.py b/client/ayon_core/hosts/nuke/plugins/publish/extract_model.py index 6f35e95630..36896fe595 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/extract_model.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/extract_model.py @@ -40,7 +40,7 @@ class ExtractModel(publish.Extractor): model_node = instance.data["transientData"]["node"] self.log.debug("Creating additional nodes for Extract Model") - subset = instance.data["subset"] + product_name = instance.data["productName"] staging_dir = self.staging_dir(instance) extension = next((k[1] for k in self.write_geo_knobs @@ -51,7 +51,7 @@ class ExtractModel(publish.Extractor): "Talk to your supervisor or pipeline admin") # create file name and path - filename = subset + ".{}".format(extension) + filename = product_name + ".{}".format(extension) file_path = os.path.join(staging_dir, filename).replace("\\", "/") with maintained_selection(): diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_render_local.py b/client/ayon_core/hosts/nuke/plugins/publish/extract_render_local.py index 45514ede5e..25b9f67fba 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/extract_render_local.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/extract_render_local.py @@ -38,7 +38,7 @@ class NukeRenderLocal(publish.Extractor, self.log.debug("instance collected: {}".format(instance.data)) - node_subset_name = instance.data.get("name", None) + node_product_name = instance.data.get("name", None) first_frame = instance.data.get("frameStartHandle", None) last_frame = instance.data.get("frameEndHandle", None) @@ -80,7 +80,7 @@ class NukeRenderLocal(publish.Extractor, # Render frames nuke.execute( - str(node_subset_name), + str(node_product_name), int(render_first_frame), int(render_last_frame) ) @@ -125,21 +125,25 @@ class NukeRenderLocal(publish.Extractor, )) families = instance.data["families"] + anatomy_data = instance.data["anatomyData"] # redefinition of families if "render.local" in families: - instance.data['family'] = 'render' - families.remove('render.local') + instance.data["productType"] = "render" + families.remove("render.local") families.insert(0, "render2d") - instance.data["anatomyData"]["family"] = "render" + anatomy_data["family"] = "render" + anatomy_data["product"]["type"] = "render" elif "prerender.local" in families: - instance.data['family'] = 'prerender' - families.remove('prerender.local') + instance.data["productType"] = "prerender" + families.remove("prerender.local") families.insert(0, "prerender") - instance.data["anatomyData"]["family"] = "prerender" + anatomy_data["family"] = "prerender" + anatomy_data["product"]["type"] = "prerender" elif "image.local" in families: - instance.data['family'] = 'image' - families.remove('image.local') - instance.data["anatomyData"]["family"] = "image" + instance.data["productType"] = "image" + families.remove("image.local") + anatomy_data["family"] = "image" + anatomy_data["product"]["type"] = "image" instance.data["families"] = families collections, remainder = clique.assemble(filenames) @@ -160,7 +164,7 @@ class NukeRenderLocal(publish.Extractor, These are base of files which will be extended/fixed for specific frames. Renames published file to expected file name based on frame, eg. - test_project_test_asset_subset_v005.1001.exr > new_render.1001.exr + test_project_test_asset_product_v005.1001.exr > new_render.1001.exr """ last_published = instance.data["last_version_published_files"] last_published_and_frames = collect_frames(last_published) diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py b/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py index 1f4410d347..daf12b9b84 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py @@ -54,7 +54,7 @@ class ExtractReviewIntermediates(publish.Extractor): families = set(instance.data["families"]) # add main family to make sure all families are compared - families.add(instance.data["family"]) + families.add(instance.data["productType"]) task_type = instance.context.data["taskType"] subset = instance.data["subset"] diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_slate_frame.py b/client/ayon_core/hosts/nuke/plugins/publish/extract_slate_frame.py index 6918e4f50f..c013da84d2 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/extract_slate_frame.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/extract_slate_frame.py @@ -115,7 +115,7 @@ class ExtractSlateFrame(publish.Extractor): """Slate frame renderer Args: - instance (PyblishInstance): Pyblish instance with subset data + instance (PyblishInstance): Pyblish instance with product data output_name (str, optional): Slate variation name. Defaults to None. bake_viewer_process (bool, optional): diff --git a/client/ayon_core/hosts/nuke/plugins/publish/validate_exposed_knobs.py b/client/ayon_core/hosts/nuke/plugins/publish/validate_exposed_knobs.py index c047347481..217fe6fb85 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/validate_exposed_knobs.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/validate_exposed_knobs.py @@ -29,7 +29,8 @@ class RepairExposedKnobs(pyblish.api.Action): if x.Class() == "Write": write_node = x - plugin_name = plugin.families_mapping[instance.data["family"]] + product_type = instance.data["productType"] + plugin_name = plugin.product_types_mapping[product_type] nuke_settings = instance.context.data["project_settings"]["nuke"] create_settings = nuke_settings["create"][plugin_name] exposed_knobs = create_settings["exposed_knobs"] @@ -51,7 +52,7 @@ class ValidateExposedKnobs( label = "Validate Exposed Knobs" actions = [RepairExposedKnobs] hosts = ["nuke"] - families_mapping = { + product_types_mapping = { "render": "CreateWriteRender", "prerender": "CreateWritePrerender", "image": "CreateWriteImage" @@ -61,7 +62,8 @@ class ValidateExposedKnobs( if not self.is_active(instance.data): return - plugin = self.families_mapping[instance.data["family"]] + product_type = instance.data["productType"] + plugin = self.product_types_mapping[product_type] group_node = instance.data["transientData"]["node"] nuke_settings = instance.context.data["project_settings"]["nuke"] create_settings = nuke_settings["create"][plugin] diff --git a/client/ayon_core/hosts/nuke/plugins/publish/validate_knobs.py b/client/ayon_core/hosts/nuke/plugins/publish/validate_knobs.py index bede67dabe..281e172788 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/validate_knobs.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/validate_knobs.py @@ -67,7 +67,7 @@ class ValidateKnobs(pyblish.api.ContextPlugin): settings_knobs = json.loads(cls.knobs) # Filter families. - families = [instance.data["family"]] + families = [instance.data["productType"]] families += instance.data.get("families", []) # Get all knobs to validate. diff --git a/client/ayon_core/hosts/nuke/startup/custom_write_node.py b/client/ayon_core/hosts/nuke/startup/custom_write_node.py index 5eb58a3679..6573ccfbde 100644 --- a/client/ayon_core/hosts/nuke/startup/custom_write_node.py +++ b/client/ayon_core/hosts/nuke/startup/custom_write_node.py @@ -111,9 +111,14 @@ class WriteNodeKnobSettingPanel(nukescripts.PythonPanel): ) for write_node in write_selected_nodes: # data for mapping the path + # TODO add more fill data + product_name = write_node["name"].value() data = { "work": os.getenv("AYON_WORKDIR"), - "subset": write_node["name"].value(), + "subset": product_name, + "product": { + "name": product_name, + }, "frame": "#" * frame_padding, "ext": ext } From b7732dbddc5b07bf207b3708d9f77fe74c31d894 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 11:42:12 +0100 Subject: [PATCH 259/573] resolve is using product name and type --- client/ayon_core/hosts/resolve/api/lib.py | 4 +- .../ayon_core/hosts/resolve/api/pipeline.py | 4 +- client/ayon_core/hosts/resolve/api/plugin.py | 60 ++++++++++--------- .../plugins/create/create_shot_clip.py | 18 +++--- .../hosts/resolve/plugins/load/load_clip.py | 2 +- .../plugins/publish/precollect_instances.py | 20 +++---- .../plugins/publish/precollect_workfile.py | 10 ++-- 7 files changed, 61 insertions(+), 57 deletions(-) diff --git a/client/ayon_core/hosts/resolve/api/lib.py b/client/ayon_core/hosts/resolve/api/lib.py index 5eb88afdcb..6e4e17811f 100644 --- a/client/ayon_core/hosts/resolve/api/lib.py +++ b/client/ayon_core/hosts/resolve/api/lib.py @@ -520,8 +520,8 @@ def imprint(timeline_item, data=None): Examples: data = { 'folderPath': 'sq020sh0280', - 'family': 'render', - 'subset': 'subsetMain' + 'productType': 'render', + 'productName': 'productMain' } """ data = data or {} diff --git a/client/ayon_core/hosts/resolve/api/pipeline.py b/client/ayon_core/hosts/resolve/api/pipeline.py index 2c5e0daf4b..19d33971dc 100644 --- a/client/ayon_core/hosts/resolve/api/pipeline.py +++ b/client/ayon_core/hosts/resolve/api/pipeline.py @@ -296,8 +296,8 @@ def list_instances(): if tag_data: asset = tag_data.get("asset") - subset = tag_data.get("subset") - tag_data["label"] = f"{ti_name} [{asset}-{subset}]" + product_name = tag_data.get("productName") + tag_data["label"] = f"{ti_name} [{asset}-{product_name}]" listed_instances.append(tag_data) return listed_instances diff --git a/client/ayon_core/hosts/resolve/api/plugin.py b/client/ayon_core/hosts/resolve/api/plugin.py index ccb20f712f..607bf58afd 100644 --- a/client/ayon_core/hosts/resolve/api/plugin.py +++ b/client/ayon_core/hosts/resolve/api/plugin.py @@ -350,7 +350,7 @@ class ClipLoader: """ Gets context and convert it to self.data data structure: { - "name": "assetName_subsetName_representationName" + "name": "assetName_productName_representationName" "binPath": "projectBinPath", } """ @@ -358,17 +358,17 @@ class ClipLoader: representation = self.context["representation"] representation_context = representation["context"] asset = str(representation_context["asset"]) - subset = str(representation_context["subset"]) + product_name = str(representation_context["subset"]) representation_name = str(representation_context["representation"]) self.data["clip_name"] = "_".join([ asset, - subset, + product_name, representation_name ]) self.data["versionData"] = self.context["version"]["data"] self.data["timeline_basename"] = "timeline_{}_{}".format( - subset, representation_name) + product_name, representation_name) # solve project bin structure path hierarchy = str("/".join(( @@ -603,9 +603,9 @@ class PublishClip: rename_default = False hierarchy_default = "{_folder_}/{_sequence_}/{_track_}" clip_name_default = "shot_{_trackIndex_:0>3}_{_clipIndex_:0>4}" - subset_name_default = "" + base_product_name_default = "" review_track_default = "< none >" - subset_family_default = "plate" + product_type_default = "plate" count_from_default = 10 count_steps_default = 10 vertical_sync_default = False @@ -728,10 +728,10 @@ class PublishClip: "countFrom", {}).get("value") or self.count_from_default self.count_steps = self.ui_inputs.get( "countSteps", {}).get("value") or self.count_steps_default - self.subset_name = self.ui_inputs.get( - "subsetName", {}).get("value") or self.subset_name_default - self.subset_family = self.ui_inputs.get( - "subsetFamily", {}).get("value") or self.subset_family_default + self.base_product_name = self.ui_inputs.get( + "productName", {}).get("value") or self.base_product_name_default + self.product_type = self.ui_inputs.get( + "productType", {}).get("value") or self.product_type_default self.vertical_sync = self.ui_inputs.get( "vSyncOn", {}).get("value") or self.vertical_sync_default self.driving_layer = self.ui_inputs.get( @@ -739,12 +739,14 @@ class PublishClip: self.review_track = self.ui_inputs.get( "reviewTrack", {}).get("value") or self.review_track_default - # build subset name from layer name - if self.subset_name == "": - self.subset_name = self.track_name + # build product name from layer name + if self.base_product_name == "": + self.base_product_name = self.track_name - # create subset for publishing - self.subset = self.subset_family + self.subset_name.capitalize() + # create product name for publishing + self.product_name = ( + self.product_type + self.base_product_name.capitalize() + ) def _replace_hash_to_expression(self, name, text): """ Replace hash with number in correct padding. """ @@ -824,17 +826,19 @@ class PublishClip: # driving layer is set as negative match for (_in, _out), hero_data in self.vertical_clip_match.items(): hero_data.update({"heroTrack": False}) - if _in == self.clip_in and _out == self.clip_out: - data_subset = hero_data["subset"] - # add track index in case duplicity of names in hero data - if self.subset in data_subset: - hero_data["subset"] = self.subset + str( - self.track_index) - # in case track name and subset name is the same then add - if self.subset_name == self.track_name: - hero_data["subset"] = self.subset - # assign data to return hierarchy data to tag - tag_hierarchy_data = hero_data + if _in != self.clip_in or _out != self.clip_out: + continue + + data_product_name = hero_data["productName"] + # add track index in case duplicity of names in hero data + if self.product_name in data_product_name: + hero_data["productName"] = self.product_name + str( + self.track_index) + # in case track name and product name is the same then add + if self.base_product_name == self.track_name: + hero_data["productName"] = self.product_name + # assign data to return hierarchy data to tag + tag_hierarchy_data = hero_data # add data to return data dict self.tag_data.update(tag_hierarchy_data) @@ -859,8 +863,8 @@ class PublishClip: "hierarchy": hierarchy_filled, "parents": self.parents, "hierarchyData": hierarchy_formatting_data, - "subset": self.subset, - "family": self.subset_family + "productName": self.product_name, + "productType": self.product_type } def _convert_to_entity(self, key): diff --git a/client/ayon_core/hosts/resolve/plugins/create/create_shot_clip.py b/client/ayon_core/hosts/resolve/plugins/create/create_shot_clip.py index 65bc9fed9d..3a2a0345ea 100644 --- a/client/ayon_core/hosts/resolve/plugins/create/create_shot_clip.py +++ b/client/ayon_core/hosts/resolve/plugins/create/create_shot_clip.py @@ -10,12 +10,12 @@ class CreateShotClip(plugin.Creator): """Publishable clip""" label = "Create Publishable Clip" - family = "clip" + product_type = "clip" icon = "film" defaults = ["Main"] gui_tracks = get_video_track_names() - gui_name = "OpenPype publish attributes creator" + gui_name = "AYON publish attributes creator" gui_info = "Define sequential rename and fill hierarchy data." gui_inputs = { "renameHierarchy": { @@ -133,19 +133,19 @@ class CreateShotClip(plugin.Creator): "target": "ui", "order": 3, "value": { - "subsetName": { + "productName": { "value": ["", "main", "bg", "fg", "bg", "animatic"], "type": "QComboBox", - "label": "Subset Name", + "label": "Product Name", "target": "ui", - "toolTip": "chose subset name pattern, if is selected, name of track layer will be used", # noqa + "toolTip": "chose product name pattern, if is selected, name of track layer will be used", # noqa "order": 0}, - "subsetFamily": { + "productType": { "value": ["plate", "take"], "type": "QComboBox", - "label": "Subset Family", - "target": "ui", "toolTip": "What use of this subset is for", # noqa + "label": "Product type", + "target": "ui", "toolTip": "What use of this product is for", # noqa "order": 1}, "reviewTrack": { "value": ["< none >"] + gui_tracks, @@ -159,7 +159,7 @@ class CreateShotClip(plugin.Creator): "type": "QCheckBox", "label": "Include audio", "target": "tag", - "toolTip": "Process subsets with corresponding audio", # noqa + "toolTip": "Process products with corresponding audio", # noqa "order": 3}, "sourceResolution": { "value": False, diff --git a/client/ayon_core/hosts/resolve/plugins/load/load_clip.py b/client/ayon_core/hosts/resolve/plugins/load/load_clip.py index 47aeac213b..4d1f8f1f7c 100644 --- a/client/ayon_core/hosts/resolve/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/resolve/plugins/load/load_clip.py @@ -15,7 +15,7 @@ from ayon_core.lib.transcoding import ( class LoadClip(plugin.TimelineItemLoader): - """Load a subset to timeline as clip + """Load a product to timeline as clip Place clip to timeline on its asset origin timings collected during conforming to project diff --git a/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py b/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py index 78c5fbbbf7..025b64878e 100644 --- a/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py +++ b/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py @@ -64,11 +64,11 @@ class PrecollectInstances(pyblish.api.ContextPlugin): }) asset = tag_data["folder_path"] - subset = tag_data["subset"] + product_name = tag_data["productName"] data.update({ - "name": "{}_{}".format(asset, subset), - "label": "{} {}".format(asset, subset), + "name": "{}_{}".format(asset, product_name), + "label": "{} {}".format(asset, product_name), "folderPath": asset, "item": timeline_item, "publish": get_publish_attribute(timeline_item), @@ -128,18 +128,18 @@ class PrecollectInstances(pyblish.api.ContextPlugin): return asset = data["folderPath"] - subset = "shotMain" + product_name = "shotMain" # insert family into families - family = "shot" + product_type = "shot" data.update({ - "name": "{}_{}".format(asset, subset), - "label": "{} {}".format(asset, subset), - "subset": subset, + "name": "{}_{}".format(asset, product_name), + "label": "{} {}".format(asset, product_name), "folderPath": asset, - "family": family, - "families": [], + "productName": product_name, + "productType": product_type, + "families": [product_type], "publish": get_publish_attribute(timeline_item) }) diff --git a/client/ayon_core/hosts/resolve/plugins/publish/precollect_workfile.py b/client/ayon_core/hosts/resolve/plugins/publish/precollect_workfile.py index 814b1e159f..4526b812a0 100644 --- a/client/ayon_core/hosts/resolve/plugins/publish/precollect_workfile.py +++ b/client/ayon_core/hosts/resolve/plugins/publish/precollect_workfile.py @@ -17,7 +17,7 @@ class PrecollectWorkfile(pyblish.api.ContextPlugin): current_asset_name = get_current_asset_name() asset_name = current_asset_name.split("/")[-1] - subset = "workfileMain" + product_name = "workfileMain" project = rapi.get_current_project() fps = project.GetSetting("timelineFrameRate") video_tracks = rapi.get_video_track_names() @@ -26,12 +26,12 @@ class PrecollectWorkfile(pyblish.api.ContextPlugin): otio_timeline = davinci_export.create_otio_timeline(project) instance_data = { - "name": "{}_{}".format(asset_name, subset), - "label": "{} {}".format(current_asset_name, subset), + "name": "{}_{}".format(asset_name, product_name), + "label": "{} {}".format(current_asset_name, product_name), "folderPath": current_asset_name, - "subset": subset, + "productName": product_name, "item": project, - "family": "workfile", + "productType": "workfile", "families": [] } From adf5318c9abc45ffcc505b108c58631ad9e8112a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 11:43:04 +0100 Subject: [PATCH 260/573] add product_type to json in layout extraction --- client/ayon_core/hosts/unreal/plugins/publish/extract_layout.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/hosts/unreal/plugins/publish/extract_layout.py b/client/ayon_core/hosts/unreal/plugins/publish/extract_layout.py index a508f79f18..de8cf0be2a 100644 --- a/client/ayon_core/hosts/unreal/plugins/publish/extract_layout.py +++ b/client/ayon_core/hosts/unreal/plugins/publish/extract_layout.py @@ -68,6 +68,7 @@ class ExtractLayout(publish.Extractor): json_element = {} json_element["reference"] = str(blend_id) json_element["family"] = family + json_element["product_type"] = family json_element["instance_name"] = actor.get_name() json_element["asset_name"] = mesh.get_name() import_data = mesh.get_editor_property("asset_import_data") From b21d75896de544b585d4a4bc4ec51e2ed9d9de3d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 12:24:08 +0100 Subject: [PATCH 261/573] hiero is using product name and type --- client/ayon_core/hosts/hiero/api/lib.py | 4 +- client/ayon_core/hosts/hiero/api/plugin.py | 48 +++++++------- client/ayon_core/hosts/hiero/api/tags.py | 18 +++--- .../hiero/plugins/create/create_shot_clip.py | 16 ++--- .../hosts/hiero/plugins/load/load_clip.py | 2 +- .../plugins/publish/collect_clip_effects.py | 32 +++++----- .../publish/collect_frame_tag_instances.py | 64 ++++++++++--------- .../plugins/publish/collect_tag_tasks.py | 8 ++- .../plugins/publish/extract_clip_effects.py | 14 ++-- .../plugins/publish/precollect_instances.py | 41 ++++++------ .../plugins/publish/precollect_workfile.py | 16 +++-- .../collect_assetbuilds.py | 10 +-- .../vendor/google/protobuf/descriptor.py | 2 +- 13 files changed, 145 insertions(+), 130 deletions(-) diff --git a/client/ayon_core/hosts/hiero/api/lib.py b/client/ayon_core/hosts/hiero/api/lib.py index d74ff13f05..ef0f9edca9 100644 --- a/client/ayon_core/hosts/hiero/api/lib.py +++ b/client/ayon_core/hosts/hiero/api/lib.py @@ -589,8 +589,8 @@ def imprint(track_item, data=None): Examples: data = { 'asset': 'sq020sh0280', - 'family': 'render', - 'subset': 'subsetMain' + 'productType': 'render', + 'productName': 'productMain' } """ data = data or {} diff --git a/client/ayon_core/hosts/hiero/api/plugin.py b/client/ayon_core/hosts/hiero/api/plugin.py index 574865488f..8f1be97680 100644 --- a/client/ayon_core/hosts/hiero/api/plugin.py +++ b/client/ayon_core/hosts/hiero/api/plugin.py @@ -439,7 +439,7 @@ class ClipLoader: """ Gets context and convert it to self.data data structure: { - "name": "assetName_subsetName_representationName" + "name": "assetName_productName_representationName" "path": "path/to/file/created/by/get_repr..", "binPath": "projectBinPath", } @@ -448,10 +448,10 @@ class ClipLoader: repr = self.context["representation"] repr_cntx = repr["context"] asset = str(repr_cntx["asset"]) - subset = str(repr_cntx["subset"]) + product_name = str(repr_cntx["subset"]) representation = str(repr_cntx["representation"]) self.data["clip_name"] = self.clip_name_template.format(**repr_cntx) - self.data["track_name"] = "_".join([subset, representation]) + self.data["track_name"] = "_".join([product_name, representation]) self.data["versionData"] = self.context["version"]["data"] # gets file path file = get_representation_path_from_context(self.context) @@ -659,9 +659,9 @@ class PublishClip: rename_default = False hierarchy_default = "{_folder_}/{_sequence_}/{_track_}" clip_name_default = "shot_{_trackIndex_:0>3}_{_clipIndex_:0>4}" - subset_name_default = "" + base_product_name_default = "" review_track_default = "< none >" - subset_family_default = "plate" + product_type_default = "plate" count_from_default = 10 count_steps_default = 10 vertical_sync_default = False @@ -785,10 +785,10 @@ class PublishClip: "countFrom", {}).get("value") or self.count_from_default self.count_steps = self.ui_inputs.get( "countSteps", {}).get("value") or self.count_steps_default - self.subset_name = self.ui_inputs.get( - "subsetName", {}).get("value") or self.subset_name_default - self.subset_family = self.ui_inputs.get( - "subsetFamily", {}).get("value") or self.subset_family_default + self.base_product_name = self.ui_inputs.get( + "productName", {}).get("value") or self.base_product_name_default + self.product_type = self.ui_inputs.get( + "productType", {}).get("value") or self.product_type_default self.vertical_sync = self.ui_inputs.get( "vSyncOn", {}).get("value") or self.vertical_sync_default self.driving_layer = self.ui_inputs.get( @@ -798,12 +798,14 @@ class PublishClip: self.audio = self.ui_inputs.get( "audio", {}).get("value") or False - # build subset name from layer name - if self.subset_name == "": - self.subset_name = self.track_name + # build product name from layer name + if self.base_product_name == "": + self.base_product_name = self.track_name - # create subset for publishing - self.subset = self.subset_family + self.subset_name.capitalize() + # create product for publishing + self.product_name = ( + self.product_type + self.base_product_name.capitalize() + ) def _replace_hash_to_expression(self, name, text): """ Replace hash with number in correct padding. """ @@ -885,14 +887,14 @@ class PublishClip: for (_in, _out), hero_data in self.vertical_clip_match.items(): hero_data.update({"heroTrack": False}) if _in == self.clip_in and _out == self.clip_out: - data_subset = hero_data["subset"] + data_product_name = hero_data["productName"] # add track index in case duplicity of names in hero data - if self.subset in data_subset: - hero_data["subset"] = self.subset + str( + if self.product_name in data_product_name: + hero_data["productName"] = self.product_name + str( self.track_index) - # in case track name and subset name is the same then add - if self.subset_name == self.track_name: - hero_data["subset"] = self.subset + # in case track name and product name is the same then add + if self.base_product_name == self.track_name: + hero_data["productName"] = self.product_name # assign data to return hierarchy data to tag tag_hierarchy_data = hero_data @@ -913,9 +915,9 @@ class PublishClip: "hierarchy": hierarchy_filled, "parents": self.parents, "hierarchyData": hierarchy_formatting_data, - "subset": self.subset, - "family": self.subset_family, - "families": [self.data["family"]] + "productName": self.product_name, + "productType": self.product_type, + "families": [self.product_type, self.data["family"]] } def _convert_to_entity(self, type, template): diff --git a/client/ayon_core/hosts/hiero/api/tags.py b/client/ayon_core/hosts/hiero/api/tags.py index ad7c7e44a1..6491b1f384 100644 --- a/client/ayon_core/hosts/hiero/api/tags.py +++ b/client/ayon_core/hosts/hiero/api/tags.py @@ -28,8 +28,8 @@ def tag_data(): # "note": "Collecting track items to Nuke scripts.", # "icon": "icons:TagNuke.png", # "metadata": { - # "family": "nukescript", - # "subset": "main" + # "productType": "nukescript", + # "productName": "main" # } # }, "Comment": { @@ -37,17 +37,17 @@ def tag_data(): "note": "Comment on a shot.", "icon": "icons:TagComment.png", "metadata": { - "family": "comment", - "subset": "main" + "productType": "comment", + "productName": "main" } }, "FrameMain": { "editable": "1", - "note": "Publishing a frame subset.", + "note": "Publishing a frame product.", "icon": "z_layer_main.png", "metadata": { - "family": "frame", - "subset": "main", + "productType": "frame", + "productName": "main", "format": "png" } } @@ -153,7 +153,7 @@ def add_tags_to_workfile(): "note": task_type, "icon": "icons:TagGood.png", "metadata": { - "family": "task", + "productType": "task", "type": task_type } } @@ -173,7 +173,7 @@ def add_tags_to_workfile(): "path": "icons:TagActor.png" }, "metadata": { - "family": "assetbuild" + "productType": "assetbuild" } } diff --git a/client/ayon_core/hosts/hiero/plugins/create/create_shot_clip.py b/client/ayon_core/hosts/hiero/plugins/create/create_shot_clip.py index ce84a9120e..90ea9ef50f 100644 --- a/client/ayon_core/hosts/hiero/plugins/create/create_shot_clip.py +++ b/client/ayon_core/hosts/hiero/plugins/create/create_shot_clip.py @@ -10,7 +10,7 @@ class CreateShotClip(phiero.Creator): """Publishable clip""" label = "Create Publishable Clip" - family = "clip" + product_type = "clip" icon = "film" defaults = ["Main"] @@ -133,19 +133,19 @@ class CreateShotClip(phiero.Creator): "target": "ui", "order": 3, "value": { - "subsetName": { + "productName": { "value": ["", "main", "bg", "fg", "bg", "animatic"], "type": "QComboBox", - "label": "Subset Name", + "label": "pRODUCT Name", "target": "ui", - "toolTip": "chose subset name pattern, if is selected, name of track layer will be used", # noqa + "toolTip": "chose product name pattern, if is selected, name of track layer will be used", # noqa "order": 0}, - "subsetFamily": { + "productType": { "value": ["plate", "take"], "type": "QComboBox", - "label": "Subset Family", - "target": "ui", "toolTip": "What use of this subset is for", # noqa + "label": "Product Type", + "target": "ui", "toolTip": "What use of this product is for", # noqa "order": 1}, "reviewTrack": { "value": ["< none >"] + gui_tracks, @@ -159,7 +159,7 @@ class CreateShotClip(phiero.Creator): "type": "QCheckBox", "label": "Include audio", "target": "tag", - "toolTip": "Process subsets with corresponding audio", # noqa + "toolTip": "Process productS with corresponding audio", # noqa "order": 3}, "sourceResolution": { "value": False, diff --git a/client/ayon_core/hosts/hiero/plugins/load/load_clip.py b/client/ayon_core/hosts/hiero/plugins/load/load_clip.py index b8c51e7536..016ece31f4 100644 --- a/client/ayon_core/hosts/hiero/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/hiero/plugins/load/load_clip.py @@ -14,7 +14,7 @@ import ayon_core.hosts.hiero.api as phiero class LoadClip(phiero.SequenceLoader): - """Load a subset to timeline as clip + """Load a product to timeline as clip Place clip to timeline on its asset origin timings collected during conforming to project diff --git a/client/ayon_core/hosts/hiero/plugins/publish/collect_clip_effects.py b/client/ayon_core/hosts/hiero/plugins/publish/collect_clip_effects.py index 3bd5b88942..1107bb19f6 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/collect_clip_effects.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/collect_clip_effects.py @@ -12,13 +12,13 @@ class CollectClipEffects(pyblish.api.InstancePlugin): effect_categories = [] def process(self, instance): - family = "effect" + product_type = "effect" effects = {} review = instance.data.get("review") review_track_index = instance.context.data.get("reviewTrackIndex") item = instance.data["item"] - if "audio" in instance.data["family"]: + if "audio" in instance.data["productType"]: return # frame range @@ -61,16 +61,16 @@ class CollectClipEffects(pyblish.api.InstancePlugin): if not effects: return - subset = instance.data.get("subset") - effects.update({"assignTo": subset}) + product_name = instance.data.get("productName") + effects.update({"assignTo": product_name}) - subset_split = re.findall(r'[A-Z][^A-Z]*', subset) + product_name_split = re.findall(r'[A-Z][^A-Z]*', product_name) - if len(subset_split) > 0: - root_name = subset.replace(subset_split[0], "") - subset_split.insert(0, root_name.capitalize()) + if len(product_name_split) > 0: + root_name = product_name.replace(product_name_split[0], "") + product_name_split.insert(0, root_name.capitalize()) - subset_split.insert(0, "effect") + product_name_split.insert(0, "effect") effect_categories = { x["name"]: x["effect_classes"] for x in self.effect_categories @@ -104,8 +104,8 @@ class CollectClipEffects(pyblish.api.InstancePlugin): effects_categorized[category]["assignTo"] = effects["assignTo"] for category, effects in effects_categorized.items(): - name = "".join(subset_split) - name += category.capitalize() + product_name = "".join(product_name_split) + product_name += category.capitalize() # create new instance and inherit data data = {} @@ -115,12 +115,12 @@ class CollectClipEffects(pyblish.api.InstancePlugin): data[key] = value # change names - data["subset"] = name - data["family"] = family - data["families"] = [family] - data["name"] = data["subset"] + "_" + data["folderPath"] + data["productName"] = product_name + data["productType"] = product_type + data["families"] = [product_type] + data["name"] = product_name + "_" + data["folderPath"] data["label"] = "{} - {}".format( - data["folderPath"], data["subset"] + data["folderPath"], product_name ) data["effects"] = effects diff --git a/client/ayon_core/hosts/hiero/plugins/publish/collect_frame_tag_instances.py b/client/ayon_core/hosts/hiero/plugins/publish/collect_frame_tag_instances.py index 6f99e6be29..0cdb3a5478 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/collect_frame_tag_instances.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/collect_frame_tag_instances.py @@ -13,8 +13,8 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin): Tag is expected to have metadata: { - "family": "frame" - "subset": "main" + "productType": "frame" + "productName": "main" } """ @@ -26,14 +26,14 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin): self._context = context # collect all sequence tags - subset_data = self._create_frame_subset_data_sequence(context) + product_data = self._create_frame_product_data_sequence(context) - self.log.debug("__ subset_data: {}".format( - pformat(subset_data) + self.log.debug("__ product_data: {}".format( + pformat(product_data) )) # create instances - self._create_instances(subset_data) + self._create_instances(product_data) def _get_tag_data(self, tag): data = {} @@ -66,7 +66,7 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin): return data - def _create_frame_subset_data_sequence(self, context): + def _create_frame_product_data_sequence(self, context): sequence_tags = [] sequence = context.data["activeTimeline"] @@ -87,10 +87,13 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin): if not tag_data: continue - if "family" not in tag_data: + product_type = tag_data.get("productType") + if product_type is None: + product_type = tag_data.get("family") + if not product_type: continue - if tag_data["family"] != "frame": + if product_type != "frame": continue sequence_tags.append(tag_data) @@ -99,8 +102,8 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin): pformat(sequence_tags) )) - # first collect all available subset tag frames - subset_data = {} + # first collect all available product tag frames + product_data = {} context_asset_doc = context.data["assetEntity"] context_folder_path = get_asset_name_identifier(context_asset_doc) @@ -110,33 +113,36 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin): if frame not in publish_frames: continue - subset = tag_data["subset"] + product_name = tag_data.get("productName") + if product_name is None: + product_name = tag_data["subset"] - if subset in subset_data: - # update existing subset key - subset_data[subset]["frames"].append(frame) + if product_name in product_data: + # update existing product key + product_data[product_name]["frames"].append(frame) else: - # create new subset key - subset_data[subset] = { + # create new product key + product_data[product_name] = { "frames": [frame], "format": tag_data["format"], "folderPath": context_folder_path } - return subset_data + return product_data - def _create_instances(self, subset_data): - # create instance per subset - for subset_name, subset_data in subset_data.items(): - name = "frame" + subset_name.title() + def _create_instances(self, product_data): + # create instance per product + product_type = "image" + for product_name, product_data in product_data.items(): + name = "frame" + product_name.title() data = { "name": name, - "label": "{} {}".format(name, subset_data["frames"]), - "family": "image", - "families": ["frame"], - "folderPath": subset_data["folderPath"], - "subset": name, - "format": subset_data["format"], - "frames": subset_data["frames"] + "label": "{} {}".format(name, product_data["frames"]), + "productType": product_type, + "families": [product_type, "frame"], + "folderPath": product_data["folderPath"], + "productName": name, + "format": product_data["format"], + "frames": product_data["frames"] } self._context.create_instance(**data) diff --git a/client/ayon_core/hosts/hiero/plugins/publish/collect_tag_tasks.py b/client/ayon_core/hosts/hiero/plugins/publish/collect_tag_tasks.py index 27968060e1..35a8c02cc0 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/collect_tag_tasks.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/collect_tag_tasks.py @@ -16,10 +16,12 @@ class CollectClipTagTasks(api.InstancePlugin): tasks = {} for tag in tags: t_metadata = dict(tag.metadata()) - t_family = t_metadata.get("tag.family", "") + t_product_type = t_metadata.get("tag.productType") + if t_product_type is None: + t_product_type = t_metadata.get("tag.family", "") - # gets only task family tags and collect labels - if "task" in t_family: + # gets only task product type tags and collect labels + if "task" in t_product_type: t_task_name = t_metadata.get("tag.label", "") t_task_type = t_metadata.get("tag.type", "") tasks[t_task_name] = {"type": t_task_type} diff --git a/client/ayon_core/hosts/hiero/plugins/publish/extract_clip_effects.py b/client/ayon_core/hosts/hiero/plugins/publish/extract_clip_effects.py index d1edfed0d7..25b52968fa 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/extract_clip_effects.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/extract_clip_effects.py @@ -21,8 +21,8 @@ class ExtractClipEffects(publish.Extractor): if not effects: return - subset = instance.data.get("subset") - family = instance.data["family"] + product_name = instance.data.get("productName") + product_type = instance.data["productType"] self.log.debug("creating staging dir") staging_dir = self.staging_dir(instance) @@ -32,7 +32,7 @@ class ExtractClipEffects(publish.Extractor): instance.data["transfers"] = list() ext = "json" - file = subset + "." + ext + file = product_name + "." + ext # when instance is created during collection part resources_dir = instance.data["resourcesDir"] @@ -68,8 +68,10 @@ class ExtractClipEffects(publish.Extractor): version_data.update({ "colorspace": item.sourceMediaColourTransform(), "colorspaceScript": instance.context.data["colorspace"], - "families": [family, "plate"], - "subset": subset, + "families": [product_type, "plate"], + # TODO find out if 'subset' is needed (and 'productName') + "subset": product_name, + "productName": product_name, "fps": instance.context.data["fps"] }) instance.data["versionData"] = version_data @@ -77,7 +79,7 @@ class ExtractClipEffects(publish.Extractor): representation = { 'files': file, 'stagingDir': staging_dir, - 'name': family + ext.title(), + 'name': product_type + ext.title(), 'ext': ext } instance.data["representations"].append(representation) diff --git a/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py b/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py index e6381af939..0b905064b3 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py @@ -87,19 +87,20 @@ class PrecollectInstances(pyblish.api.ContextPlugin): asset, asset_name = self._get_asset_data(tag_data) - subset = tag_data["subset"] + product_name = tag_data.get("productName") + if product_name is None: + product_name = tag_data["subset"] - # insert family into families families = [str(f) for f in tag_data["families"]] # form label label = "{} -".format(asset) if asset_name != clip_name: label += " ({})".format(clip_name) - label += " {}".format(subset) + label += " {}".format(product_name) data.update({ - "name": "{}_{}".format(asset, subset), + "name": "{}_{}".format(asset, product_name), "label": label, "folderPath": asset, "asset_name": asset_name, @@ -146,7 +147,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin): if not with_audio: continue - # create audio subset instance + # create audio product instance self.create_audio_instance(context, **data) # add audioReview attribute to plate instance data @@ -180,7 +181,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin): }) def create_shot_instance(self, context, **data): - subset = "shotMain" + product_name = "shotMain" master_layer = data.get("heroTrack") hierarchy_data = data.get("hierarchyData") item = data.get("item") @@ -195,21 +196,20 @@ class PrecollectInstances(pyblish.api.ContextPlugin): asset = data["folderPath"] asset_name = data["asset_name"] - # insert family into families - family = "shot" + product_type = "shot" # form label label = "{} -".format(asset) if asset_name != clip_name: label += " ({}) ".format(clip_name) - label += " {}".format(subset) + label += " {}".format(product_name) data.update({ - "name": "{}_{}".format(asset, subset), + "name": "{}_{}".format(asset, product_name), "label": label, - "subset": subset, - "family": family, - "families": [] + "productName": product_name, + "productType": product_type, + "families": [product_type] }) instance = context.create_instance(**data) @@ -238,7 +238,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin): return folder_path, asset_name def create_audio_instance(self, context, **data): - subset = "audioMain" + product_name = "audioMain" master_layer = data.get("heroTrack") if not master_layer: @@ -254,21 +254,20 @@ class PrecollectInstances(pyblish.api.ContextPlugin): asset = data["folderPath"] asset_name = data["asset_name"] - # insert family into families - family = "audio" + product_type = "audio" # form label label = "{} -".format(asset) if asset_name != clip_name: label += " ({}) ".format(clip_name) - label += " {}".format(subset) + label += " {}".format(product_name) data.update({ - "name": "{}_{}".format(asset, subset), + "name": "{}_{}".format(asset, product_name), "label": label, - "subset": subset, - "family": family, - "families": ["clip"] + "productName": product_name, + "productType": product_type, + "families": [product_type, "clip"] }) # remove review track attr if any data.pop("reviewTrack") diff --git a/client/ayon_core/hosts/hiero/plugins/publish/precollect_workfile.py b/client/ayon_core/hosts/hiero/plugins/publish/precollect_workfile.py index 15dd0dee26..e4cde297de 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/precollect_workfile.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/precollect_workfile.py @@ -59,17 +59,19 @@ class PrecollectWorkfile(pyblish.api.ContextPlugin): 'files': base_name, "stagingDir": staging_dir, } - family = "workfile" + product_type = "workfile" instance_data = { "label": "{} - {}Main".format( - asset, family), - "name": "{}_{}".format(asset_name, family), + asset, product_type), + "name": "{}_{}".format(asset_name, product_type), "folderPath": context.data["folderPath"], - # TODO use 'get_subset_name' - "subset": "{}{}Main".format(asset_name, family.capitalize()), + # TODO use 'get_product_name' + "productName": "{}{}Main".format( + asset_name, product_type.capitalize() + ), "item": project, - "family": family, - "families": [], + "productType": product_type, + "families": [product_type], "representations": [workfile_representation, thumb_representation] } diff --git a/client/ayon_core/hosts/hiero/plugins/publish_old_workflow/collect_assetbuilds.py b/client/ayon_core/hosts/hiero/plugins/publish_old_workflow/collect_assetbuilds.py index ca937f4aa2..96d471115a 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish_old_workflow/collect_assetbuilds.py +++ b/client/ayon_core/hosts/hiero/plugins/publish_old_workflow/collect_assetbuilds.py @@ -8,7 +8,7 @@ class CollectAssetBuilds(api.ContextPlugin): Tag is expected to have name of the asset and metadata: { - "family": "assetbuild" + "productType": "assetbuild" } """ @@ -29,7 +29,7 @@ class CollectAssetBuilds(api.ContextPlugin): asset_builds[asset_name] = asset_doc for instance in context: - if instance.data["family"] != "clip": + if instance.data["productType"] != "clip": continue # Exclude non-tagged instances. @@ -38,9 +38,11 @@ class CollectAssetBuilds(api.ContextPlugin): for tag in instance.data["tags"]: t_metadata = dict(tag.metadata()) - t_family = t_metadata.get("tag.family", "") + t_product_type = t_metadata.get("tag.productType") + if t_product_type is None: + t_product_type = t_metadata.get("tag.family", "") - if t_family.lower() == "assetbuild": + if t_product_type.lower() == "assetbuild": asset_names.append(tag["name"]) tagged = True diff --git a/client/ayon_core/hosts/hiero/vendor/google/protobuf/descriptor.py b/client/ayon_core/hosts/hiero/vendor/google/protobuf/descriptor.py index ad70be9a11..5d16ae2a0c 100644 --- a/client/ayon_core/hosts/hiero/vendor/google/protobuf/descriptor.py +++ b/client/ayon_core/hosts/hiero/vendor/google/protobuf/descriptor.py @@ -990,7 +990,7 @@ class FileDescriptor(DescriptorBase): :class:`descriptor_pb2.FileDescriptorProto`. dependencies (list[FileDescriptor]): List of other :class:`FileDescriptor` objects this :class:`FileDescriptor` depends on. - public_dependencies (list[FileDescriptor]): A subset of + public_dependencies (list[FileDescriptor]): A product of :attr:`dependencies`, which were declared as "public". message_types_by_name (dict(str, Descriptor)): Mapping from message names to their :class:`Descriptor`. From 8549588f2793366dc541f00ecaba21fdf140726d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 12:25:22 +0100 Subject: [PATCH 262/573] change constants and do smaller fixes --- .../aftereffects/plugins/create/create_render.py | 4 ++-- client/ayon_core/hosts/nuke/api/lib.py | 3 ++- .../plugins/publish/extract_review_intermediates.py | 8 ++++---- .../ayon_core/hosts/nuke/startup/custom_write_node.py | 1 + .../hosts/photoshop/plugins/create/create_image.py | 4 ++-- .../photoshop/plugins/publish/validate_naming.py | 4 ++-- client/ayon_core/pipeline/create/README.md | 2 +- client/ayon_core/pipeline/create/__init__.py | 11 ++++------- client/ayon_core/pipeline/create/constants.py | 8 ++++---- client/ayon_core/pipeline/create/creator_plugins.py | 4 ++-- client/ayon_core/pipeline/create/product_name.py | 10 +++++----- client/ayon_core/pipeline/load/plugins.py | 4 ++-- .../pipeline/workfile/workfile_template_builder.py | 10 +++++++--- 13 files changed, 38 insertions(+), 35 deletions(-) diff --git a/client/ayon_core/hosts/aftereffects/plugins/create/create_render.py b/client/ayon_core/hosts/aftereffects/plugins/create/create_render.py index a795d61b39..d7f7040953 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/create/create_render.py +++ b/client/ayon_core/hosts/aftereffects/plugins/create/create_render.py @@ -11,7 +11,7 @@ from ayon_core.pipeline import ( from ayon_core.hosts.aftereffects.api.pipeline import cache_and_get_instances from ayon_core.hosts.aftereffects.api.lib import set_settings from ayon_core.lib import prepare_template_data -from ayon_core.pipeline.create import SUBSET_NAME_ALLOWED_SYMBOLS +from ayon_core.pipeline.create import PRODUCT_NAME_ALLOWED_SYMBOLS class RenderCreator(Creator): @@ -58,7 +58,7 @@ class RenderCreator(Creator): len(comps) > 1) for comp in comps: composition_name = re.sub( - "[^{}]+".format(SUBSET_NAME_ALLOWED_SYMBOLS), + "[^{}]+".format(PRODUCT_NAME_ALLOWED_SYMBOLS), "", comp.name ) diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index ce6f66e3e0..426f93a187 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -445,7 +445,7 @@ def set_avalon_knob_data(node, data=None, prefix="avalon:"): create = OrderedDict() tab_name = NODE_TAB_NAME - editable = ["asset", "subset", "name", "namespace"] + editable = ["folderPath", "productName", "name", "namespace"] existed_knobs = node.knobs() @@ -714,6 +714,7 @@ def get_imageio_node_override_setting( if plugin_name not in onode["plugins"]: continue + # TODO change 'subsets' to 'product_names' in settings if ( onode["subsets"] and not any( diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py b/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py index daf12b9b84..d40513b7c8 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py @@ -57,7 +57,7 @@ class ExtractReviewIntermediates(publish.Extractor): families.add(instance.data["productType"]) task_type = instance.context.data["taskType"] - subset = instance.data["subset"] + product_name = instance.data["productName"] self.log.debug("Creating staging dir...") if "representations" not in instance.data: @@ -93,8 +93,8 @@ class ExtractReviewIntermediates(publish.Extractor): f_task_types, task_type)) self.log.debug( - "product_names `{}` > subset: {}".format( - product_names, subset)) + "product_names `{}` > product: {}".format( + product_names, product_name)) # test if family found in context # using intersection to make sure all defined @@ -111,7 +111,7 @@ class ExtractReviewIntermediates(publish.Extractor): # test subsets from filter if product_names and not any( - re.search(p, subset) for p in product_names + re.search(p, product_name) for p in product_names ): continue diff --git a/client/ayon_core/hosts/nuke/startup/custom_write_node.py b/client/ayon_core/hosts/nuke/startup/custom_write_node.py index 6573ccfbde..84e99f34c4 100644 --- a/client/ayon_core/hosts/nuke/startup/custom_write_node.py +++ b/client/ayon_core/hosts/nuke/startup/custom_write_node.py @@ -144,6 +144,7 @@ class WriteNodeKnobSettingPanel(nukescripts.PythonPanel): knobs_nodes = settings[i]["knobs"] for setting in settings: + # TODO change 'subsets' to 'product_names' in settings for subset in setting["subsets"]: preset_name.append(subset) diff --git a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py b/client/ayon_core/hosts/photoshop/plugins/create/create_image.py index 5da275e10f..b524245243 100644 --- a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/create/create_image.py @@ -8,7 +8,7 @@ from ayon_core.pipeline import ( CreatorError ) from ayon_core.lib import prepare_template_data -from ayon_core.pipeline.create import SUBSET_NAME_ALLOWED_SYMBOLS +from ayon_core.pipeline.create import PRODUCT_NAME_ALLOWED_SYMBOLS from ayon_core.hosts.photoshop.api.pipeline import cache_and_get_instances from ayon_core.hosts.photoshop.lib import clean_product_name @@ -80,7 +80,7 @@ class ImageCreator(Creator): if use_layer_name: layer_name = re.sub( - "[^{}]+".format(SUBSET_NAME_ALLOWED_SYMBOLS), + "[^{}]+".format(PRODUCT_NAME_ALLOWED_SYMBOLS), "", group.name ) diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/validate_naming.py b/client/ayon_core/hosts/photoshop/plugins/publish/validate_naming.py index af4e646a67..ce940c47ce 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/validate_naming.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/validate_naming.py @@ -3,7 +3,7 @@ import re import pyblish.api from ayon_core.hosts.photoshop import api as photoshop -from ayon_core.pipeline.create import SUBSET_NAME_ALLOWED_SYMBOLS +from ayon_core.pipeline.create import PRODUCT_NAME_ALLOWED_SYMBOLS from ayon_core.pipeline.publish import ( ValidateContentsOrder, PublishXmlValidationError, @@ -56,7 +56,7 @@ class ValidateNamingRepair(pyblish.api.Action): # format from Tool Creator product_name = re.sub( - "[^{}]+".format(SUBSET_NAME_ALLOWED_SYMBOLS), + "[^{}]+".format(PRODUCT_NAME_ALLOWED_SYMBOLS), "", product_name ) diff --git a/client/ayon_core/pipeline/create/README.md b/client/ayon_core/pipeline/create/README.md index 353f28e88b..bbfd1bfa0f 100644 --- a/client/ayon_core/pipeline/create/README.md +++ b/client/ayon_core/pipeline/create/README.md @@ -31,7 +31,7 @@ Product type tells how should be instance processed and product name what name w "productType": , # Mutable data - ## Subset name based on product name template - may change overtime (on context change) + ## Product name based on product name template - may change overtime (on context change) "productName": , ## Instance is active and will be published "active": True, diff --git a/client/ayon_core/pipeline/create/__init__.py b/client/ayon_core/pipeline/create/__init__.py index ca2c47f1ff..da9cafad5a 100644 --- a/client/ayon_core/pipeline/create/__init__.py +++ b/client/ayon_core/pipeline/create/__init__.py @@ -1,6 +1,6 @@ from .constants import ( - SUBSET_NAME_ALLOWED_SYMBOLS, - DEFAULT_SUBSET_TEMPLATE, + PRODUCT_NAME_ALLOWED_SYMBOLS, + DEFAULT_PRODUCT_TEMPLATE, PRE_CREATE_THUMBNAIL_KEY, DEFAULT_VARIANT_VALUE, ) @@ -48,17 +48,14 @@ from .legacy_create import ( __all__ = ( - "SUBSET_NAME_ALLOWED_SYMBOLS", - "DEFAULT_SUBSET_TEMPLATE", + "PRODUCT_NAME_ALLOWED_SYMBOLS", + "DEFAULT_PRODUCT_TEMPLATE", "PRE_CREATE_THUMBNAIL_KEY", "DEFAULT_VARIANT_VALUE", "get_last_versions_for_instances", "get_next_versions_for_instances", - "get_subset_name_template", - "get_subset_name", - "TaskNotSetError", "get_product_name", "get_product_name_template", diff --git a/client/ayon_core/pipeline/create/constants.py b/client/ayon_core/pipeline/create/constants.py index 7d1d0154e9..a0bcea55ff 100644 --- a/client/ayon_core/pipeline/create/constants.py +++ b/client/ayon_core/pipeline/create/constants.py @@ -1,12 +1,12 @@ -SUBSET_NAME_ALLOWED_SYMBOLS = "a-zA-Z0-9_." -DEFAULT_SUBSET_TEMPLATE = "{family}{Variant}" +PRODUCT_NAME_ALLOWED_SYMBOLS = "a-zA-Z0-9_." +DEFAULT_PRODUCT_TEMPLATE = "{family}{Variant}" PRE_CREATE_THUMBNAIL_KEY = "thumbnail_source" DEFAULT_VARIANT_VALUE = "Main" __all__ = ( - "SUBSET_NAME_ALLOWED_SYMBOLS", - "DEFAULT_SUBSET_TEMPLATE", + "PRODUCT_NAME_ALLOWED_SYMBOLS", + "DEFAULT_PRODUCT_TEMPLATE", "PRE_CREATE_THUMBNAIL_KEY", "DEFAULT_VARIANT_VALUE", ) diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index 21d4371dc3..acb1bb5c07 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -532,7 +532,7 @@ class BaseCreator: instance is passed in. Args: - variant(str): Subset name variant. In most of cases user input. + variant(str): Product name variant. In most of cases user input. task_name(str): For which task product is created. asset_doc(dict): Asset document for which product is created. project_name(str): Project name. @@ -687,7 +687,7 @@ class Creator(BaseCreator): Ideally should be stored to workfile using host implementation. Args: - product_name(str): Subset name of created instance. + product_name(str): Product name of created instance. instance_data(dict): Base data for instance. pre_create_data(dict): Data based on pre creation attributes. Those may affect how creator works. diff --git a/client/ayon_core/pipeline/create/product_name.py b/client/ayon_core/pipeline/create/product_name.py index 64a2760797..8413bfa9d8 100644 --- a/client/ayon_core/pipeline/create/product_name.py +++ b/client/ayon_core/pipeline/create/product_name.py @@ -3,7 +3,7 @@ import os from ayon_core.settings import get_project_settings from ayon_core.lib import filter_profiles, prepare_template_data -from .constants import DEFAULT_SUBSET_TEMPLATE +from .constants import DEFAULT_PRODUCT_TEMPLATE class TaskNotSetError(KeyError): @@ -40,7 +40,7 @@ def get_product_name_template( task_type (str): Type of task in which context the product is created. default_template (Union[str, None]): Default template which is used if settings won't find any matching possitibility. Constant - 'DEFAULT_SUBSET_TEMPLATE' is used if not defined. + 'DEFAULT_PRODUCT_TEMPLATE' is used if not defined. project_settings (Union[Dict[str, Any], None]): Prepared settings for project. Settings are queried if not passed. """ @@ -75,7 +75,7 @@ def get_product_name_template( # Make sure template is set (matching may have empty string) if not template: - template = default_template or DEFAULT_SUBSET_TEMPLATE + template = default_template or DEFAULT_PRODUCT_TEMPLATE return template @@ -96,7 +96,7 @@ def get_product_name( Subst name templates are defined in `project_settings/global/tools/creator /product_name_profiles` where are profiles with host name, product type, task name and task type filters. If context does not match any profile - then `DEFAULT_SUBSET_TEMPLATE` is used as default template. + then `DEFAULT_PRODUCT_TEMPLATE` is used as default template. That's main reason why so many arguments are required to calculate product name. @@ -114,7 +114,7 @@ def get_product_name( asset_doc (dict): Queried asset document with its tasks in data. Used to get task type. default_template (Optional[str]): Default template if any profile does - not match passed context. Constant 'DEFAULT_SUBSET_TEMPLATE' + not match passed context. Constant 'DEFAULT_PRODUCT_TEMPLATE' is used if is not passed. dynamic_data (Optional[Dict[str, Any]]): Dynamic data specific for a creator which creates instance. diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 962417c6f2..1005f4a2c0 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -225,7 +225,7 @@ class LoaderPlugin(list): Returns static (cls) options or could collect from 'contexts'. Args: - contexts (list): of repre or subset contexts + contexts (list): of repre or product contexts Returns: (list) """ @@ -246,7 +246,7 @@ class LoaderPlugin(list): class SubsetLoaderPlugin(LoaderPlugin): - """Load subset into host application + """Load product into host application Arguments: context (dict): avalon-core:context-1.0 name (str, optional): Use pre-defined name diff --git a/client/ayon_core/pipeline/workfile/workfile_template_builder.py b/client/ayon_core/pipeline/workfile/workfile_template_builder.py index 3004e55861..8da3961a3d 100644 --- a/client/ayon_core/pipeline/workfile/workfile_template_builder.py +++ b/client/ayon_core/pipeline/workfile/workfile_template_builder.py @@ -1299,6 +1299,10 @@ class PlaceholderLoadMixin(object): " used." ) + product_type = options.get("product_type") + if product_type is None: + product_type = options.get("family") + return [ attribute_definitions.UISeparatorDef(), attribute_definitions.UILabelDef("Main attributes"), @@ -1312,9 +1316,9 @@ class PlaceholderLoadMixin(object): tooltip=build_type_help ), attribute_definitions.EnumDef( - "family", - label="Family", - default=options.get("family"), + "product_type", + label="Product type", + default=product_type, items=families ), attribute_definitions.TextDef( From 2ac1acb19e8ef10fa5ca35b659f17920102f8f56 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 12:29:06 +0100 Subject: [PATCH 263/573] loader is using product name and type --- .../ayon_core/tools/loader/models/actions.py | 12 ++++---- .../tools/loader/models/site_sync.py | 12 ++++---- .../tools/loader/ui/folders_widget.py | 30 +++++++++---------- .../tools/loader/ui/products_widget.py | 2 +- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/client/ayon_core/tools/loader/models/actions.py b/client/ayon_core/tools/loader/models/actions.py index c70ccb3e18..dff15ea16c 100644 --- a/client/ayon_core/tools/loader/models/actions.py +++ b/client/ayon_core/tools/loader/models/actions.py @@ -809,10 +809,10 @@ class LoaderActionsModel: error_info = [] if loader.is_multiple_contexts_compatible: - subset_names = [] + product_names = [] for context in version_contexts: - subset_name = context.get("subset", {}).get("name") or "N/A" - subset_names.append(subset_name) + product_name = context.get("subset", {}).get("name") or "N/A" + product_names.append(product_name) try: load_with_subset_contexts( loader, @@ -831,12 +831,12 @@ class LoaderActionsModel: str(exc), formatted_traceback, None, - ", ".join(subset_names), + ", ".join(product_names), None )) else: for version_context in version_contexts: - subset_name = ( + product_name = ( version_context.get("subset", {}).get("name") or "N/A" ) try: @@ -860,7 +860,7 @@ class LoaderActionsModel: str(exc), formatted_traceback, None, - subset_name, + product_name, None )) diff --git a/client/ayon_core/tools/loader/models/site_sync.py b/client/ayon_core/tools/loader/models/site_sync.py index e6158ea280..2a6f1558ad 100644 --- a/client/ayon_core/tools/loader/models/site_sync.py +++ b/client/ayon_core/tools/loader/models/site_sync.py @@ -325,21 +325,21 @@ class SiteSyncModel: repre_docs = list(get_representations( project_name, representation_ids=representation_ids )) - families_per_repre_id = { + product_type_by_repre_id = { item["_id"]: item["context"]["family"] for item in repre_docs } for repre_id in representation_ids: - family = families_per_repre_id[repre_id] + product_type = product_type_by_repre_id[repre_id] if identifier == DOWNLOAD_IDENTIFIER: self._add_site( - project_name, repre_id, active_site, family + project_name, repre_id, active_site, product_type ) elif identifier == UPLOAD_IDENTIFIER: self._add_site( - project_name, repre_id, remote_site, family + project_name, repre_id, remote_site, product_type ) elif identifier == REMOVE_IDENTIFIER: @@ -485,13 +485,13 @@ class SiteSyncModel: representation_ids=representation_ids, ) - def _add_site(self, project_name, repre_id, site_name, family): + def _add_site(self, project_name, repre_id, site_name, product_type): self._site_sync_addon.add_site( project_name, repre_id, site_name, force=True ) # TODO this should happen in site sync addon - if family != "workfile": + if product_type != "workfile": return links = get_linked_representation_id( diff --git a/client/ayon_core/tools/loader/ui/folders_widget.py b/client/ayon_core/tools/loader/ui/folders_widget.py index 9d5b95b2a6..34881ab49d 100644 --- a/client/ayon_core/tools/loader/ui/folders_widget.py +++ b/client/ayon_core/tools/loader/ui/folders_widget.py @@ -56,34 +56,34 @@ class UnderlinesFolderDelegate(QtWidgets.QItemDelegate): item_rect = QtCore.QRect(option.rect) item_rect.setHeight(option.rect.height() - self.bar_height) - subset_colors = index.data(UNDERLINE_COLORS_ROLE) or [] + product_colors = index.data(UNDERLINE_COLORS_ROLE) or [] - subset_colors_width = 0 - if subset_colors: - subset_colors_width = option.rect.width() / len(subset_colors) + product_colors_width = 0 + if product_colors: + product_colors_width = option.rect.width() / len(product_colors) - subset_rects = [] + product_rects = [] counter = 0 - for subset_c in subset_colors: + for product_c in product_colors: new_color = None new_rect = None - if subset_c: - new_color = QtGui.QColor(subset_c) + if product_c: + new_color = QtGui.QColor(product_c) new_rect = QtCore.QRect( - option.rect.left() + (counter * subset_colors_width), + option.rect.left() + (counter * product_colors_width), option.rect.top() + ( option.rect.height() - self.bar_height ), - subset_colors_width, + product_colors_width, self.bar_height ) - subset_rects.append((new_color, new_rect)) + product_rects.append((new_color, new_rect)) counter += 1 # Background if option.state & QtWidgets.QStyle.State_Selected: - if len(subset_colors) == 0: + if len(product_colors) == 0: item_rect.setTop(item_rect.top() + (self.bar_height / 2)) if option.state & QtWidgets.QStyle.State_MouseOver: @@ -106,10 +106,10 @@ class UnderlinesFolderDelegate(QtWidgets.QItemDelegate): ) if option.state & QtWidgets.QStyle.State_Selected: - for color, subset_rect in subset_rects: - if not color or not subset_rect: + for color, product_rect in product_rects: + if not color or not product_rect: continue - painter.fillRect(subset_rect, QtGui.QBrush(color)) + painter.fillRect(product_rect, QtGui.QBrush(color)) # Icon icon_index = index.model().index( diff --git a/client/ayon_core/tools/loader/ui/products_widget.py b/client/ayon_core/tools/loader/ui/products_widget.py index 5a29f3f762..3025ec18bd 100644 --- a/client/ayon_core/tools/loader/ui/products_widget.py +++ b/client/ayon_core/tools/loader/ui/products_widget.py @@ -106,7 +106,7 @@ class ProductsWidget(QtWidgets.QWidget): products_view = DeselectableTreeView(self) # TODO - define custom object name in style - products_view.setObjectName("SubsetView") + products_view.setObjectName("ProductView") products_view.setSelectionMode( QtWidgets.QAbstractItemView.ExtendedSelection ) From 4974c492b073dad6094fd2190f6f1b825ab45874 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 12:35:58 +0100 Subject: [PATCH 264/573] push to project is using product name and type --- .../tools/push_to_project/control.py | 33 +++---- .../tools/push_to_project/models/integrate.py | 87 ++++++++++--------- .../push_to_project/models/user_values.py | 4 +- 3 files changed, 66 insertions(+), 58 deletions(-) diff --git a/client/ayon_core/tools/push_to_project/control.py b/client/ayon_core/tools/push_to_project/control.py index b3a5382343..3b6bd85028 100644 --- a/client/ayon_core/tools/push_to_project/control.py +++ b/client/ayon_core/tools/push_to_project/control.py @@ -256,12 +256,12 @@ class PushToContextController: project_settings = get_project_settings(project_name) subset_doc = self._src_subset_doc - family = subset_doc["data"].get("family") - if not family: - family = subset_doc["data"]["families"][0] + product_type = subset_doc["data"].get("family") + if not product_type: + product_type = subset_doc["data"]["families"][0] template = get_product_name_template( self._src_project_name, - family, + product_type, task_name, task_type, None, @@ -279,28 +279,31 @@ class PushToContextController: template_s = template[:idx] template_e = template[idx + len(variant_placeholder):] fill_data = prepare_template_data({ - "family": family, + "family": product_type, + "product": { + "type": product_type, + }, "task": task_name }) try: - subset_s = template_s.format(**fill_data) - subset_e = template_e.format(**fill_data) + product_s = template_s.format(**fill_data) + product_e = template_e.format(**fill_data) except Exception as exc: print("Failed format", exc) return "" - subset_name = self._src_subset_doc["name"] + product_name = self._src_subset_doc["name"] if ( - (subset_s and not subset_name.startswith(subset_s)) - or (subset_e and not subset_name.endswith(subset_e)) + (product_s and not product_name.startswith(product_s)) + or (product_e and not product_name.endswith(product_e)) ): return "" - if subset_s: - subset_name = subset_name[len(subset_s):] - if subset_e: - subset_name = subset_name[:len(subset_e)] - return subset_name + if product_s: + product_name = product_name[len(product_s):] + if product_e: + product_name = product_name[:len(product_e)] + return product_name def _check_submit_validations(self): if not self._user_values.is_valid: diff --git a/client/ayon_core/tools/push_to_project/models/integrate.py b/client/ayon_core/tools/push_to_project/models/integrate.py index 3de1beb2d2..b427f3d226 100644 --- a/client/ayon_core/tools/push_to_project/models/integrate.py +++ b/client/ayon_core/tools/push_to_project/models/integrate.py @@ -42,7 +42,7 @@ from ayon_core.pipeline import Anatomy from ayon_core.pipeline.version_start import get_versioning_start from ayon_core.pipeline.template_data import get_template_data from ayon_core.pipeline.publish import get_publish_template_name -from ayon_core.pipeline.create import get_subset_name +from ayon_core.pipeline.create import get_product_name UNKNOWN = object() @@ -461,8 +461,8 @@ class ProjectPushItemProcess: self._subset_doc = None self._version_doc = None - self._family = None - self._subset_name = None + self._product_type = None + self._product_name = None self._project_settings = None self._template_name = None @@ -494,9 +494,9 @@ class ProjectPushItemProcess: self._log_info("Destination project was found") self._fill_or_create_destination_asset() self._log_info("Destination asset was determined") - self._determine_family() + self._determine_product_type() self._determine_publish_template_name() - self._determine_subset_name() + self._determine_product_name() self._make_sure_subset_exists() self._make_sure_version_exists() self._log_info("Prerequirements were prepared") @@ -584,11 +584,11 @@ class ProjectPushItemProcess: )) raise PushToProjectError(self._status.fail_reason) - subset_id = version_doc["parent"] - subset_doc = get_subset_by_id(src_project_name, subset_id) + product_id = version_doc["parent"] + subset_doc = get_subset_by_id(src_project_name, product_id) if not subset_doc: self._status.set_failed(( - f"Could find subset with id \"{subset_id}\"" + f"Could find product with id \"{product_id}\"" f" in project \"{src_project_name}\"" )) raise PushToProjectError(self._status.fail_reason) @@ -791,29 +791,30 @@ class ProjectPushItemProcess: task_info.update(task_type_info) self._task_info = task_info - def _determine_family(self): + def _determine_product_type(self): subset_doc = self._src_subset_doc - family = subset_doc["data"].get("family") + product_type = subset_doc["data"].get("family") families = subset_doc["data"].get("families") - if not family and families: - family = families[0] + if not product_type and families: + product_type = families[0] - if not family: + if not product_type: self._status.set_failed( - "Couldn't figure out family from source subset" + "Couldn't figure out product type from source product" ) raise PushToProjectError(self._status.fail_reason) self._log_debug( - f"Publishing family is '{family}' (Based on source subset)" + f"Publishing product type is '{product_type}'" + f" (Based on source product)" ) - self._family = family + self._product_type = product_type def _determine_publish_template_name(self): template_name = get_publish_template_name( self._item.dst_project_name, self.host_name, - self._family, + self._product_type, self._task_info.get("name"), self._task_info.get("type"), project_settings=self._project_settings @@ -823,39 +824,39 @@ class ProjectPushItemProcess: ) self._template_name = template_name - def _determine_subset_name(self): - family = self._family + def _determine_product_name(self): + product_type = self._product_type asset_doc = self._asset_doc task_info = self._task_info - subset_name = get_subset_name( - family, - self._item.variant, - task_info.get("name"), + product_name = get_product_name( + self._item.dst_project_name, asset_doc, - project_name=self._item.dst_project_name, - host_name=self.host_name, + task_info.get("name"), + self.host_name, + product_type, + self._item.variant, project_settings=self._project_settings ) self._log_info( - f"Push will be integrating to subset with name '{subset_name}'" + f"Push will be integrating to product with name '{product_name}'" ) - self._subset_name = subset_name + self._product_name = product_name def _make_sure_subset_exists(self): project_name = self._item.dst_project_name asset_id = self._asset_doc["_id"] - subset_name = self._subset_name - family = self._family - subset_doc = get_subset_by_name(project_name, subset_name, asset_id) + product_name = self._product_name + product_type = self._product_type + subset_doc = get_subset_by_name(project_name, product_name, asset_id) if subset_doc: self._subset_doc = subset_doc return subset_doc data = { - "families": [family] + "families": [product_type] } subset_doc = new_subset_document( - subset_name, family, asset_id, data + product_name, product_type, asset_id, data ) self._operations.create_entity(project_name, "subset", subset_doc) self._subset_doc = subset_doc @@ -867,7 +868,7 @@ class ProjectPushItemProcess: version = self._item.dst_version src_version_doc = self._src_version_doc subset_doc = self._subset_doc - subset_id = subset_doc["_id"] + product_id = subset_doc["_id"] src_data = src_version_doc["data"] families = subset_doc["data"].get("families") if not families: @@ -884,7 +885,7 @@ class ProjectPushItemProcess: } if version is None: last_version_doc = get_last_version_by_subset_id( - project_name, subset_id + project_name, product_id ) if last_version_doc: version = int(last_version_doc["name"]) + 1 @@ -894,17 +895,17 @@ class ProjectPushItemProcess: self.host_name, task_name=self._task_info["name"], task_type=self._task_info["type"], - family=families[0], - subset=subset_doc["name"] + product_type=families[0], + product_name=subset_doc["name"] ) existing_version_doc = get_version_by_name( - project_name, version, subset_id + project_name, version, product_id ) # Update existing version if existing_version_doc: version_doc = new_version_doc( - version, subset_id, version_data, existing_version_doc["_id"] + version, product_id, version_data, existing_version_doc["_id"] ) update_data = prepare_version_update_data( existing_version_doc, version_doc @@ -921,7 +922,7 @@ class ProjectPushItemProcess: return version_doc = new_version_doc( - version, subset_id, version_data + version, product_id, version_data ) self._operations.create_entity(project_name, "version", version_doc) @@ -955,8 +956,12 @@ class ProjectPushItemProcess: self.host_name ) formatting_data.update({ - "subset": self._subset_name, - "family": self._family, + "subset": self._product_name, + "family": self._product_type, + "product": { + "name": self._product_name, + "type": self._product_type, + }, "version": version_doc["name"] }) diff --git a/client/ayon_core/tools/push_to_project/models/user_values.py b/client/ayon_core/tools/push_to_project/models/user_values.py index a12a1513ee..edef2fe4fb 100644 --- a/client/ayon_core/tools/push_to_project/models/user_values.py +++ b/client/ayon_core/tools/push_to_project/models/user_values.py @@ -1,6 +1,6 @@ import re -from ayon_core.pipeline.create import SUBSET_NAME_ALLOWED_SYMBOLS +from ayon_core.pipeline.create import PRODUCT_NAME_ALLOWED_SYMBOLS class UserPublishValuesModel: @@ -12,7 +12,7 @@ class UserPublishValuesModel: """ folder_name_regex = re.compile("^[a-zA-Z0-9_.]+$") - variant_regex = re.compile("^[{}]+$".format(SUBSET_NAME_ALLOWED_SYMBOLS)) + variant_regex = re.compile("^[{}]+$".format(PRODUCT_NAME_ALLOWED_SYMBOLS)) def __init__(self, controller): self._controller = controller From 1089f6e587b600d8ad8cb2ce29cede2346219815 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 12:37:53 +0100 Subject: [PATCH 265/573] creator tool is using product name and type --- client/ayon_core/tools/creator/constants.py | 2 +- client/ayon_core/tools/creator/model.py | 14 +- client/ayon_core/tools/creator/widgets.py | 75 +++++----- client/ayon_core/tools/creator/window.py | 149 ++++++++++---------- 4 files changed, 121 insertions(+), 119 deletions(-) diff --git a/client/ayon_core/tools/creator/constants.py b/client/ayon_core/tools/creator/constants.py index 5c4bbdcca3..ec555fbe9c 100644 --- a/client/ayon_core/tools/creator/constants.py +++ b/client/ayon_core/tools/creator/constants.py @@ -1,7 +1,7 @@ from qtpy import QtCore -FAMILY_ROLE = QtCore.Qt.UserRole + 1 +PRODUCT_TYPE_ROLE = QtCore.Qt.UserRole + 1 ITEM_ID_ROLE = QtCore.Qt.UserRole + 2 SEPARATOR = "---" diff --git a/client/ayon_core/tools/creator/model.py b/client/ayon_core/tools/creator/model.py index 3650993b9e..bf6c7380a1 100644 --- a/client/ayon_core/tools/creator/model.py +++ b/client/ayon_core/tools/creator/model.py @@ -4,7 +4,7 @@ from qtpy import QtGui, QtCore from ayon_core.pipeline import discover_legacy_creator_plugins from . constants import ( - FAMILY_ROLE, + PRODUCT_TYPE_ROLE, ITEM_ID_ROLE ) @@ -28,15 +28,15 @@ class CreatorsModel(QtGui.QStandardItemModel): item_id = str(uuid.uuid4()) self._creators_by_id[item_id] = creator - label = creator.label or creator.family + label = creator.label or creator.product_type item = QtGui.QStandardItem(label) item.setEditable(False) item.setData(item_id, ITEM_ID_ROLE) - item.setData(creator.family, FAMILY_ROLE) + item.setData(creator.product_type, PRODUCT_TYPE_ROLE) items.append(item) if not items: - item = QtGui.QStandardItem("No registered families") + item = QtGui.QStandardItem("No registered create plugins") item.setEnabled(False) item.setData(False, QtCore.Qt.ItemIsEnabled) items.append(item) @@ -47,15 +47,15 @@ class CreatorsModel(QtGui.QStandardItemModel): def get_creator_by_id(self, item_id): return self._creators_by_id.get(item_id) - def get_indexes_by_family(self, family): + def get_indexes_by_product_type(self, product_type): indexes = [] for row in range(self.rowCount()): index = self.index(row, 0) item_id = index.data(ITEM_ID_ROLE) creator_plugin = self._creators_by_id.get(item_id) if creator_plugin and ( - creator_plugin.label.lower() == family.lower() - or creator_plugin.family.lower() == family.lower() + creator_plugin.label.lower() == product_type.lower() + or creator_plugin.product_type.lower() == product_type.lower() ): indexes.append(index) return indexes diff --git a/client/ayon_core/tools/creator/widgets.py b/client/ayon_core/tools/creator/widgets.py index 05b5469151..53a2ee1080 100644 --- a/client/ayon_core/tools/creator/widgets.py +++ b/client/ayon_core/tools/creator/widgets.py @@ -5,7 +5,7 @@ from qtpy import QtWidgets, QtCore, QtGui import qtawesome -from ayon_core.pipeline.create import SUBSET_NAME_ALLOWED_SYMBOLS +from ayon_core.pipeline.create import PRODUCT_NAME_ALLOWED_SYMBOLS from ayon_core.tools.utils import ErrorMessageBox if hasattr(QtGui, "QRegularExpressionValidator"): @@ -19,16 +19,16 @@ else: class CreateErrorMessageBox(ErrorMessageBox): def __init__( self, - family, - subset_name, - asset_name, + product_type, + product_name, + folder_path, exc_msg, formatted_traceback, parent ): - self._family = family - self._subset_name = subset_name - self._asset_name = asset_name + self._product_type = product_type + self._product_name = product_name + self._folder_path = folder_path self._exc_msg = exc_msg self._formatted_traceback = formatted_traceback super(CreateErrorMessageBox, self).__init__("Creation failed", parent) @@ -42,14 +42,14 @@ class CreateErrorMessageBox(ErrorMessageBox): def _get_report_data(self): report_message = ( - "Failed to create Product: \"{subset}\"" - " Type: \"{family}\"" - " in Asset: \"{asset}\"" + "Failed to create Product: \"{product_name}\"" + " Type: \"{product_type}\"" + " in Folder: \"{folder_path}\"" "\n\nError: {message}" ).format( - subset=self._subset_name, - family=self._family, - asset=self._asset_name, + product_name=self._product_name, + product_type=self._product_type, + folder_path=self._folder_path, message=self._exc_msg ) if self._formatted_traceback: @@ -74,7 +74,7 @@ class CreateErrorMessageBox(ErrorMessageBox): item_name_widget = QtWidgets.QLabel(self) item_name_widget.setText( item_name_template.format( - self._family, self._subset_name, self._asset_name + self._product_type, self._product_name, self._folder_path ) ) content_layout.addWidget(item_name_widget) @@ -94,16 +94,16 @@ class CreateErrorMessageBox(ErrorMessageBox): content_layout.addWidget(tb_widget) -class SubsetNameValidator(RegularExpressionValidatorClass): +class ProductNameValidator(RegularExpressionValidatorClass): invalid = QtCore.Signal(set) - pattern = "^[{}]*$".format(SUBSET_NAME_ALLOWED_SYMBOLS) + pattern = "^[{}]*$".format(PRODUCT_NAME_ALLOWED_SYMBOLS) def __init__(self): reg = RegularExpressionClass(self.pattern) - super(SubsetNameValidator, self).__init__(reg) + super(ProductNameValidator, self).__init__(reg) def validate(self, text, pos): - results = super(SubsetNameValidator, self).validate(text, pos) + results = super(ProductNameValidator, self).validate(text, pos) if results[0] == self.Invalid: self.invalid.emit(self.invalid_chars(text)) return results @@ -131,7 +131,7 @@ class VariantLineEdit(QtWidgets.QLineEdit): def __init__(self, *args, **kwargs): super(VariantLineEdit, self).__init__(*args, **kwargs) - validator = SubsetNameValidator() + validator = ProductNameValidator() self.setValidator(validator) self.setToolTip("Only alphanumeric characters (A-Z a-z 0-9), " "'_' and '.' are allowed.") @@ -183,24 +183,24 @@ class VariantLineEdit(QtWidgets.QLineEdit): ) -class FamilyDescriptionWidget(QtWidgets.QWidget): - """A family description widget. +class ProductTypeDescriptionWidget(QtWidgets.QWidget): + """A product type description widget. - Shows a family icon, family name and a help description. + Shows a product type icon, name and a help description. Used in creator header. - _________________ - | ____ | - | |icon| FAMILY | - | |____| help | - |_________________| + _______________________ + | ____ | + | |icon| PRODUCT TYPE | + | |____| help | + |_______________________| """ SIZE = 35 def __init__(self, parent=None): - super(FamilyDescriptionWidget, self).__init__(parent=parent) + super(ProductTypeDescriptionWidget, self).__init__(parent=parent) icon_label = QtWidgets.QLabel(self) icon_label.setSizePolicy( @@ -215,14 +215,14 @@ class FamilyDescriptionWidget(QtWidgets.QWidget): label_layout = QtWidgets.QVBoxLayout() label_layout.setSpacing(0) - family_label = QtWidgets.QLabel(self) - family_label.setObjectName("CreatorFamilyLabel") - family_label.setAlignment(QtCore.Qt.AlignBottom | QtCore.Qt.AlignLeft) + product_type_label = QtWidgets.QLabel(self) + product_type_label.setObjectName("CreatorProductTypeLabel") + product_type_label.setAlignment(QtCore.Qt.AlignBottom | QtCore.Qt.AlignLeft) help_label = QtWidgets.QLabel(self) help_label.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft) - label_layout.addWidget(family_label) + label_layout.addWidget(product_type_label) label_layout.addWidget(help_label) layout = QtWidgets.QHBoxLayout(self) @@ -232,14 +232,15 @@ class FamilyDescriptionWidget(QtWidgets.QWidget): layout.addLayout(label_layout) self._help_label = help_label - self._family_label = family_label + self._product_type_label = product_type_label self._icon_label = icon_label def set_item(self, creator_plugin): - """Update elements to display information of a family item. + """Update elements to display information of a product type item. Args: - item (dict): A family item as registered with name, help and icon + creator_plugin (dict): A product type item as registered with + name, help and icon. Returns: None @@ -247,7 +248,7 @@ class FamilyDescriptionWidget(QtWidgets.QWidget): """ if not creator_plugin: self._icon_label.setPixmap(None) - self._family_label.setText("") + self._product_type_label.setText("") self._help_label.setText("") return @@ -268,5 +269,5 @@ class FamilyDescriptionWidget(QtWidgets.QWidget): creator_help = docstring.splitlines()[0] if docstring else "" self._icon_label.setPixmap(pixmap) - self._family_label.setText(creator_plugin.family) + self._product_type_label.setText(creator_plugin.product_type) self._help_label.setText(creator_help) diff --git a/client/ayon_core/tools/creator/window.py b/client/ayon_core/tools/creator/window.py index 5862725076..6194f019fc 100644 --- a/client/ayon_core/tools/creator/window.py +++ b/client/ayon_core/tools/creator/window.py @@ -14,7 +14,7 @@ from ayon_core.pipeline import ( get_current_task_name, ) from ayon_core.pipeline.create import ( - SUBSET_NAME_ALLOWED_SYMBOLS, + PRODUCT_NAME_ALLOWED_SYMBOLS, legacy_create, CreatorError, ) @@ -23,7 +23,7 @@ from .model import CreatorsModel from .widgets import ( CreateErrorMessageBox, VariantLineEdit, - FamilyDescriptionWidget + ProductTypeDescriptionWidget ) from .constants import ( ITEM_ID_ROLE, @@ -45,7 +45,7 @@ class CreatorWindow(QtWidgets.QDialog): self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint ) - creator_info = FamilyDescriptionWidget(self) + creator_info = ProductTypeDescriptionWidget(self) creators_model = CreatorsModel() @@ -56,19 +56,19 @@ class CreatorWindow(QtWidgets.QDialog): creators_view.setObjectName("CreatorsView") creators_view.setModel(creators_proxy) - asset_name_input = QtWidgets.QLineEdit(self) + folder_path_input = QtWidgets.QLineEdit(self) variant_input = VariantLineEdit(self) - subset_name_input = QtWidgets.QLineEdit(self) - subset_name_input.setEnabled(False) + product_name_input = QtWidgets.QLineEdit(self) + product_name_input.setEnabled(False) - subset_button = QtWidgets.QPushButton() - subset_button.setFixedWidth(18) - subset_menu = QtWidgets.QMenu(subset_button) - subset_button.setMenu(subset_menu) + variants_btn = QtWidgets.QPushButton() + variants_btn.setFixedWidth(18) + variants_menu = QtWidgets.QMenu(variants_btn) + variants_btn.setMenu(variants_menu) name_layout = QtWidgets.QHBoxLayout() name_layout.addWidget(variant_input) - name_layout.addWidget(subset_button) + name_layout.addWidget(variants_btn) name_layout.setSpacing(3) name_layout.setContentsMargins(0, 0, 0, 0) @@ -76,13 +76,13 @@ class CreatorWindow(QtWidgets.QDialog): body_layout.setContentsMargins(0, 0, 0, 0) body_layout.addWidget(creator_info, 0) - body_layout.addWidget(QtWidgets.QLabel("Family", self), 0) + body_layout.addWidget(QtWidgets.QLabel("Product type", self), 0) body_layout.addWidget(creators_view, 1) - body_layout.addWidget(QtWidgets.QLabel("Asset", self), 0) - body_layout.addWidget(asset_name_input, 0) - body_layout.addWidget(QtWidgets.QLabel("Subset", self), 0) + body_layout.addWidget(QtWidgets.QLabel("Folder path", self), 0) + body_layout.addWidget(folder_path_input, 0) + body_layout.addWidget(QtWidgets.QLabel("Product name", self), 0) body_layout.addLayout(name_layout, 0) - body_layout.addWidget(subset_name_input, 0) + body_layout.addWidget(product_name_input, 0) useselection_chk = QtWidgets.QCheckBox("Use selection", self) useselection_chk.setCheckState(QtCore.Qt.Checked) @@ -116,7 +116,7 @@ class CreatorWindow(QtWidgets.QDialog): variant_input.returnPressed.connect(self._on_create) variant_input.textChanged.connect(self._on_data_changed) variant_input.report.connect(self.echo) - asset_name_input.textChanged.connect(self._on_data_changed) + folder_path_input.textChanged.connect(self._on_data_changed) creators_view.selectionModel().currentChanged.connect( self._on_selection_changed ) @@ -134,15 +134,15 @@ class CreatorWindow(QtWidgets.QDialog): self._create_btn = create_btn self._useselection_chk = useselection_chk self._variant_input = variant_input - self._subset_name_input = subset_name_input - self._asset_name_input = asset_name_input + self._product_name_input = product_name_input + self._folder_path_input = folder_path_input self._creators_model = creators_model self._creators_proxy = creators_proxy self._creators_view = creators_view - self._subset_btn = subset_button - self._subset_menu = subset_menu + self._variants_btn = variants_btn + self._variants_menu = variants_menu self._msg_label = msg_label @@ -160,7 +160,7 @@ class CreatorWindow(QtWidgets.QDialog): self._create_btn.setEnabled(valid) def _build_menu(self, default_names=None): - """Create optional predefined subset names + """Create optional predefined variants. Args: default_names(list): all predefined names @@ -171,8 +171,8 @@ class CreatorWindow(QtWidgets.QDialog): if not default_names: default_names = [] - menu = self._subset_menu - button = self._subset_btn + menu = self._variants_menu + button = self._variants_btn # Get and destroy the action group group = button.findChild(QtWidgets.QActionGroup) @@ -211,10 +211,10 @@ class CreatorWindow(QtWidgets.QDialog): item_id = index.data(ITEM_ID_ROLE) creator_plugin = self._creators_model.get_creator_by_id(item_id) user_input_text = self._variant_input.text() - asset_name = self._asset_name_input.text() + folder_path = self._folder_path_input.text() - # Early exit if no asset name - if not asset_name: + # Early exit if no folder path + if not folder_path: self._build_menu() self.echo("Asset name is required ..") self._set_valid_state(False) @@ -225,64 +225,63 @@ class CreatorWindow(QtWidgets.QDialog): if creator_plugin: # Get the asset from the database which match with the name asset_doc = get_asset_by_name( - project_name, asset_name, fields=["_id"] + project_name, folder_path, fields=["_id"] ) # Get plugin if not asset_doc or not creator_plugin: - subset_name = user_input_text self._build_menu() if not creator_plugin: - self.echo("No registered families ..") + self.echo("No registered product types ..") else: - self.echo("Asset '%s' not found .." % asset_name) + self.echo("Folder '{}' not found ..".format(folder_path)) self._set_valid_state(False) return - asset_id = asset_doc["_id"] + folder_id = asset_doc["_id"] task_name = get_current_task_name() - # Calculate subset name with Creator plugin - subset_name = creator_plugin.get_subset_name( - user_input_text, task_name, asset_id, project_name + # Calculate product name with Creator plugin + product_name = creator_plugin.get_product_name( + user_input_text, task_name, folder_id, project_name ) # Force replacement of prohibited symbols # QUESTION should Creator care about this and here should be only # validated with schema regex? - # Allow curly brackets in subset name for dynamic keys + # Allow curly brackets in product name for dynamic keys curly_left = "__cbl__" curly_right = "__cbr__" - tmp_subset_name = ( - subset_name + tmp_product_name = ( + product_name .replace("{", curly_left) .replace("}", curly_right) ) # Replace prohibited symbols - tmp_subset_name = re.sub( - "[^{}]+".format(SUBSET_NAME_ALLOWED_SYMBOLS), + tmp_product_name = re.sub( + "[^{}]+".format(PRODUCT_NAME_ALLOWED_SYMBOLS), "", - tmp_subset_name + tmp_product_name ) - subset_name = ( - tmp_subset_name + product_name = ( + tmp_product_name .replace(curly_left, "{") .replace(curly_right, "}") ) - self._subset_name_input.setText(subset_name) + self._product_name_input.setText(product_name) - # Get all subsets of the current asset + # Get all products of the current folder subset_docs = get_subsets( - project_name, asset_ids=[asset_id], fields=["name"] + project_name, asset_ids=[folder_id], fields=["name"] ) - existing_subset_names = { + existing_product_names = { subset_doc["name"] for subset_doc in subset_docs } - existing_subset_names_low = set( + existing_product_names_low = set( _name.lower() - for _name in existing_subset_names + for _name in existing_product_names ) # Defaults to dropdown @@ -296,26 +295,26 @@ class CreatorWindow(QtWidgets.QDialog): # Replace compare_regex = re.compile(re.sub( - user_input_text, "(.+)", subset_name, flags=re.IGNORECASE + user_input_text, "(.+)", product_name, flags=re.IGNORECASE )) - subset_hints = set() + variant_hints = set() if user_input_text: - for _name in existing_subset_names: + for _name in existing_product_names: _result = compare_regex.search(_name) if _result: - subset_hints |= set(_result.groups()) + variant_hints |= set(_result.groups()) - if subset_hints: + if variant_hints: if defaults: defaults.append(SEPARATOR) - defaults.extend(subset_hints) + defaults.extend(variant_hints) self._build_menu(defaults) - # Indicate subset existence + # Indicate product existence if not user_input_text: self._variant_input.as_empty() - elif subset_name.lower() in existing_subset_names_low: - # validate existence of subset name with lowered text + elif product_name.lower() in existing_product_names_low: + # validate existence of product name with lowered text # - "renderMain" vs. "rensermain" mean same path item for # windows self._variant_input.as_exists() @@ -323,7 +322,7 @@ class CreatorWindow(QtWidgets.QDialog): self._variant_input.as_new() # Update the valid state - valid = subset_name.strip() != "" + valid = product_name.strip() != "" self._set_valid_state(valid) @@ -373,7 +372,7 @@ class CreatorWindow(QtWidgets.QDialog): self.setStyleSheet(style.load_stylesheet()) def refresh(self): - self._asset_name_input.setText(get_current_asset_name()) + self._folder_path_input.setText(get_current_asset_name()) self._creators_model.reset() @@ -385,7 +384,7 @@ class CreatorWindow(QtWidgets.QDialog): ["product_types_smart_select"] ) current_index = None - family = None + product_type = None task_name = get_current_task_name() or None lowered_task_name = task_name.lower() if task_name: @@ -395,13 +394,15 @@ class CreatorWindow(QtWidgets.QDialog): } for _task_name in _low_task_names: if _task_name in lowered_task_name: - family = smart_item["name"] + product_type = smart_item["name"] break - if family: + if product_type: break - if family: - indexes = self._creators_model.get_indexes_by_family(family) + if product_type: + indexes = self._creators_model.get_indexes_by_product_type( + product_type + ) if indexes: index = indexes[0] current_index = self._creators_proxy.mapFromSource(index) @@ -422,8 +423,8 @@ class CreatorWindow(QtWidgets.QDialog): if creator_plugin is None: return - subset_name = self._subset_name_input.text() - asset_name = self._asset_name_input.text() + product_name = self._product_name_input.text() + folder_path = self._folder_path_input.text() use_selection = self._useselection_chk.isChecked() variant = self._variant_input.text() @@ -432,8 +433,8 @@ class CreatorWindow(QtWidgets.QDialog): try: legacy_create( creator_plugin, - subset_name, - asset_name, + product_name, + folder_path, options={"useSelection": use_selection}, data={"variant": variant} ) @@ -453,9 +454,9 @@ class CreatorWindow(QtWidgets.QDialog): if error_info: box = CreateErrorMessageBox( - creator_plugin.family, - subset_name, - asset_name, + creator_plugin.product_type, + product_name, + folder_path, *error_info, parent=self ) @@ -464,7 +465,7 @@ class CreatorWindow(QtWidgets.QDialog): self._message_dialog = box else: - self.echo("Created %s .." % subset_name) + self.echo("Created %s .." % product_name) def _on_msg_timer(self): self._msg_label.setText("") @@ -475,7 +476,7 @@ class CreatorWindow(QtWidgets.QDialog): def show(parent=None): - """Display asset creator GUI + """Display product creator GUI Arguments: debug (bool, optional): Run loader in debug-mode, From 08840e1235125d2aebca42f1b984b4525e507717 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 12:43:06 +0100 Subject: [PATCH 266/573] sceneinventory is using product name and type --- .../ayon_core/tools/sceneinventory/model.py | 22 +++++++++---------- client/ayon_core/tools/sceneinventory/view.py | 6 ++--- .../ayon_core/tools/sceneinventory/window.py | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index 05ecfd442d..18fc56db0b 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -43,7 +43,7 @@ class InventoryModel(TreeModel): "Name", "version", "count", - "family", + "productType", "group", "loader", "objectName", @@ -140,8 +140,8 @@ class InventoryModel(TreeModel): return qtawesome.icon("fa.file-o", color=color) if index.column() == 3: - # Family icon - return item.get("familyIcon", None) + # Product type icon + return item.get("productTypeIcon", None) column_name = self.Columns[index.column()] @@ -174,7 +174,7 @@ class InventoryModel(TreeModel): return super(InventoryModel, self).data(index, role) def set_hierarchy_view(self, state): - """Set whether to display subsets in hierarchy view.""" + """Set whether to display products in hierarchy view.""" state = bool(state) if state != self._hierarchy_view: @@ -297,7 +297,7 @@ class InventoryModel(TreeModel): self.add_child(item_node, parent=group_node) # TODO Use product icons - family_icon = qtawesome.icon( + product_type_icon = qtawesome.icon( "fa.folder", color="#0091B2" ) # Prepare site sync specific data @@ -313,18 +313,18 @@ class InventoryModel(TreeModel): subset = group_dict["subset"] asset = group_dict["asset"] - # Get the primary family + # Get product type maj_version, _ = schema.get_schema_version(subset["schema"]) if maj_version < 3: src_doc = version else: src_doc = subset - prim_family = src_doc["data"].get("family") - if not prim_family: + product_type = src_doc["data"].get("family") + if not product_type: families = src_doc["data"].get("families") if families: - prim_family = families[0] + product_type = families[0] # Store the highest available version so the model can know # whether current version is currently up-to-date. @@ -340,8 +340,8 @@ class InventoryModel(TreeModel): group_node["representation"] = repre_id group_node["version"] = version["name"] group_node["highest_version"] = highest_version["name"] - group_node["family"] = prim_family or "" - group_node["familyIcon"] = family_icon + group_node["productType"] = product_type or "" + group_node["productTypeIcon"] = product_type_icon group_node["count"] = len(group_containers) group_node["isGroupNode"] = True group_node["group"] = subset["data"].get("subsetGroup") diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index 214be68ae0..80c89338f5 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -112,17 +112,17 @@ class SceneInventoryView(QtWidgets.QTreeView): loaded_hero_versions = [] versions_by_parent_id = collections.defaultdict(list) - subset_ids = set() + product_ids = set() for version in loaded_versions: if version["type"] == "hero_version": loaded_hero_versions.append(version) else: parent_id = version["parent"] versions_by_parent_id[parent_id].append(version) - subset_ids.add(parent_id) + product_ids.add(parent_id) all_versions = get_versions( - project_name, subset_ids=subset_ids, hero=True + project_name, subset_ids=product_ids, hero=True ) hero_versions = [] versions = [] diff --git a/client/ayon_core/tools/sceneinventory/window.py b/client/ayon_core/tools/sceneinventory/window.py index f1bea26bb9..9584524edd 100644 --- a/client/ayon_core/tools/sceneinventory/window.py +++ b/client/ayon_core/tools/sceneinventory/window.py @@ -78,7 +78,7 @@ class SceneInventoryWindow(QtWidgets.QDialog): view.setColumnWidth(0, 250) # name view.setColumnWidth(1, 55) # version view.setColumnWidth(2, 55) # count - view.setColumnWidth(3, 150) # family + view.setColumnWidth(3, 150) # product type view.setColumnWidth(4, 120) # group view.setColumnWidth(5, 150) # loader From 88bac818f6d2935895b662431b1dfc5d3d492f80 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 12:45:38 +0100 Subject: [PATCH 267/573] subsetmanager using product naming --- client/ayon_core/tools/subsetmanager/model.py | 6 +++++- client/ayon_core/tools/subsetmanager/window.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/subsetmanager/model.py b/client/ayon_core/tools/subsetmanager/model.py index 638d096918..4964abd86d 100644 --- a/client/ayon_core/tools/subsetmanager/model.py +++ b/client/ayon_core/tools/subsetmanager/model.py @@ -32,7 +32,11 @@ class InstanceModel(QtGui.QStandardItemModel): items = [] for instance_data in instances: item_id = str(uuid.uuid4()) - label = instance_data.get("label") or instance_data["subset"] + product_name = ( + instance_data.get("productName") + or instance_data.get("subset") + ) + label = instance_data.get("label") or product_name item = QtGui.QStandardItem(label) item.setEnabled(True) item.setEditable(False) diff --git a/client/ayon_core/tools/subsetmanager/window.py b/client/ayon_core/tools/subsetmanager/window.py index 97dab1adb2..164ffa95a7 100644 --- a/client/ayon_core/tools/subsetmanager/window.py +++ b/client/ayon_core/tools/subsetmanager/window.py @@ -45,7 +45,7 @@ class SubsetManagerWindow(QtWidgets.QDialog): # Filter input filter_input = PlaceholderLineEdit(header_widget) - filter_input.setPlaceholderText("Filter subsets..") + filter_input.setPlaceholderText("Filter products..") # Refresh button icon = qtawesome.icon("fa.refresh", color="white") From 14ed27edac1a6faa1d263fe1137a77d824ecf1a8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 12:47:04 +0100 Subject: [PATCH 268/573] texture copy using product name and type --- client/ayon_core/tools/texture_copy/app.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/tools/texture_copy/app.py b/client/ayon_core/tools/texture_copy/app.py index eef648eaf9..120051060b 100644 --- a/client/ayon_core/tools/texture_copy/app.py +++ b/client/ayon_core/tools/texture_copy/app.py @@ -31,14 +31,23 @@ class TextureCopy: if parents and len(parents) > 0: hierarchy = os.path.join(*parents) + product_name = "Main" + product_type = "texture" template_data = { "project": { "name": project_name, "code": project['data']['code'] }, - "asset": asset['name'], - "family": 'texture', - "subset": 'Main', + "asset": asset["name"], + "family": product_type, + "subset": product_name, + "folder": { + "name": asset["name"], + }, + "product": { + "name": product_name, + "type": product_type, + }, "hierarchy": hierarchy } anatomy = Anatomy(project_name) From 01720eb24874adc7d4cb863059cb74cff77464af Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 12:47:59 +0100 Subject: [PATCH 269/573] workfiles tool is using product name and type --- client/ayon_core/tools/workfiles/models/workfiles.py | 4 ++-- client/ayon_core/tools/workfiles/widgets/save_as_dialog.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/tools/workfiles/models/workfiles.py b/client/ayon_core/tools/workfiles/models/workfiles.py index 55653e34d4..1e9491b3d7 100644 --- a/client/ayon_core/tools/workfiles/models/workfiles.py +++ b/client/ayon_core/tools/workfiles/models/workfiles.py @@ -246,7 +246,7 @@ class WorkareaModel: self._controller.get_host_name(), task_name=task_info.get("name"), task_type=task_info.get("type"), - family="workfile", + product_type="workfile", project_settings=self._controller.project_settings, ) else: @@ -630,7 +630,7 @@ class PublishWorkfilesModel: if not product_ids: return output - # Get version docs of subsets with their families + # Get version docs of products with their families version_entities = ayon_api.get_versions( project_name, product_ids=product_ids, diff --git a/client/ayon_core/tools/workfiles/widgets/save_as_dialog.py b/client/ayon_core/tools/workfiles/widgets/save_as_dialog.py index 2ed2dd0659..77dac1198a 100644 --- a/client/ayon_core/tools/workfiles/widgets/save_as_dialog.py +++ b/client/ayon_core/tools/workfiles/widgets/save_as_dialog.py @@ -49,7 +49,7 @@ class SubversionLineEdit(QtWidgets.QWidget): self._input_field.setText(action.text()) def _update(self, values): - """Create optional predefined subset names + """Create optional predefined product names Args: default_names(list): all predefined names From d110f60e78aca85b3f083467ba59ea0588badc77 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 23 Feb 2024 12:48:00 +0100 Subject: [PATCH 270/573] Cleaned up how to get RR path once again --- client/ayon_core/modules/royalrender/lib.py | 2 +- .../publish/collect_rr_path_from_instance.py | 57 +++++++++++-------- .../publish/submit_jobs_to_royalrender.py | 4 +- 3 files changed, 37 insertions(+), 26 deletions(-) diff --git a/client/ayon_core/modules/royalrender/lib.py b/client/ayon_core/modules/royalrender/lib.py index 60c0427d99..d552e7fb19 100644 --- a/client/ayon_core/modules/royalrender/lib.py +++ b/client/ayon_core/modules/royalrender/lib.py @@ -108,7 +108,7 @@ class BaseCreateRoyalRenderJob(pyblish.api.InstancePlugin, context = instance.context - self._rr_root = instance.data.get("rrPathName") + self._rr_root = instance.data.get("rr_root") if not self._rr_root: raise KnownPublishError( ("Missing RoyalRender root. " diff --git a/client/ayon_core/modules/royalrender/plugins/publish/collect_rr_path_from_instance.py b/client/ayon_core/modules/royalrender/plugins/publish/collect_rr_path_from_instance.py index d860df4684..7fad573a8b 100644 --- a/client/ayon_core/modules/royalrender/plugins/publish/collect_rr_path_from_instance.py +++ b/client/ayon_core/modules/royalrender/plugins/publish/collect_rr_path_from_instance.py @@ -1,41 +1,52 @@ # -*- coding: utf-8 -*- +""" +Requires: + instance.context.data["project_settings"] +Provides: + instance.data["rr_root"] (str) - root folder of RoyalRender server +""" +import os.path + import pyblish.api +from ayon_core.modules.royalrender.rr_job import get_rr_platform class CollectRRPathFromInstance(pyblish.api.InstancePlugin): - """Collect RR Path from instance.""" + """Collect RR Path from instance. + + All RoyalRender server roots are set in `Studio Settings`, each project + uses only key pointing to that part to limit typos inside of Project + settings. + Eventually could be possible to add dropdown with these keys to the + Creators to allow artists to select which RR server they would like to use. + """ order = pyblish.api.CollectorOrder label = "Collect Royal Render path name from the Instance" families = ["render", "prerender", "renderlayer"] def process(self, instance): - instance.data["rrPathName"] = self._collect_rr_path_name(instance) + instance.data["rr_root"] = self._collect_root(instance) self.log.info( - "Using '{}' for submission.".format(instance.data["rrPathName"])) + "Using '{}' for submission.".format(instance.data["rr_root"])) - @staticmethod - def _collect_rr_path_name(instance): + def _collect_root(self, instance): # type: (pyblish.api.Instance) -> str - """Get Royal Render pat name from render instance.""" - - # TODO there are no "rrPaths" on instance, if Publisher should expose - # this (eg. artist could select specific server) it must be added - # to publisher - instance_rr_paths = instance.data.get("rrPaths") - if instance_rr_paths is None: - return "default" - + """Get Royal Render pat name from render instance. + If artist should be able to select specific RR server it must be added + to creator. It is not there yet. + """ rr_settings = instance.context.data["project_settings"]["royalrender"] rr_paths = rr_settings["rr_paths"] - selected_paths = rr_settings["selected_rr_paths"] + selected_keys = rr_settings["selected_rr_paths"] - rr_servers = { - path_key - for path_key in selected_paths - if path_key in rr_paths + platform = get_rr_platform() + key_to_path = { + item["name"]: item["value"][platform] + for item in rr_paths } - for instance_rr_path in instance_rr_paths: - if instance_rr_path in rr_servers: - return instance_rr_path - return "default" + + for selected_key in selected_keys: + rr_root = key_to_path[selected_key] + if os.path.exists(rr_root): + return rr_root diff --git a/client/ayon_core/modules/royalrender/plugins/publish/submit_jobs_to_royalrender.py b/client/ayon_core/modules/royalrender/plugins/publish/submit_jobs_to_royalrender.py index dcec2ac810..54de943428 100644 --- a/client/ayon_core/modules/royalrender/plugins/publish/submit_jobs_to_royalrender.py +++ b/client/ayon_core/modules/royalrender/plugins/publish/submit_jobs_to_royalrender.py @@ -37,8 +37,8 @@ class SubmitJobsToRoyalRender(pyblish.api.ContextPlugin): isinstance(job, RRJob) for job in instance.data.get("rrJobs")): jobs += instance.data.get("rrJobs") - if instance.data.get("rrPathName"): - instance_rr_path = instance.data["rrPathName"] + if instance.data.get("rr_root"): + instance_rr_path = instance.data["rr_root"] if jobs: self._rr_root = instance_rr_path From 2fbec7c23471743caf04b209390ac3abad6c5bbd Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 12:48:08 +0100 Subject: [PATCH 271/573] modify style --- client/ayon_core/style/style.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/style/style.css b/client/ayon_core/style/style.css index f6ecebd683..d2bd4ec135 100644 --- a/client/ayon_core/style/style.css +++ b/client/ayon_core/style/style.css @@ -845,7 +845,7 @@ OverlayMessageWidget QWidget { padding: 1px 5px; } -#CreatorFamilyLabel { +#CreatorProductTypeLabel { font-size: 10pt; font-weight: bold; } @@ -875,7 +875,7 @@ OverlayMessageWidget QWidget { border-radius: 0px; } -#SubsetView::item, #RepresentationView:item { +#ProductView::item, #RepresentationView:item { padding: 5px 1px; border: 0px; } From c74fcc4bbbfbf98a40d73ee03f199e2d3566af25 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 23 Feb 2024 14:03:43 +0200 Subject: [PATCH 272/573] houdni redshift: allow using get aov from other node --- .../hosts/houdini/plugins/publish/collect_redshift_rop.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py index 26dd942559..67cc080ead 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py @@ -68,6 +68,10 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin): files_by_aov = { "_": self.generate_expected_files(instance, beauty_product)} + + aovs_rop = rop.parm("RS_aovGetFromNode").evalAsNode() + if aovs_rop: + rop = aovs_rop num_aovs = rop.evalParm("RS_aov") for index in range(num_aovs): From 9980a2e7392a7ede6bae95c81ba4b9d3f65587ea Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 14:02:30 +0100 Subject: [PATCH 273/573] remove project settings too --- client/ayon_core/addon/base.py | 2 +- client/ayon_core/lib/applications.py | 4 - client/ayon_core/settings/__init__.py | 16 +- client/ayon_core/settings/ayon_settings.py | 181 -- client/ayon_core/settings/constants.py | 22 - .../defaults/project_anatomy/attributes.json | 26 - .../defaults/project_anatomy/imageio.json | 258 --- .../defaults/project_anatomy/roots.json | 7 - .../defaults/project_anatomy/tasks.json | 44 - .../defaults/project_anatomy/templates.json | 67 - .../project_settings/aftereffects.json | 49 - .../project_settings/applications.json | 3 - .../defaults/project_settings/blender.json | 217 --- .../defaults/project_settings/celaction.json | 27 - .../defaults/project_settings/deadline.json | 157 -- .../defaults/project_settings/flame.json | 148 -- .../defaults/project_settings/ftrack.json | 522 ------ .../defaults/project_settings/fusion.json | 58 - .../defaults/project_settings/global.json | 580 ------ .../defaults/project_settings/harmony.json | 38 - .../defaults/project_settings/hiero.json | 87 - .../defaults/project_settings/houdini.json | 192 -- .../defaults/project_settings/kitsu.json | 21 - .../defaults/project_settings/max.json | 100 - .../defaults/project_settings/maya.json | 1613 ----------------- .../defaults/project_settings/nuke.json | 544 ------ .../defaults/project_settings/photoshop.json | 91 - .../defaults/project_settings/resolve.json | 35 - .../project_settings/royalrender.json | 10 - .../defaults/project_settings/shotgrid.json | 22 - .../defaults/project_settings/slack.json | 20 - .../project_settings/standalonepublisher.json | 299 --- .../project_settings/substancepainter.json | 14 - .../project_settings/traypublisher.json | 352 ---- .../defaults/project_settings/tvpaint.json | 111 -- .../defaults/project_settings/unreal.json | 21 - .../project_settings/webpublisher.json | 145 -- client/ayon_core/settings/lib.py | 328 ++-- 38 files changed, 149 insertions(+), 6282 deletions(-) delete mode 100644 client/ayon_core/settings/ayon_settings.py delete mode 100644 client/ayon_core/settings/constants.py delete mode 100644 client/ayon_core/settings/defaults/project_anatomy/attributes.json delete mode 100644 client/ayon_core/settings/defaults/project_anatomy/imageio.json delete mode 100644 client/ayon_core/settings/defaults/project_anatomy/roots.json delete mode 100644 client/ayon_core/settings/defaults/project_anatomy/tasks.json delete mode 100644 client/ayon_core/settings/defaults/project_anatomy/templates.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/aftereffects.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/applications.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/blender.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/celaction.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/deadline.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/flame.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/ftrack.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/fusion.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/global.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/harmony.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/hiero.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/houdini.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/kitsu.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/max.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/maya.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/nuke.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/photoshop.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/resolve.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/royalrender.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/shotgrid.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/slack.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/standalonepublisher.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/substancepainter.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/traypublisher.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/tvpaint.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/unreal.json delete mode 100644 client/ayon_core/settings/defaults/project_settings/webpublisher.json diff --git a/client/ayon_core/addon/base.py b/client/ayon_core/addon/base.py index bcd27d8029..f71a82b591 100644 --- a/client/ayon_core/addon/base.py +++ b/client/ayon_core/addon/base.py @@ -17,7 +17,7 @@ import appdirs from ayon_core.lib import Logger, is_dev_mode_enabled from ayon_core.client import get_ayon_server_api_connection -from ayon_core.settings.ayon_settings import get_ayon_settings +from ayon_core.settings import get_ayon_settings from .interfaces import ( IPluginPaths, diff --git a/client/ayon_core/lib/applications.py b/client/ayon_core/lib/applications.py index 373b82d82f..02325a7d8a 100644 --- a/client/ayon_core/lib/applications.py +++ b/client/ayon_core/lib/applications.py @@ -17,10 +17,6 @@ from ayon_core.settings import ( get_system_settings, get_project_settings, ) -from ayon_core.settings.constants import ( - METADATA_KEYS, - M_DYNAMIC_KEY_LABEL -) from .log import Logger from .profiles_filtering import filter_profiles from .local_settings import get_ayon_username diff --git a/client/ayon_core/settings/__init__.py b/client/ayon_core/settings/__init__.py index 8c3fdbee6d..d32b5f3391 100644 --- a/client/ayon_core/settings/__init__.py +++ b/client/ayon_core/settings/__init__.py @@ -1,22 +1,18 @@ -from .constants import ( - PROJECT_SETTINGS_KEY, -) from .lib import ( - get_general_environments, + get_ayon_settings, + get_studio_settings, get_system_settings, get_project_settings, + get_general_environments, get_current_project_settings, ) -from .ayon_settings import get_ayon_settings __all__ = ( - "PROJECT_SETTINGS_KEY", - - "get_general_environments", + "get_ayon_settings", + "get_studio_settings", "get_system_settings", + "get_general_environments", "get_project_settings", "get_current_project_settings", - - "get_ayon_settings", ) diff --git a/client/ayon_core/settings/ayon_settings.py b/client/ayon_core/settings/ayon_settings.py deleted file mode 100644 index 9b038dc1a2..0000000000 --- a/client/ayon_core/settings/ayon_settings.py +++ /dev/null @@ -1,181 +0,0 @@ -"""Helper functionality to convert AYON settings to OpenPype v3 settings. - -The settings are converted, so we can use v3 code with AYON settings. Once -the code of and addon is converted to full AYON addon which expect AYON -settings the conversion function can be removed. - -The conversion is hardcoded -> there is no other way how to achieve the result. - -Main entrypoints are functions: -- convert_project_settings - convert settings to project settings -- convert_system_settings - convert settings to system settings -# Both getters cache values -- get_ayon_project_settings - replacement for 'get_project_settings' -- get_ayon_system_settings - replacement for 'get_system_settings' -""" -import os -import collections -import json -import copy -import time - -import six - -from ayon_core.client import get_ayon_server_api_connection - - -# --------- Project settings --------- -def convert_project_settings(ayon_settings, default_settings): - default_settings = copy.deepcopy(default_settings) - output = {} - for key, value in ayon_settings.items(): - if key not in output: - output[key] = value - - for key, value in default_settings.items(): - if key not in output: - output[key] = value - - return output - - -class CacheItem: - lifetime = 10 - - def __init__(self, value, outdate_time=None): - self._value = value - if outdate_time is None: - outdate_time = time.time() + self.lifetime - self._outdate_time = outdate_time - - @classmethod - def create_outdated(cls): - return cls({}, 0) - - def get_value(self): - return copy.deepcopy(self._value) - - def update_value(self, value): - self._value = value - self._outdate_time = time.time() + self.lifetime - - @property - def is_outdated(self): - return time.time() > self._outdate_time - - -class _AyonSettingsCache: - use_bundles = None - variant = None - addon_versions = CacheItem.create_outdated() - studio_settings = CacheItem.create_outdated() - cache_by_project_name = collections.defaultdict( - CacheItem.create_outdated) - - @classmethod - def _use_bundles(cls): - if _AyonSettingsCache.use_bundles is None: - con = get_ayon_server_api_connection() - major, minor, _, _, _ = con.get_server_version_tuple() - use_bundles = True - if (major, minor) < (0, 3): - use_bundles = False - _AyonSettingsCache.use_bundles = use_bundles - return _AyonSettingsCache.use_bundles - - @classmethod - def _get_variant(cls): - if _AyonSettingsCache.variant is None: - from ayon_core.lib import is_staging_enabled, is_dev_mode_enabled - - variant = "production" - if is_dev_mode_enabled(): - variant = cls._get_bundle_name() - elif is_staging_enabled(): - variant = "staging" - - # Cache variant - _AyonSettingsCache.variant = variant - - # Set the variant to global ayon api connection - con = get_ayon_server_api_connection() - con.set_default_settings_variant(variant) - return _AyonSettingsCache.variant - - @classmethod - def _get_bundle_name(cls): - return os.environ["AYON_BUNDLE_NAME"] - - @classmethod - def get_value_by_project(cls, project_name): - cache_item = _AyonSettingsCache.cache_by_project_name[project_name] - if cache_item.is_outdated: - con = get_ayon_server_api_connection() - if cls._use_bundles(): - value = con.get_addons_settings( - bundle_name=cls._get_bundle_name(), - project_name=project_name, - variant=cls._get_variant() - ) - else: - value = con.get_addons_settings(project_name) - cache_item.update_value(value) - return cache_item.get_value() - - @classmethod - def _get_addon_versions_from_bundle(cls): - con = get_ayon_server_api_connection() - expected_bundle = cls._get_bundle_name() - bundles = con.get_bundles()["bundles"] - bundle = next( - ( - bundle - for bundle in bundles - if bundle["name"] == expected_bundle - ), - None - ) - if bundle is not None: - return bundle["addons"] - return {} - - @classmethod - def get_addon_versions(cls): - cache_item = _AyonSettingsCache.addon_versions - if cache_item.is_outdated: - if cls._use_bundles(): - addons = cls._get_addon_versions_from_bundle() - else: - con = get_ayon_server_api_connection() - settings_data = con.get_addons_settings( - only_values=False, - variant=cls._get_variant() - ) - addons = settings_data["versions"] - cache_item.update_value(addons) - - return cache_item.get_value() - - -def get_ayon_project_settings(default_values, project_name): - ayon_settings = _AyonSettingsCache.get_value_by_project(project_name) - return convert_project_settings(ayon_settings, default_values) - - -def get_ayon_system_settings(): - return _AyonSettingsCache.get_value_by_project(None) - - -def get_ayon_settings(project_name=None): - """AYON studio settings. - - Raw AYON settings values. - - Args: - project_name (Optional[str]): Project name. - - Returns: - dict[str, Any]: AYON settings. - """ - - return _AyonSettingsCache.get_value_by_project(project_name) diff --git a/client/ayon_core/settings/constants.py b/client/ayon_core/settings/constants.py deleted file mode 100644 index fef220bc31..0000000000 --- a/client/ayon_core/settings/constants.py +++ /dev/null @@ -1,22 +0,0 @@ -# Metadata keys for work with studio and project overrides -M_OVERRIDDEN_KEY = "__overriden_keys__" -# Metadata key for storing dynamic created labels -M_DYNAMIC_KEY_LABEL = "__dynamic_keys_labels__" - -METADATA_KEYS = frozenset([ - M_OVERRIDDEN_KEY, - M_DYNAMIC_KEY_LABEL -]) - -# Keys where studio's system overrides are stored -PROJECT_SETTINGS_KEY = "project_settings" - - -__all__ = ( - "M_OVERRIDDEN_KEY", - "M_DYNAMIC_KEY_LABEL", - - "METADATA_KEYS", - - "PROJECT_SETTINGS_KEY", -) diff --git a/client/ayon_core/settings/defaults/project_anatomy/attributes.json b/client/ayon_core/settings/defaults/project_anatomy/attributes.json deleted file mode 100644 index 0cc414fb69..0000000000 --- a/client/ayon_core/settings/defaults/project_anatomy/attributes.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "fps": 25.0, - "frameStart": 1001, - "frameEnd": 1001, - "clipIn": 1, - "clipOut": 1, - "handleStart": 0, - "handleEnd": 0, - "resolutionWidth": 1920, - "resolutionHeight": 1080, - "pixelAspect": 1.0, - "applications": [ - "maya/2020", - "nuke/12-2", - "nukex/12-2", - "hiero/12-2", - "resolve/stable", - "houdini/18-5", - "blender/2-91", - "harmony/20", - "photoshop/2021", - "aftereffects/2021" - ], - "tools_env": [], - "active": true -} diff --git a/client/ayon_core/settings/defaults/project_anatomy/imageio.json b/client/ayon_core/settings/defaults/project_anatomy/imageio.json deleted file mode 100644 index d38d0a0774..0000000000 --- a/client/ayon_core/settings/defaults/project_anatomy/imageio.json +++ /dev/null @@ -1,258 +0,0 @@ -{ - "hiero": { - "workfile": { - "ocioConfigName": "nuke-default", - "ocioconfigpath": { - "windows": [], - "darwin": [], - "linux": [] - }, - "workingSpace": "linear", - "sixteenBitLut": "sRGB", - "eightBitLut": "sRGB", - "floatLut": "linear", - "logLut": "Cineon", - "viewerLut": "sRGB", - "thumbnailLut": "sRGB" - }, - "regexInputs": { - "inputs": [ - { - "regex": "[^-a-zA-Z0-9](plateRef).*(?=mp4)", - "colorspace": "sRGB" - } - ] - } - }, - "nuke": { - "viewer": { - "viewerProcess": "sRGB" - }, - "baking": { - "viewerProcess": "rec709" - }, - "workfile": { - "colorManagement": "Nuke", - "OCIO_config": "nuke-default", - "customOCIOConfigPath": { - "windows": [], - "darwin": [], - "linux": [] - }, - "workingSpaceLUT": "linear", - "monitorLut": "sRGB", - "int8Lut": "sRGB", - "int16Lut": "sRGB", - "logLut": "Cineon", - "floatLut": "linear" - }, - "nodes": { - "requiredNodes": [ - { - "plugins": [ - "CreateWriteRender" - ], - "nukeNodeClass": "Write", - "knobs": [ - { - "type": "text", - "name": "file_type", - "value": "exr" - }, - { - "type": "text", - "name": "datatype", - "value": "16 bit half" - }, - { - "type": "text", - "name": "compression", - "value": "Zip (1 scanline)" - }, - { - "type": "bool", - "name": "autocrop", - "value": true - }, - { - "type": "color_gui", - "name": "tile_color", - "value": [ - 186, - 35, - 35, - 255 - ] - }, - { - "type": "text", - "name": "channels", - "value": "rgb" - }, - { - "type": "text", - "name": "colorspace", - "value": "linear" - }, - { - "type": "bool", - "name": "create_directories", - "value": true - } - ] - }, - { - "plugins": [ - "CreateWritePrerender" - ], - "nukeNodeClass": "Write", - "knobs": [ - { - "type": "text", - "name": "file_type", - "value": "exr" - }, - { - "type": "text", - "name": "datatype", - "value": "16 bit half" - }, - { - "type": "text", - "name": "compression", - "value": "Zip (1 scanline)" - }, - { - "type": "bool", - "name": "autocrop", - "value": true - }, - { - "type": "color_gui", - "name": "tile_color", - "value": [ - 171, - 171, - 10, - 255 - ] - }, - { - "type": "text", - "name": "channels", - "value": "rgb" - }, - { - "type": "text", - "name": "colorspace", - "value": "linear" - }, - { - "type": "bool", - "name": "create_directories", - "value": true - } - ] - }, - { - "plugins": [ - "CreateWriteImage" - ], - "nukeNodeClass": "Write", - "knobs": [ - { - "type": "text", - "name": "file_type", - "value": "tiff" - }, - { - "type": "text", - "name": "datatype", - "value": "16 bit" - }, - { - "type": "text", - "name": "compression", - "value": "Deflate" - }, - { - "type": "color_gui", - "name": "tile_color", - "value": [ - 56, - 162, - 7, - 255 - ] - }, - { - "type": "text", - "name": "channels", - "value": "rgb" - }, - { - "type": "text", - "name": "colorspace", - "value": "sRGB" - }, - { - "type": "bool", - "name": "create_directories", - "value": true - } - ] - } - ], - "overrideNodes": [] - }, - "regexInputs": { - "inputs": [ - { - "regex": "(beauty).*(?=.exr)", - "colorspace": "linear" - } - ] - } - }, - "maya": { - "colorManagementPreference_v2": { - "enabled": true, - "configFilePath": { - "windows": [], - "darwin": [], - "linux": [] - }, - "renderSpace": "ACEScg", - "displayName": "sRGB", - "viewName": "ACES 1.0 SDR-video" - }, - "colorManagementPreference": { - "configFilePath": { - "windows": [], - "darwin": [], - "linux": [] - }, - "renderSpace": "scene-linear Rec 709/sRGB", - "viewTransform": "sRGB gamma" - } - }, - "flame": { - "project": { - "colourPolicy": "ACES 1.1", - "frameDepth": "16-bit fp", - "fieldDominance": "PROGRESSIVE" - }, - "profilesMapping": { - "inputs": [ - { - "flameName": "ACEScg", - "ocioName": "ACES - ACEScg" - }, - { - "flameName": "Rec.709 video", - "ocioName": "Output - Rec.709" - } - ] - } - } -} diff --git a/client/ayon_core/settings/defaults/project_anatomy/roots.json b/client/ayon_core/settings/defaults/project_anatomy/roots.json deleted file mode 100644 index 8171d17d56..0000000000 --- a/client/ayon_core/settings/defaults/project_anatomy/roots.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "work": { - "windows": "C:/projects", - "darwin": "/Volumes/path", - "linux": "/mnt/share/projects" - } -} diff --git a/client/ayon_core/settings/defaults/project_anatomy/tasks.json b/client/ayon_core/settings/defaults/project_anatomy/tasks.json deleted file mode 100644 index 135462839f..0000000000 --- a/client/ayon_core/settings/defaults/project_anatomy/tasks.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "Generic": { - "short_name": "gener" - }, - "Art": { - "short_name": "art" - }, - "Modeling": { - "short_name": "mdl" - }, - "Texture": { - "short_name": "tex" - }, - "Lookdev": { - "short_name": "look" - }, - "Rigging": { - "short_name": "rig" - }, - "Edit": { - "short_name": "edit" - }, - "Layout": { - "short_name": "lay" - }, - "Setdress": { - "short_name": "dress" - }, - "Animation": { - "short_name": "anim" - }, - "FX": { - "short_name": "fx" - }, - "Lighting": { - "short_name": "lgt" - }, - "Paint": { - "short_name": "paint" - }, - "Compositing": { - "short_name": "comp" - } -} diff --git a/client/ayon_core/settings/defaults/project_anatomy/templates.json b/client/ayon_core/settings/defaults/project_anatomy/templates.json deleted file mode 100644 index 6c3e038d27..0000000000 --- a/client/ayon_core/settings/defaults/project_anatomy/templates.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "defaults": { - "version_padding": 3, - "version": "v{version:0>{@version_padding}}", - "frame_padding": 4, - "frame": "{frame:0>{@frame_padding}}" - }, - "work": { - "folder": "{root[work]}/{project[name]}/{hierarchy}/{asset}/work/{task[name]}", - "file": "{project[code]}_{asset}_{task[name]}_{@version}<_{comment}>.{ext}", - "path": "{@folder}/{@file}" - }, - "render": { - "folder": "{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}", - "file": "{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}>.{ext}", - "path": "{@folder}/{@file}" - }, - "publish": { - "folder": "{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}", - "file": "{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}><_{udim}>.{ext}", - "path": "{@folder}/{@file}", - "thumbnail": "{thumbnail_root}/{project[name]}/{_id}_{thumbnail_type}.{ext}" - }, - "hero": { - "folder": "{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/hero", - "file": "{project[code]}_{asset}_{subset}_hero<_{output}><.{frame}>.{ext}", - "path": "{@folder}/{@file}" - }, - "delivery": {}, - "unreal": { - "folder": "{root[work]}/{project[name]}/unreal/{task[name]}", - "file": "{project[code]}_{asset}.{ext}", - "path": "{@folder}/{@file}" - }, - "others": { - "maya2unreal": { - "folder": "{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}", - "file": "{subset}_{@version}<_{output}><.{@frame}>.{ext}", - "path": "{@folder}/{@file}" - }, - "online": { - "folder": "{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}", - "file": "{originalBasename}<.{@frame}><_{udim}>.{ext}", - "path": "{@folder}/{@file}" - }, - "tycache": { - "folder": "{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}", - "file": "{originalBasename}.{ext}", - "path": "{@folder}/{@file}" - }, - "source": { - "folder": "{root[work]}/{originalDirname}", - "file": "{originalBasename}.{ext}", - "path": "{@folder}/{@file}" - }, - "transient": { - "folder": "{root[work]}/{project[name]}/{hierarchy}/{asset}/work/{family}/{subset}" - }, - "__dynamic_keys_labels__": { - "maya2unreal": "Maya to Unreal", - "online": "online", - "tycache": "tycache", - "source": "source", - "transient": "transient" - } - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/aftereffects.json b/client/ayon_core/settings/defaults/project_settings/aftereffects.json deleted file mode 100644 index 77ccb74410..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/aftereffects.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "imageio": { - "activate_host_color_management": true, - "ocio_config": { - "override_global_config": false, - "filepath": [] - }, - "file_rules": { - "activate_host_rules": false, - "rules": {} - } - }, - "create": { - "RenderCreator": { - "default_variants": [ - "Main" - ], - "mark_for_review": true - } - }, - "publish": { - "CollectReview": { - "enabled": true - }, - "ValidateSceneSettings": { - "enabled": true, - "optional": true, - "active": true, - "skip_resolution_check": [ - ".*" - ], - "skip_timelines_check": [ - ".*" - ] - }, - "ValidateContainers": { - "enabled": true, - "optional": true, - "active": true - } - }, - "workfile_builder": { - "create_first_version": false, - "custom_templates": [] - }, - "templated_workfile_build": { - "profiles": [] - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/applications.json b/client/ayon_core/settings/defaults/project_settings/applications.json deleted file mode 100644 index 62f3cdfe1b..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/applications.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "only_available": false -} diff --git a/client/ayon_core/settings/defaults/project_settings/blender.json b/client/ayon_core/settings/defaults/project_settings/blender.json deleted file mode 100644 index 48f3ef8ef0..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/blender.json +++ /dev/null @@ -1,217 +0,0 @@ -{ - "unit_scale_settings": { - "enabled": true, - "apply_on_opening": false, - "base_file_unit_scale": 0.01 - }, - "set_resolution_startup": true, - "set_frames_startup": true, - "imageio": { - "activate_host_color_management": true, - "ocio_config": { - "override_global_config": false, - "filepath": [] - }, - "file_rules": { - "activate_host_rules": false, - "rules": {} - } - }, - "RenderSettings": { - "default_render_image_folder": "renders/blender", - "aov_separator": "underscore", - "image_format": "exr", - "multilayer_exr": true, - "renderer": "CYCLES", - "aov_list": ["combined"], - "custom_passes": [] - }, - "workfile_builder": { - "create_first_version": false, - "custom_templates": [] - }, - "publish": { - "ValidateCameraZeroKeyframe": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateFileSaved": { - "enabled": true, - "optional": false, - "active": true, - "exclude_families": [] - }, - "ValidateRenderCameraIsSet": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateDeadlinePublish": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateMeshHasUvs": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateMeshNoNegativeScale": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateTransformZero": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateNoColonsInName": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateInstanceEmpty": { - "enabled": true, - "optional": false, - "active": true - }, - "ExtractBlend": { - "enabled": true, - "optional": true, - "active": true, - "families": [ - "model", - "camera", - "rig", - "action", - "layout", - "blendScene" - ] - }, - "ExtractFBX": { - "enabled": true, - "optional": true, - "active": false - }, - "ExtractModelABC": { - "enabled": true, - "optional": true, - "active": true - }, - "ExtractBlendAnimation": { - "enabled": true, - "optional": true, - "active": true - }, - "ExtractAnimationFBX": { - "enabled": true, - "optional": true, - "active": false - }, - "ExtractCamera": { - "enabled": true, - "optional": true, - "active": true - }, - "ExtractCameraABC": { - "enabled": true, - "optional": true, - "active": true - }, - "ExtractLayout": { - "enabled": true, - "optional": true, - "active": false - }, - "ExtractThumbnail": { - "enabled": true, - "optional": true, - "active": true, - "presets": { - "model": { - "image_settings": { - "file_format": "JPEG", - "color_mode": "RGB", - "quality": 100 - }, - "display_options": { - "shading": { - "light": "STUDIO", - "studio_light": "Default", - "type": "SOLID", - "color_type": "OBJECT", - "show_xray": false, - "show_shadows": false, - "show_cavity": true - }, - "overlay": { - "show_overlays": false - } - } - }, - "rig": { - "image_settings": { - "file_format": "JPEG", - "color_mode": "RGB", - "quality": 100 - }, - "display_options": { - "shading": { - "light": "STUDIO", - "studio_light": "Default", - "type": "SOLID", - "color_type": "OBJECT", - "show_xray": true, - "show_shadows": false, - "show_cavity": false - }, - "overlay": { - "show_overlays": true, - "show_ortho_grid": false, - "show_floor": false, - "show_axis_x": false, - "show_axis_y": false, - "show_axis_z": false, - "show_text": false, - "show_stats": false, - "show_cursor": false, - "show_annotation": false, - "show_extras": false, - "show_relationship_lines": false, - "show_outline_selected": false, - "show_motion_paths": false, - "show_object_origins": false, - "show_bones": true - } - } - } - } - }, - "ExtractPlayblast": { - "enabled": true, - "optional": true, - "active": true, - "presets": { - "default": { - "image_settings": { - "file_format": "PNG", - "color_mode": "RGB", - "color_depth": "8", - "compression": 15 - }, - "display_options": { - "shading": { - "type": "MATERIAL", - "render_pass": "COMBINED" - }, - "overlay": { - "show_overlays": false - } - } - } - } - } - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/celaction.json b/client/ayon_core/settings/defaults/project_settings/celaction.json deleted file mode 100644 index af56a36649..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/celaction.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "imageio": { - "activate_host_color_management": true, - "ocio_config": { - "override_global_config": false, - "filepath": [] - }, - "file_rules": { - "activate_host_rules": false, - "rules": {} - } - }, - "workfile": { - "submission_overrides": [ - "render_chunk", - "frame_range", - "resolution" - ] - }, - "publish": { - "CollectRenderPath": { - "output_extension": "png", - "anatomy_template_key_render_files": "render", - "anatomy_template_key_metadata": "render" - } - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/deadline.json b/client/ayon_core/settings/defaults/project_settings/deadline.json deleted file mode 100644 index b02cfa8207..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/deadline.json +++ /dev/null @@ -1,157 +0,0 @@ -{ - "deadline_servers": [], - "publish": { - "CollectDefaultDeadlineServer": { - "pass_mongo_url": true - }, - "CollectDeadlinePools": { - "primary_pool": "", - "secondary_pool": "" - }, - "ValidateExpectedFiles": { - "enabled": true, - "active": true, - "allow_user_override": true, - "families": [ - "render" - ], - "targets": [ - "deadline" - ] - }, - "MayaSubmitDeadline": { - "enabled": true, - "optional": false, - "active": true, - "tile_assembler_plugin": "DraftTileAssembler", - "use_published": true, - "import_reference": false, - "asset_dependencies": true, - "priority": 50, - "tile_priority": 50, - "group": "none", - "limit": [], - "jobInfo": {}, - "pluginInfo": {}, - "scene_patches": [], - "strict_error_checking": true - }, - "MaxSubmitDeadline": { - "enabled": true, - "optional": false, - "active": true, - "use_published": true, - "priority": 50, - "chunk_size": 10, - "group": "none" - }, - "FusionSubmitDeadline": { - "enabled": true, - "optional": false, - "active": true, - "priority": 50, - "chunk_size": 10, - "concurrent_tasks": 1, - "group": "", - "plugin": "Fusion" - }, - "NukeSubmitDeadline": { - "enabled": true, - "optional": false, - "active": true, - "priority": 50, - "chunk_size": 10, - "concurrent_tasks": 1, - "group": "", - "department": "", - "use_gpu": true, - "workfile_dependency": true, - "use_published_workfile": true, - "env_allowed_keys": [], - "env_search_replace_values": {}, - "limit_groups": {} - }, - "HarmonySubmitDeadline": { - "enabled": true, - "optional": false, - "active": true, - "use_published": true, - "priority": 50, - "chunk_size": 10000, - "group": "", - "department": "" - }, - "AfterEffectsSubmitDeadline": { - "enabled": true, - "optional": false, - "active": true, - "use_published": true, - "priority": 50, - "chunk_size": 10000, - "group": "", - "department": "", - "multiprocess": true - }, - "CelactionSubmitDeadline": { - "enabled": true, - "deadline_department": "", - "deadline_priority": 50, - "deadline_pool": "", - "deadline_pool_secondary": "", - "deadline_group": "", - "deadline_chunk_size": 10, - "deadline_job_delay": "00:00:00:00" - }, - "BlenderSubmitDeadline": { - "enabled": true, - "optional": false, - "active": true, - "use_published": true, - "priority": 50, - "chunk_size": 10, - "group": "none", - "job_delay": "00:00:00:00" - }, - "ProcessSubmittedCacheJobOnFarm": { - "enabled": true, - "deadline_department": "", - "deadline_pool": "", - "deadline_group": "", - "deadline_chunk_size": 1, - "deadline_priority": 50 - }, - "ProcessSubmittedJobOnFarm": { - "enabled": true, - "deadline_department": "", - "deadline_pool": "", - "deadline_group": "", - "deadline_chunk_size": 1, - "deadline_priority": 50, - "publishing_script": "", - "skip_integration_repre_list": [], - "aov_filter": { - "maya": [ - ".*([Bb]eauty).*" - ], - "blender": [ - ".*([Bb]eauty).*" - ], - "aftereffects": [ - ".*" - ], - "celaction": [ - ".*" - ], - "harmony": [ - ".*" - ], - "max": [ - ".*" - ], - "fusion": [ - ".*" - ] - } - } - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/flame.json b/client/ayon_core/settings/defaults/project_settings/flame.json deleted file mode 100644 index 5b4b62c140..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/flame.json +++ /dev/null @@ -1,148 +0,0 @@ -{ - "imageio": { - "activate_host_color_management": true, - "remapping": { - "rules": [] - }, - "ocio_config": { - "override_global_config": false, - "filepath": [] - }, - "file_rules": { - "activate_host_rules": false, - "rules": {} - }, - "project": { - "colourPolicy": "ACES 1.1", - "frameDepth": "16-bit fp", - "fieldDominance": "PROGRESSIVE" - }, - "profilesMapping": { - "inputs": [ - { - "flameName": "ACEScg", - "ocioName": "ACES - ACEScg" - }, - { - "flameName": "Rec.709 video", - "ocioName": "Output - Rec.709" - } - ] - } - }, - "create": { - "CreateShotClip": { - "hierarchy": "{folder}/{sequence}", - "useShotName": true, - "clipRename": false, - "clipName": "{sequence}{shot}", - "segmentIndex": true, - "countFrom": 10, - "countSteps": 10, - "folder": "shots", - "episode": "ep01", - "sequence": "a", - "track": "{_track_}", - "shot": "####", - "vSyncOn": false, - "workfileFrameStart": 1001, - "handleStart": 5, - "handleEnd": 5, - "includeHandles": false, - "retimedHandles": true, - "retimedFramerange": true - } - }, - "publish": { - "CollectTimelineInstances": { - "xml_preset_attrs_from_comments": [ - { - "name": "width", - "type": "number" - }, - { - "name": "height", - "type": "number" - }, - { - "name": "pixelRatio", - "type": "float" - }, - { - "name": "resizeType", - "type": "string" - }, - { - "name": "resizeFilter", - "type": "string" - } - ], - "add_tasks": [ - { - "name": "compositing", - "type": "Compositing", - "create_batch_group": true - } - ] - }, - "ExtractSubsetResources": { - "keep_original_representation": false, - "export_presets_mapping": { - "exr16fpdwaa": { - "active": true, - "export_type": "File Sequence", - "ext": "exr", - "xml_preset_file": "OpenEXR (16-bit fp DWAA).xml", - "colorspace_out": "ACES - ACEScg", - "xml_preset_dir": "", - "parsed_comment_attrs": true, - "representation_add_range": true, - "representation_tags": [], - "load_to_batch_group": true, - "batch_group_loader_name": "LoadClipBatch", - "filter_path_regex": ".*" - } - } - }, - "IntegrateBatchGroup": { - "enabled": false - } - }, - "load": { - "LoadClip": { - "enabled": true, - "families": [ - "render2d", - "source", - "plate", - "render", - "review" - ], - "reel_group_name": "OpenPype_Reels", - "reel_name": "Loaded", - "clip_name_template": "{asset}_{subset}<_{output}>", - "layer_rename_template": "{asset}_{subset}<_{output}>", - "layer_rename_patterns": [ - "rgb", - "rgba" - ] - }, - "LoadClipBatch": { - "enabled": true, - "families": [ - "render2d", - "source", - "plate", - "render", - "review" - ], - "reel_name": "OP_LoadedReel", - "clip_name_template": "{batch}_{asset}_{subset}<_{output}>", - "layer_rename_template": "{asset}_{subset}<_{output}>", - "layer_rename_patterns": [ - "rgb", - "rgba" - ] - } - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/ftrack.json b/client/ayon_core/settings/defaults/project_settings/ftrack.json deleted file mode 100644 index e2ca334b5f..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/ftrack.json +++ /dev/null @@ -1,522 +0,0 @@ -{ - "events": { - "sync_to_avalon": { - "role_list": [ - "Pypeclub", - "Administrator", - "Project manager" - ] - }, - "prepare_project": { - "enabled": true, - "role_list": [ - "Pypeclub", - "Administrator", - "Project manager" - ] - }, - "sync_hier_entity_attributes": { - "enabled": true, - "interest_entity_types": [ - "Shot", - "Asset Build" - ], - "interest_attributes": [ - "frameStart", - "frameEnd" - ], - "action_enabled": true, - "role_list": [ - "Pypeclub", - "Administrator", - "Project Manager" - ] - }, - "clone_review_session": { - "enabled": true, - "role_list": [ - "Pypeclub", - "Administrator", - "Project Manager" - ] - }, - "thumbnail_updates": { - "enabled": true, - "levels": 1 - }, - "user_assignment": { - "enabled": true - }, - "status_update": { - "enabled": true, - "mapping": { - "In Progress": [ - "__any__" - ], - "Ready": [ - "Not Ready" - ], - "__ignore__": [ - "in progress", - "omitted", - "on hold" - ] - } - }, - "status_task_to_parent": { - "enabled": true, - "parent_object_types": [ - "Shot", - "Asset Build" - ], - "parent_status_match_all_task_statuses": { - "Completed": [ - "Approved", - "Omitted" - ] - }, - "parent_status_by_task_status": [ - { - "new_status": "In Progress", - "task_statuses": [ - "in progress", - "change requested", - "retake", - "pending review" - ] - } - ] - }, - "status_task_to_version": { - "enabled": true, - "mapping": {}, - "asset_types_filter": [] - }, - "status_version_to_task": { - "enabled": true, - "mapping": {}, - "asset_types_to_skip": [] - }, - "next_task_update": { - "enabled": true, - "mapping": { - "Not Ready": "Ready" - }, - "ignored_statuses": [ - "Omitted" - ], - "name_sorting": false - }, - "transfer_values_of_hierarchical_attributes": { - "enabled": true, - "role_list": [ - "Administrator", - "Project manager" - ] - }, - "create_daily_review_session": { - "enabled": true, - "role_list": [ - "Administrator", - "Project Manager" - ], - "cycle_enabled": false, - "cycle_hour_start": [ - 0, - 0, - 0 - ], - "review_session_template": "{yy}{mm}{dd}" - } - }, - "user_handlers": { - "application_launch_statuses": { - "enabled": true, - "ignored_statuses": [ - "In Progress", - "Omitted", - "On hold", - "Approved" - ], - "status_change": { - "In Progress": [] - } - }, - "create_update_attributes": { - "role_list": [ - "Pypeclub", - "Administrator" - ] - }, - "prepare_project": { - "enabled": true, - "role_list": [ - "Pypeclub", - "Administrator", - "Project manager" - ], - "create_project_structure_checked": false - }, - "clean_hierarchical_attr": { - "enabled": true, - "role_list": [ - "Pypeclub", - "Administrator", - "Project manager" - ] - }, - "delete_asset_subset": { - "enabled": true, - "role_list": [ - "Pypeclub", - "Administrator", - "Project Manager" - ] - }, - "delete_old_versions": { - "enabled": true, - "role_list": [ - "Pypeclub", - "Project Manager", - "Administrator" - ] - }, - "delivery_action": { - "enabled": true, - "role_list": [ - "Pypeclub", - "Project Manager", - "Administrator" - ] - }, - "store_thubmnail_to_avalon": { - "enabled": true, - "role_list": [ - "Pypeclub", - "Project Manager", - "Administrator" - ] - }, - "job_killer": { - "enabled": true, - "role_list": [ - "Pypeclub", - "Administrator" - ] - }, - "sync_to_avalon_local": { - "enabled": true, - "role_list": [ - "Pypeclub", - "Administrator" - ] - }, - "fill_workfile_attribute": { - "enabled": false, - "custom_attribute_key": "", - "role_list": [] - }, - "seed_project": { - "enabled": true, - "role_list": [ - "Pypeclub" - ] - } - }, - "publish": { - "CollectFtrackFamily": { - "enabled": true, - "profiles": [ - { - "hosts": [ - "standalonepublisher" - ], - "families": [], - "task_types": [], - "tasks": [], - "add_ftrack_family": true, - "advanced_filtering": [] - }, - { - "hosts": [ - "standalonepublisher" - ], - "families": [ - "matchmove", - "shot" - ], - "task_types": [], - "tasks": [], - "add_ftrack_family": false, - "advanced_filtering": [] - }, - { - "hosts": [ - "standalonepublisher" - ], - "families": [ - "plate" - ], - "task_types": [], - "tasks": [], - "add_ftrack_family": false, - "advanced_filtering": [ - { - "families": [ - "clip", - "review" - ], - "add_ftrack_family": true - } - ] - }, - { - "hosts": [ - "traypublisher" - ], - "families": [], - "task_types": [], - "tasks": [], - "add_ftrack_family": true, - "advanced_filtering": [] - }, - { - "hosts": [ - "traypublisher" - ], - "families": [ - "matchmove", - "shot" - ], - "task_types": [], - "tasks": [], - "add_ftrack_family": false, - "advanced_filtering": [] - }, - { - "hosts": [ - "traypublisher" - ], - "families": [ - "plate", - "review", - "audio" - ], - "task_types": [], - "tasks": [], - "add_ftrack_family": false, - "advanced_filtering": [ - { - "families": [ - "clip", - "review" - ], - "add_ftrack_family": true - } - ] - }, - { - "hosts": [ - "maya" - ], - "families": [ - "model", - "setdress", - "animation", - "look", - "rig", - "camera", - "renderlayer" - ], - "task_types": [], - "tasks": [], - "add_ftrack_family": true, - "advanced_filtering": [] - }, - { - "hosts": [ - "tvpaint" - ], - "families": [ - "renderPass" - ], - "task_types": [], - "tasks": [], - "add_ftrack_family": false, - "advanced_filtering": [] - }, - { - "hosts": [ - "tvpaint" - ], - "families": [], - "task_types": [], - "tasks": [], - "add_ftrack_family": true, - "advanced_filtering": [] - }, - { - "hosts": [ - "nuke" - ], - "families": [ - "write", - "render", - "prerender" - ], - "task_types": [], - "tasks": [], - "add_ftrack_family": false, - "advanced_filtering": [ - { - "families": [ - "review" - ], - "add_ftrack_family": true - } - ] - }, - { - "hosts": [ - "aftereffects" - ], - "families": [ - "render", - "workfile" - ], - "task_types": [], - "tasks": [], - "add_ftrack_family": true, - "advanced_filtering": [] - }, - { - "hosts": [ - "flame" - ], - "families": [ - "plate", - "take" - ], - "task_types": [], - "tasks": [], - "add_ftrack_family": true, - "advanced_filtering": [] - }, - { - "hosts": [ - "houdini" - ], - "families": [ - "usd" - ], - "task_types": [], - "tasks": [], - "add_ftrack_family": true, - "advanced_filtering": [] - }, - { - "hosts": [ - "photoshop" - ], - "families": [ - "review" - ], - "task_types": [], - "tasks": [], - "add_ftrack_family": true, - "advanced_filtering": [] - } - ] - }, - "CollectFtrackCustomAttributeData": { - "enabled": false, - "custom_attribute_keys": [] - }, - "IntegrateHierarchyToFtrack": { - "create_task_status_profiles": [] - }, - "IntegrateFtrackNote": { - "enabled": true, - "note_template": "{intent}: {comment}", - "note_labels": [] - }, - "IntegrateFtrackDescription": { - "enabled": false, - "optional": true, - "active": true, - "description_template": "{comment}" - }, - "ValidateFtrackAttributes": { - "enabled": false, - "ftrack_custom_attributes": {} - }, - "IntegrateFtrackComponentOverwrite": { - "enabled": true - }, - "IntegrateFtrackInstance": { - "family_mapping": { - "camera": "cam", - "look": "look", - "mayaAscii": "scene", - "model": "geo", - "rig": "rig", - "setdress": "setdress", - "pointcache": "cache", - "render": "render", - "prerender": "render", - "render2d": "render", - "nukescript": "comp", - "write": "render", - "review": "mov", - "plate": "img", - "audio": "audio", - "workfile": "scene", - "animation": "cache", - "image": "img", - "reference": "reference", - "ass": "cache", - "mayaScene": "scene", - "camerarig": "rig", - "yeticache": "cache", - "yetiRig": "rig", - "xgen": "xgen", - "rendersetup": "rendersetup", - "assembly": "assembly", - "layout": "layout", - "unrealStaticMesh": "geo", - "vrayproxy": "cache", - "redshiftproxy": "cache", - "usd": "usd" - }, - "keep_first_subset_name_for_review": true, - "asset_versions_status_profiles": [], - "additional_metadata_keys": [], - "upload_reviewable_with_origin_name": false - }, - "IntegrateFtrackFarmStatus": { - "farm_status_profiles": [ - { - "hosts": [ - "celaction" - ], - "task_types": [], - "task_names": [], - "families": [ - "render" - ], - "subsets": [], - "status_name": "Render" - } - ] - }, - "ftrack_task_status_local_publish": { - "status_profiles": [] - }, - "ftrack_task_status_on_farm_publish": { - "status_profiles": [] - }, - "IntegrateFtrackTaskStatus": { - "after_version_statuses": true - } - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/fusion.json b/client/ayon_core/settings/defaults/project_settings/fusion.json deleted file mode 100644 index f890f94b6f..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/fusion.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "imageio": { - "activate_host_color_management": true, - "ocio_config": { - "override_global_config": false, - "filepath": [] - }, - "file_rules": { - "activate_host_rules": false, - "rules": {} - } - }, - "copy_fusion_settings": { - "copy_path": "~/.openpype/hosts/fusion/profiles", - "copy_status": false, - "force_sync": false - }, - "hooks": { - "InstallPySideToFusion": { - "enabled": true - } - }, - "create": { - "CreateSaver": { - "temp_rendering_path_template": "{workdir}/renders/fusion/{subset}/{subset}.{frame}.{ext}", - "default_variants": [ - "Main", - "Mask" - ], - "instance_attributes": [ - "reviewable", - "farm_rendering" - ], - "image_format": "exr", - "default_frame_range_option": "asset_db" - }, - "CreateImageSaver": { - "temp_rendering_path_template": "{workdir}/renders/fusion/{subset}/{subset}.{ext}", - "default_variants": [ - "Main", - "Mask" - ], - "instance_attributes": [ - "reviewable", - "farm_rendering" - ], - "image_format": "exr", - "default_frame": 0 - } - }, - "publish": { - "ValidateSaverResolution": { - "enabled": true, - "optional": true, - "active": true - } - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/global.json b/client/ayon_core/settings/defaults/project_settings/global.json deleted file mode 100644 index 782fff1052..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/global.json +++ /dev/null @@ -1,580 +0,0 @@ -{ - "version_start_category": { - "profiles": [] - }, - "imageio": { - "activate_global_color_management": false, - "ocio_config": { - "filepath": [ - "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", - "{BUILTIN_OCIO_ROOT}/nuke-default/config.ocio" - ] - }, - "file_rules": { - "activate_global_file_rules": false, - "rules": { - "example": { - "pattern": ".*(beauty).*", - "colorspace": "ACES - ACEScg", - "ext": "exr" - } - } - } - }, - "publish": { - "CollectAnatomyInstanceData": { - "follow_workfile_version": false - }, - "CollectAudio": { - "enabled": false, - "audio_subset_name": "audioMain" - }, - "CollectSceneVersion": { - "hosts": [ - "aftereffects", - "blender", - "celaction", - "fusion", - "harmony", - "hiero", - "houdini", - "maya", - "nuke", - "photoshop", - "resolve", - "tvpaint" - ], - "skip_hosts_headless_publish": [] - }, - "collect_comment_per_instance": { - "enabled": false, - "families": [] - }, - "CollectFramesFixDef": { - "enabled": true, - "rewrite_version_enable": true - }, - "ValidateEditorialAssetName": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateVersion": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateIntent": { - "enabled": false, - "profiles": [] - }, - "ExtractThumbnail": { - "enabled": true, - "subsets": [], - "integrate_thumbnail": false, - "background_color": [ - 0, - 0, - 0, - 255 - ], - "duration_split": 0.5, - "target_size": { - "type": "resize", - "width": 1920, - "height": 1080 - }, - "oiiotool_defaults": { - "type": "colorspace", - "colorspace": "color_picking", - "view": "sRGB", - "display": "default" - }, - "ffmpeg_args": { - "input": [ - "-apply_trc gamma22" - ], - "output": [] - } - }, - "ExtractOIIOTranscode": { - "enabled": true, - "profiles": [] - }, - "ExtractReview": { - "enabled": true, - "profiles": [ - { - "families": [], - "hosts": [], - "outputs": { - "png": { - "ext": "png", - "tags": [ - "ftrackreview", - "kitsureview" - ], - "burnins": [], - "ffmpeg_args": { - "video_filters": [], - "audio_filters": [], - "input": [], - "output": [] - }, - "filter": { - "families": [ - "render", - "review", - "ftrack" - ], - "subsets": [], - "custom_tags": [], - "single_frame_filter": "single_frame" - }, - "overscan_crop": "", - "overscan_color": [ - 0, - 0, - 0, - 255 - ], - "width": 1920, - "height": 1080, - "scale_pixel_aspect": true, - "bg_color": [ - 0, - 0, - 0, - 0 - ], - "letter_box": { - "enabled": false, - "ratio": 0.0, - "fill_color": [ - 0, - 0, - 0, - 255 - ], - "line_thickness": 0, - "line_color": [ - 255, - 0, - 0, - 255 - ] - } - }, - "h264": { - "ext": "mp4", - "tags": [ - "burnin", - "ftrackreview", - "kitsureview" - ], - "burnins": [], - "ffmpeg_args": { - "video_filters": [], - "audio_filters": [], - "input": [ - "-apply_trc gamma22" - ], - "output": [ - "-pix_fmt yuv420p", - "-crf 18", - "-intra" - ] - }, - "filter": { - "families": [ - "render", - "review", - "ftrack" - ], - "subsets": [], - "custom_tags": [], - "single_frame_filter": "multi_frame" - }, - "overscan_crop": "", - "overscan_color": [ - 0, - 0, - 0, - 255 - ], - "width": 0, - "height": 0, - "scale_pixel_aspect": true, - "bg_color": [ - 0, - 0, - 0, - 0 - ], - "letter_box": { - "enabled": false, - "ratio": 0.0, - "fill_color": [ - 0, - 0, - 0, - 255 - ], - "line_thickness": 0, - "line_color": [ - 255, - 0, - 0, - 255 - ] - } - } - } - } - ] - }, - "ExtractBurnin": { - "enabled": true, - "options": { - "font_size": 42, - "font_color": [ - 255, - 255, - 255, - 255 - ], - "bg_color": [ - 0, - 0, - 0, - 127 - ], - "x_offset": 5, - "y_offset": 5, - "bg_padding": 5, - "font_filepath": { - "windows": "", - "darwin": "", - "linux": "" - } - }, - "profiles": [ - { - "families": [], - "hosts": [], - "task_types": [], - "task_names": [], - "subsets": [], - "burnins": { - "burnin": { - "TOP_LEFT": "{yy}-{mm}-{dd}", - "TOP_CENTERED": "", - "TOP_RIGHT": "{anatomy[version]}", - "BOTTOM_LEFT": "{username}", - "BOTTOM_CENTERED": "{asset}", - "BOTTOM_RIGHT": "{frame_start}-{current_frame}-{frame_end}", - "filter": { - "families": [], - "tags": [] - } - } - } - }, - { - "families": [ - "review" - ], - "hosts": [ - "maya", - "houdini", - "max" - ], - "task_types": [], - "task_names": [], - "subsets": [], - "burnins": { - "focal_length_burnin": { - "TOP_LEFT": "{yy}-{mm}-{dd}", - "TOP_CENTERED": "{focalLength:.2f} mm", - "TOP_RIGHT": "{anatomy[version]}", - "BOTTOM_LEFT": "{username}", - "BOTTOM_CENTERED": "{asset}", - "BOTTOM_RIGHT": "{frame_start}-{current_frame}-{frame_end}", - "filter": { - "families": [], - "tags": [] - } - } - } - } - ] - }, - "PreIntegrateThumbnails": { - "enabled": true, - "integrate_profiles": [] - }, - "IntegrateSubsetGroup": { - "subset_grouping_profiles": [ - { - "families": [], - "hosts": [], - "task_types": [], - "tasks": [], - "template": "" - } - ] - }, - "IntegrateHeroVersion": { - "enabled": true, - "optional": true, - "active": true, - "families": [ - "model", - "rig", - "look", - "pointcache", - "animation", - "setdress", - "layout", - "mayaScene" - ], - "template_name_profiles": [] - }, - "CleanUp": { - "paterns": [], - "remove_temp_renders": false - }, - "CleanUpFarm": { - "enabled": false - } - }, - "tools": { - "creator": { - "families_smart_select": { - "Render": [ - "light", - "render" - ], - "Model": [ - "model" - ], - "Layout": [ - "layout" - ], - "Look": [ - "look" - ], - "Rig": [ - "rigging", - "rig" - ] - }, - "subset_name_profiles": [ - { - "families": [], - "hosts": [], - "task_types": [], - "tasks": [], - "template": "{family}{variant}" - }, - { - "families": [ - "workfile" - ], - "hosts": [], - "task_types": [], - "tasks": [], - "template": "{family}{Task}" - }, - { - "families": [ - "render" - ], - "hosts": [], - "task_types": [], - "tasks": [], - "template": "{family}{Task}{Variant}" - }, - { - "families": [ - "renderLayer", - "renderPass" - ], - "hosts": [ - "tvpaint" - ], - "task_types": [], - "tasks": [], - "template": "{family}{Task}_{Renderlayer}_{Renderpass}" - }, - { - "families": [ - "review", - "workfile" - ], - "hosts": [ - "aftereffects", - "tvpaint" - ], - "task_types": [], - "tasks": [], - "template": "{family}{Task}" - }, - { - "families": [ - "render" - ], - "hosts": [ - "aftereffects" - ], - "task_types": [], - "tasks": [], - "template": "{family}{Task}{Composition}{Variant}" - }, - { - "families": [ - "staticMesh" - ], - "hosts": [ - "maya" - ], - "task_types": [], - "tasks": [], - "template": "S_{asset}{variant}" - }, - { - "families": [ - "skeletalMesh" - ], - "hosts": [ - "maya" - ], - "task_types": [], - "tasks": [], - "template": "SK_{asset}{variant}" - } - ] - }, - "Workfiles": { - "workfile_template_profiles": [ - { - "task_types": [], - "hosts": [], - "workfile_template": "work" - }, - { - "task_types": [], - "hosts": [ - "unreal" - ], - "workfile_template": "unreal" - } - ], - "last_workfile_on_startup": [ - { - "hosts": [], - "task_types": [], - "tasks": [], - "enabled": true, - "use_last_published_workfile": false - } - ], - "open_workfile_tool_on_startup": [ - { - "hosts": [], - "task_types": [], - "tasks": [], - "enabled": false - } - ], - "extra_folders": [], - "workfile_lock_profiles": [] - }, - "loader": { - "family_filter_profiles": [ - { - "hosts": [], - "task_types": [], - "is_include": true, - "filter_families": [] - } - ] - }, - "publish": { - "template_name_profiles": [ - { - "families": [], - "hosts": [], - "task_types": [], - "task_names": [], - "template_name": "publish" - }, - { - "families": [ - "review", - "render", - "prerender" - ], - "hosts": [], - "task_types": [], - "task_names": [], - "template_name": "render" - }, - { - "families": [ - "staticMesh", - "skeletalMesh" - ], - "hosts": [ - "maya" - ], - "task_types": [], - "task_names": [], - "template_name": "maya2unreal" - }, - { - "families": [ - "online" - ], - "hosts": [ - "traypublisher" - ], - "task_types": [], - "task_names": [], - "template_name": "online" - }, - { - "families": [ - "tycache" - ], - "hosts": [ - "max" - ], - "task_types": [], - "task_names": [], - "template_name": "tycache" - } - ], - "hero_template_name_profiles": [], - "custom_staging_dir_profiles": [] - } - }, - "project_folder_structure": "{\"__project_root__\": {\"prod\": {}, \"resources\": {\"footage\": {\"plates\": {}, \"offline\": {}}, \"audio\": {}, \"art_dept\": {}}, \"editorial\": {}, \"assets\": {\"characters\": {}, \"locations\": {}}, \"shots\": {}}}", - "sync_server": { - "enabled": false, - "config": { - "retry_cnt": "3", - "loop_delay": "60", - "always_accessible_on": [], - "active_site": "studio", - "remote_site": "studio" - }, - "sites": {} - }, - "project_plugins": { - "windows": [], - "darwin": [], - "linux": [] - }, - "project_environments": {} -} diff --git a/client/ayon_core/settings/defaults/project_settings/harmony.json b/client/ayon_core/settings/defaults/project_settings/harmony.json deleted file mode 100644 index b424b43cc1..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/harmony.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "imageio": { - "activate_host_color_management": true, - "ocio_config": { - "override_global_config": false, - "filepath": [] - }, - "file_rules": { - "activate_host_rules": false, - "rules": {} - } - }, - "publish": { - "CollectPalettes": { - "allowed_tasks": [ - ".*" - ] - }, - "ValidateAudio": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateContainers": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateSceneSettings": { - "enabled": true, - "optional": true, - "active": true, - "frame_check_filter": [], - "skip_resolution_check": [], - "skip_timelines_check": [] - } - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/hiero.json b/client/ayon_core/settings/defaults/project_settings/hiero.json deleted file mode 100644 index 9c83733b09..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/hiero.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "imageio": { - "activate_host_color_management": true, - "ocio_config": { - "override_global_config": false, - "filepath": [] - }, - "file_rules": { - "activate_host_rules": false, - "rules": {} - }, - "workfile": { - "ocioConfigName": "nuke-default", - "workingSpace": "linear", - "sixteenBitLut": "sRGB", - "eightBitLut": "sRGB", - "floatLut": "linear", - "logLut": "Cineon", - "viewerLut": "sRGB", - "thumbnailLut": "sRGB", - "monitorOutLut": "sRGB" - }, - "regexInputs": { - "inputs": [ - { - "regex": "[^-a-zA-Z0-9](plateRef).*(?=mp4)", - "colorspace": "sRGB" - } - ] - } - }, - "create": { - "CreateShotClip": { - "hierarchy": "{folder}/{sequence}", - "clipRename": true, - "clipName": "{track}{sequence}{shot}", - "countFrom": 10, - "countSteps": 10, - "folder": "shots", - "episode": "ep01", - "sequence": "sq01", - "track": "{_track_}", - "shot": "sh###", - "vSyncOn": false, - "workfileFrameStart": 1001, - "handleStart": 10, - "handleEnd": 10 - } - }, - "load": { - "LoadClip": { - "enabled": true, - "families": [ - "render2d", - "source", - "plate", - "render", - "review" - ], - "clip_name_template": "{asset}_{subset}_{representation}" - } - }, - "publish": { - "CollectInstanceVersion": { - "enabled": false - }, - "ExtractReviewCutUpVideo": { - "enabled": true, - "tags_addition": [ - "review" - ] - } - }, - "filters": {}, - "scriptsmenu": { - "name": "OpenPype Tools", - "definition": [ - { - "type": "action", - "sourcetype": "python", - "title": "OpenPype Docs", - "command": "import webbrowser;webbrowser.open(url='https://openpype.io/docs/artist_hosts_hiero')", - "tooltip": "Open the OpenPype Hiero user doc page" - } - ] - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/houdini.json b/client/ayon_core/settings/defaults/project_settings/houdini.json deleted file mode 100644 index 813e7153ea..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/houdini.json +++ /dev/null @@ -1,192 +0,0 @@ -{ - "general": { - "add_self_publish_button": false, - "update_houdini_var_context": { - "enabled": true, - "houdini_vars":[ - { - "var": "JOB", - "value": "{root[work]}/{project[name]}/{hierarchy}/{asset}/work/{task[name]}", - "is_directory": true - } - ] - } - }, - "imageio": { - "activate_host_color_management": true, - "ocio_config": { - "override_global_config": false, - "filepath": [] - }, - "file_rules": { - "activate_host_rules": false, - "rules": {} - } - }, - "shelves": [], - "create": { - "CreateAlembicCamera": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateArnoldAss": { - "enabled": true, - "default_variants": [ - "Main" - ], - "ext": ".ass" - }, - "CreateArnoldRop": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateCompositeSequence": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateHDA": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateKarmaROP": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateMantraIFD": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateMantraROP": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreatePointCache": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateBGEO": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateRedshiftProxy": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateRedshiftROP": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateReview": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateStaticMesh": { - "enabled": true, - "default_variants": [ - "Main" - ], - "static_mesh_prefix": "S", - "collision_prefixes": [ - "UBX", - "UCP", - "USP", - "UCX" - ] - }, - "CreateUSD": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateUSDRender": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateVDBCache": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateVrayROP": { - "enabled": true, - "default_variants": [ - "Main" - ] - } - }, - "publish": { - "CollectAssetHandles": { - "use_asset_handles": true - }, - "CollectChunkSize": { - "enabled": true, - "optional": true, - "chunk_size": 999999 - }, - "ValidateContainers": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateMeshIsStatic": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateReviewColorspace": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateSubsetName": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateUnrealStaticMeshName": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateWorkfilePaths": { - "enabled": true, - "optional": true, - "node_types": [ - "file", - "alembic" - ], - "prohibited_vars": [ - "$HIP", - "$JOB" - ] - } - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/kitsu.json b/client/ayon_core/settings/defaults/project_settings/kitsu.json deleted file mode 100644 index 59a36d8b97..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/kitsu.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "entities_naming_pattern": { - "episode": "E##", - "sequence": "SQ##", - "shot": "SH##" - }, - "publish": { - "IntegrateKitsuNote": { - "set_status_note": false, - "note_status_shortname": "wfa", - "status_change_conditions": { - "status_conditions": [], - "family_requirements": [] - }, - "custom_comment_template": { - "enabled": false, - "comment_template": "{comment}\n\n| | |\n|--|--|\n| version| `{version}` |\n| family | `{family}` |\n| name | `{name}` |" - } - } - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/max.json b/client/ayon_core/settings/defaults/project_settings/max.json deleted file mode 100644 index a0a4fcf83d..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/max.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "unit_scale_settings": { - "enabled": true, - "scene_unit_scale": "Meters" - }, - "imageio": { - "activate_host_color_management": true, - "ocio_config": { - "override_global_config": false, - "filepath": [] - }, - "file_rules": { - "activate_host_rules": false, - "rules": {} - } - }, - "RenderSettings": { - "default_render_image_folder": "renders/3dsmax", - "aov_separator": "underscore", - "image_format": "exr", - "multipass": true - }, - "CreateReview": { - "review_width": 1920, - "review_height": 1080, - "percentSize": 100.0, - "keep_images": false, - "image_format": "png", - "visual_style": "Realistic", - "viewport_preset": "Quality", - "anti_aliasing": "None", - "vp_texture": true - }, - "PointCloud": { - "attribute": { - "Age": "age", - "Radius": "radius", - "Position": "position", - "Rotation": "rotation", - "Scale": "scale", - "Velocity": "velocity", - "Color": "color", - "TextureCoordinate": "texcoord", - "MaterialID": "matid", - "custFloats": "custFloats", - "custVecs": "custVecs" - } - }, - "publish": { - "ValidateFrameRange": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateAttributes": { - "enabled": false, - "attributes": {} - }, - "ValidateCameraAttributes": { - "enabled": true, - "optional": true, - "active": false, - "fov": 45.0, - "nearrange": 0.0, - "farrange": 1000.0, - "nearclip": 1.0, - "farclip": 1000.0 - }, - "ValidateLoadedPlugin": { - "enabled": false, - "optional": true, - "family_plugins_mapping": [] - }, - "ExtractModelObj": { - "enabled": true, - "optional": true, - "active": false - }, - "ExtractModelFbx": { - "enabled": true, - "optional": true, - "active": false - }, - "ExtractModelUSD": { - "enabled": true, - "optional": true, - "active": false - }, - "ExtractModel": { - "enabled": true, - "optional": true, - "active": true - }, - "ExtractMaxSceneRaw": { - "enabled": true, - "optional": true, - "active": true - } - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/maya.json b/client/ayon_core/settings/defaults/project_settings/maya.json deleted file mode 100644 index b2dc0ccd65..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/maya.json +++ /dev/null @@ -1,1613 +0,0 @@ -{ - "open_workfile_post_initialization": false, - "explicit_plugins_loading": { - "enabled": false, - "plugins_to_load": [ - { - "enabled": false, - "name": "AbcBullet" - }, - { - "enabled": true, - "name": "AbcExport" - }, - { - "enabled": true, - "name": "AbcImport" - }, - { - "enabled": false, - "name": "animImportExport" - }, - { - "enabled": false, - "name": "ArubaTessellator" - }, - { - "enabled": false, - "name": "ATFPlugin" - }, - { - "enabled": false, - "name": "atomImportExport" - }, - { - "enabled": false, - "name": "AutodeskPacketFile" - }, - { - "enabled": false, - "name": "autoLoader" - }, - { - "enabled": false, - "name": "bifmeshio" - }, - { - "enabled": false, - "name": "bifrostGraph" - }, - { - "enabled": false, - "name": "bifrostshellnode" - }, - { - "enabled": false, - "name": "bifrostvisplugin" - }, - { - "enabled": false, - "name": "blast2Cmd" - }, - { - "enabled": false, - "name": "bluePencil" - }, - { - "enabled": false, - "name": "Boss" - }, - { - "enabled": false, - "name": "bullet" - }, - { - "enabled": true, - "name": "cacheEvaluator" - }, - { - "enabled": false, - "name": "cgfxShader" - }, - { - "enabled": false, - "name": "cleanPerFaceAssignment" - }, - { - "enabled": false, - "name": "clearcoat" - }, - { - "enabled": false, - "name": "convertToComponentTags" - }, - { - "enabled": false, - "name": "curveWarp" - }, - { - "enabled": false, - "name": "ddsFloatReader" - }, - { - "enabled": true, - "name": "deformerEvaluator" - }, - { - "enabled": false, - "name": "dgProfiler" - }, - { - "enabled": false, - "name": "drawUfe" - }, - { - "enabled": false, - "name": "dx11Shader" - }, - { - "enabled": false, - "name": "fbxmaya" - }, - { - "enabled": false, - "name": "fltTranslator" - }, - { - "enabled": false, - "name": "freeze" - }, - { - "enabled": false, - "name": "Fur" - }, - { - "enabled": false, - "name": "gameFbxExporter" - }, - { - "enabled": false, - "name": "gameInputDevice" - }, - { - "enabled": false, - "name": "GamePipeline" - }, - { - "enabled": false, - "name": "gameVertexCount" - }, - { - "enabled": false, - "name": "geometryReport" - }, - { - "enabled": false, - "name": "geometryTools" - }, - { - "enabled": false, - "name": "glslShader" - }, - { - "enabled": true, - "name": "GPUBuiltInDeformer" - }, - { - "enabled": false, - "name": "gpuCache" - }, - { - "enabled": false, - "name": "hairPhysicalShader" - }, - { - "enabled": false, - "name": "ik2Bsolver" - }, - { - "enabled": false, - "name": "ikSpringSolver" - }, - { - "enabled": false, - "name": "invertShape" - }, - { - "enabled": false, - "name": "lges" - }, - { - "enabled": false, - "name": "lookdevKit" - }, - { - "enabled": false, - "name": "MASH" - }, - { - "enabled": false, - "name": "matrixNodes" - }, - { - "enabled": false, - "name": "mayaCharacterization" - }, - { - "enabled": false, - "name": "mayaHIK" - }, - { - "enabled": false, - "name": "MayaMuscle" - }, - { - "enabled": false, - "name": "mayaUsdPlugin" - }, - { - "enabled": false, - "name": "mayaVnnPlugin" - }, - { - "enabled": false, - "name": "melProfiler" - }, - { - "enabled": false, - "name": "meshReorder" - }, - { - "enabled": true, - "name": "modelingToolkit" - }, - { - "enabled": false, - "name": "mtoa" - }, - { - "enabled": false, - "name": "mtoh" - }, - { - "enabled": false, - "name": "nearestPointOnMesh" - }, - { - "enabled": true, - "name": "objExport" - }, - { - "enabled": false, - "name": "OneClick" - }, - { - "enabled": false, - "name": "OpenEXRLoader" - }, - { - "enabled": false, - "name": "pgYetiMaya" - }, - { - "enabled": false, - "name": "pgyetiVrayMaya" - }, - { - "enabled": false, - "name": "polyBoolean" - }, - { - "enabled": false, - "name": "poseInterpolator" - }, - { - "enabled": false, - "name": "quatNodes" - }, - { - "enabled": false, - "name": "randomizerDevice" - }, - { - "enabled": false, - "name": "redshift4maya" - }, - { - "enabled": true, - "name": "renderSetup" - }, - { - "enabled": false, - "name": "retargeterNodes" - }, - { - "enabled": false, - "name": "RokokoMotionLibrary" - }, - { - "enabled": false, - "name": "rotateHelper" - }, - { - "enabled": false, - "name": "sceneAssembly" - }, - { - "enabled": false, - "name": "shaderFXPlugin" - }, - { - "enabled": false, - "name": "shotCamera" - }, - { - "enabled": false, - "name": "snapTransform" - }, - { - "enabled": false, - "name": "stage" - }, - { - "enabled": true, - "name": "stereoCamera" - }, - { - "enabled": false, - "name": "stlTranslator" - }, - { - "enabled": false, - "name": "studioImport" - }, - { - "enabled": false, - "name": "Substance" - }, - { - "enabled": false, - "name": "substancelink" - }, - { - "enabled": false, - "name": "substancemaya" - }, - { - "enabled": false, - "name": "substanceworkflow" - }, - { - "enabled": false, - "name": "svgFileTranslator" - }, - { - "enabled": false, - "name": "sweep" - }, - { - "enabled": false, - "name": "testify" - }, - { - "enabled": false, - "name": "tiffFloatReader" - }, - { - "enabled": false, - "name": "timeSliderBookmark" - }, - { - "enabled": false, - "name": "Turtle" - }, - { - "enabled": false, - "name": "Type" - }, - { - "enabled": false, - "name": "udpDevice" - }, - { - "enabled": false, - "name": "ufeSupport" - }, - { - "enabled": false, - "name": "Unfold3D" - }, - { - "enabled": false, - "name": "VectorRender" - }, - { - "enabled": false, - "name": "vrayformaya" - }, - { - "enabled": false, - "name": "vrayvolumegrid" - }, - { - "enabled": false, - "name": "xgenToolkit" - }, - { - "enabled": false, - "name": "xgenVray" - } - ] - }, - "imageio": { - "activate_host_color_management": true, - "ocio_config": { - "override_global_config": false, - "filepath": [] - }, - "file_rules": { - "activate_host_rules": false, - "rules": {} - }, - "workfile": { - "enabled": false, - "renderSpace": "ACES - ACEScg", - "displayName": "ACES", - "viewName": "sRGB" - }, - "colorManagementPreference_v2": { - "enabled": true, - "renderSpace": "ACEScg", - "displayName": "sRGB", - "viewName": "ACES 1.0 SDR-video" - }, - "colorManagementPreference": { - "renderSpace": "scene-linear Rec 709/sRGB", - "viewTransform": "sRGB gamma" - } - }, - "mel_workspace": "workspace -fr \"shaders\" \"renderData/shaders\";\nworkspace -fr \"images\" \"renders/maya\";\nworkspace -fr \"particles\" \"particles\";\nworkspace -fr \"mayaAscii\" \"\";\nworkspace -fr \"mayaBinary\" \"\";\nworkspace -fr \"scene\" \"\";\nworkspace -fr \"alembicCache\" \"cache/alembic\";\nworkspace -fr \"renderData\" \"renderData\";\nworkspace -fr \"sourceImages\" \"sourceimages\";\nworkspace -fr \"fileCache\" \"cache/nCache\";\nworkspace -fr \"autoSave\" \"autosave\";", - "ext_mapping": { - "model": "ma", - "mayaAscii": "ma", - "camera": "ma", - "rig": "ma", - "workfile": "ma", - "yetiRig": "ma" - }, - "maya-dirmap": { - "use_env_var_as_root": false, - "enabled": false, - "paths": { - "source-path": [], - "destination-path": [] - } - }, - "include_handles": { - "include_handles_default": false, - "per_task_type": [] - }, - "scriptsmenu": { - "name": "OpenPype Tools", - "definition": [ - { - "type": "action", - "command": "import ayon_core.hosts.maya.api.commands as op_cmds; op_cmds.edit_shader_definitions()", - "sourcetype": "python", - "title": "Edit shader name definitions", - "tooltip": "Edit shader name definitions used in validation and renaming.", - "tags": [ - "pipeline", - "shader" - ] - } - ] - }, - "RenderSettings": { - "apply_render_settings": true, - "default_render_image_folder": "renders/maya", - "enable_all_lights": true, - "aov_separator": "underscore", - "remove_aovs": false, - "reset_current_frame": false, - "arnold_renderer": { - "image_prefix": "//_", - "image_format": "exr", - "multilayer_exr": true, - "tiled": true, - "aov_list": [], - "additional_options": [] - }, - "vray_renderer": { - "image_prefix": "//", - "engine": "1", - "image_format": "exr", - "aov_list": [], - "additional_options": [] - }, - "redshift_renderer": { - "image_prefix": "//", - "primary_gi_engine": "0", - "secondary_gi_engine": "0", - "image_format": "exr", - "multilayer_exr": true, - "force_combine": true, - "aov_list": [], - "additional_options": [] - }, - "renderman_renderer": { - "image_prefix": "{aov_separator}..", - "image_dir": "/", - "display_filters": [], - "imageDisplay_dir": "/{aov_separator}imageDisplayFilter..", - "sample_filters": [], - "cryptomatte_dir": "/{aov_separator}cryptomatte..", - "watermark_dir": "/{aov_separator}watermarkFilter..", - "additional_options": [] - } - }, - "create": { - "CreateLook": { - "enabled": true, - "make_tx": true, - "rs_tex": false, - "default_variants": [ - "Main" - ] - }, - "CreateRender": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateUnrealStaticMesh": { - "enabled": true, - "default_variants": [ - "", - "_Main" - ], - "static_mesh_prefix": "S", - "collision_prefixes": [ - "UBX", - "UCP", - "USP", - "UCX" - ] - }, - "CreateUnrealSkeletalMesh": { - "enabled": true, - "default_variants": [ - "Main" - ], - "joint_hints": "jnt_org" - }, - "CreateMultiverseLook": { - "enabled": true, - "publish_mip_map": true - }, - "CreateAnimation": { - "write_color_sets": false, - "write_face_sets": false, - "include_parent_hierarchy": false, - "include_user_defined_attributes": false, - "default_variants": [ - "Main" - ] - }, - "CreateModel": { - "enabled": true, - "write_color_sets": false, - "write_face_sets": false, - "default_variants": [ - "Main", - "Proxy", - "Sculpt" - ] - }, - "CreatePointCache": { - "enabled": true, - "write_color_sets": false, - "write_face_sets": false, - "include_user_defined_attributes": false, - "default_variants": [ - "Main" - ] - }, - "CreateProxyAlembic": { - "enabled": true, - "write_color_sets": false, - "write_face_sets": false, - "default_variants": [ - "Main" - ] - }, - "CreateReview": { - "enabled": true, - "default_variants": [ - "Main" - ], - "useMayaTimeline": true - }, - "CreateAss": { - "enabled": true, - "default_variants": [ - "Main" - ], - "expandProcedurals": false, - "motionBlur": true, - "motionBlurKeys": 2, - "motionBlurLength": 0.5, - "maskOptions": false, - "maskCamera": false, - "maskLight": false, - "maskShape": false, - "maskShader": false, - "maskOverride": false, - "maskDriver": false, - "maskFilter": false, - "maskOperator": false, - "maskColor_manager": false - }, - "CreateVrayProxy": { - "enabled": true, - "vrmesh": true, - "alembic": true, - "default_variants": [ - "Main" - ] - }, - "CreateMultiverseUsd": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateMultiverseUsdComp": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateMultiverseUsdOver": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateAssembly": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateCamera": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateLayout": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateMayaScene": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateRenderSetup": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateRig": { - "enabled": true, - "default_variants": [ - "Main", - "Sim", - "Cloth" - ] - }, - "CreateSetDress": { - "enabled": true, - "default_variants": [ - "Main", - "Anim" - ] - }, - "CreateVRayScene": { - "enabled": true, - "default_variants": [ - "Main" - ] - }, - "CreateYetiRig": { - "enabled": true, - "default_variants": [ - "Main" - ] - } - }, - "publish": { - "CollectMayaRender": { - "sync_workfile_version": false - }, - "CollectFbxAnimation": { - "enabled": true - }, - "CollectFbxCamera": { - "enabled": false - }, - "CollectGLTF": { - "enabled": false - }, - "ValidateInstanceInContext": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateContainers": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateFrameRange": { - "enabled": true, - "optional": true, - "active": true, - "exclude_families": [ - "model", - "rig", - "staticMesh" - ] - }, - "ValidateShaderName": { - "enabled": false, - "optional": true, - "active": true, - "regex": "(?P.*)_(.*)_SHD" - }, - "ValidateShadingEngine": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateMayaColorSpace": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateAttributes": { - "enabled": false, - "attributes": {} - }, - "ValidateLoadedPlugin": { - "enabled": false, - "optional": true, - "whitelist_native_plugins": false, - "authorized_plugins": [] - }, - "ValidateMayaUnits": { - "enabled": true, - "optional": false, - "validate_linear_units": true, - "linear_units": "cm", - "validate_angular_units": true, - "angular_units": "deg", - "validate_fps": true - }, - "ValidateUnrealStaticMeshName": { - "enabled": true, - "optional": true, - "validate_mesh": false, - "validate_collision": true - }, - "ValidateCycleError": { - "enabled": true, - "optional": false, - "families": [ - "rig" - ] - }, - "ValidatePluginPathAttributes": { - "enabled": true, - "optional": false, - "active": true, - "attribute": { - "AlembicNode": "abc_File", - "VRayProxy": "fileName", - "RenderManArchive": "filename", - "pgYetiMaya": "cacheFileName", - "aiStandIn": "dso", - "RedshiftSprite": "tex0", - "RedshiftBokeh": "dofBokehImage", - "RedshiftCameraMap": "tex0", - "RedshiftEnvironment": "tex2", - "RedshiftDomeLight": "tex1", - "RedshiftIESLight": "profile", - "RedshiftLightGobo": "tex0", - "RedshiftNormalMap": "tex0", - "RedshiftProxyMesh": "fileName", - "RedshiftVolumeShape": "fileName", - "VRayTexGLSL": "fileName", - "VRayMtlGLSL": "fileName", - "VRayVRmatMtl": "fileName", - "VRayPtex": "ptexFile", - "VRayLightIESShape": "iesFile", - "VRayMesh": "materialAssignmentsFile", - "VRayMtlOSL": "fileName", - "VRayTexOSL": "fileName", - "VRayTexOCIO": "ocioConfigFile", - "VRaySettingsNode": "pmap_autoSaveFile2", - "VRayScannedMtl": "file", - "VRayScene": "parameterOverrideFilePath", - "VRayMtlMDL": "filename", - "VRaySimbiont": "file", - "dlOpenVDBShape": "filename", - "pgYetiMayaShape": "liveABCFilename", - "gpuCache": "cacheFileName" - } - }, - "ValidateRenderSettings": { - "arnold_render_attributes": [], - "vray_render_attributes": [], - "redshift_render_attributes": [], - "renderman_render_attributes": [] - }, - "ValidateResolution": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateCurrentRenderLayerIsRenderable": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateGLSLMaterial": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateGLSLPlugin": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateRenderImageRule": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateRenderNoDefaultCameras": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateRenderSingleCamera": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateRenderLayerAOVs": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateStepSize": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateVRayDistributedRendering": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateVrayReferencedAOVs": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateVRayTranslatorEnabled": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateVrayProxy": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateVrayProxyMembers": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateYetiRenderScriptCallbacks": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateYetiRigCacheState": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateYetiRigInputShapesInInstance": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateYetiRigSettings": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateModelName": { - "enabled": false, - "database": true, - "material_file": { - "windows": "", - "darwin": "", - "linux": "" - }, - "regex": "(.*)_(\\d)*_(?P.*)_(GEO)", - "top_level_regex": ".*_GRP" - }, - "ValidateModelContent": { - "enabled": true, - "optional": false, - "validate_top_group": true - }, - "ValidateTransformNamingSuffix": { - "enabled": true, - "optional": true, - "SUFFIX_NAMING_TABLE": { - "mesh": [ - "_GEO", - "_GES", - "_GEP", - "_OSD" - ], - "nurbsCurve": [ - "_CRV" - ], - "nurbsSurface": [ - "_NRB" - ], - "locator": [ - "_LOC" - ], - "group": [ - "_GRP" - ] - }, - "ALLOW_IF_NOT_IN_SUFFIX_TABLE": true - }, - "ValidateColorSets": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateMeshHasOverlappingUVs": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateMeshArnoldAttributes": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateMeshShaderConnections": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateMeshSingleUVSet": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateMeshHasUVs": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateMeshLaminaFaces": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateMeshNgons": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateMeshNonManifold": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateMeshNoNegativeScale": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateMeshNonZeroEdgeLength": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateMeshNormalsUnlocked": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateMeshUVSetMap1": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateMeshVerticesHaveEdges": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateNoAnimation": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateNoNamespace": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateNoNullTransforms": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateNoUnknownNodes": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateNodeNoGhosting": { - "enabled": false, - "optional": false, - "active": true - }, - "ValidateShapeDefaultNames": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateShapeRenderStats": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateShapeZero": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateTransformZero": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateUniqueNames": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateNoVRayMesh": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateUnrealMeshTriangulated": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateAlembicVisibleOnly": { - "enabled": true, - "optional": false, - "active": true - }, - "ExtractProxyAlembic": { - "enabled": true, - "families": [ - "proxyAbc" - ] - }, - "ExtractAlembic": { - "enabled": true, - "families": [ - "pointcache", - "model", - "vrayproxy.alembic" - ] - }, - "ExtractObj": { - "enabled": false, - "optional": true - }, - "ValidateRigContents": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateRigJointsHidden": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateRigControllers": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateAnimatedReferenceRig": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateAnimationContent": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateOutRelatedNodeIds": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateRigControllersArnoldAttributes": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateSkeletalMeshHierarchy": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateSkeletonRigContents": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateSkeletonRigControllers": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateSkinclusterDeformerSet": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateRigOutSetNodeIds": { - "enabled": true, - "optional": false, - "allow_history_only": false - }, - "ValidateSkeletonRigOutSetNodeIds": { - "enabled": false, - "optional": false, - "allow_history_only": false - }, - "ValidateSkeletonRigOutputIds": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateSkeletonTopGroupHierarchy": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateCameraAttributes": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateAssemblyName": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateAssemblyNamespaces": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateAssemblyModelTransforms": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateAssRelativePaths": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateInstancerContent": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateInstancerFrameRanges": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateNoDefaultCameras": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateUnrealUpAxis": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateCameraContents": { - "enabled": true, - "optional": false, - "validate_shapes": true - }, - "ExtractPlayblast": { - "capture_preset": { - "Codec": { - "compression": "png", - "format": "image", - "quality": 95 - }, - "Display Options": { - "override_display": true, - "background": [ - 125, - 125, - 125, - 255 - ], - "backgroundBottom": [ - 125, - 125, - 125, - 255 - ], - "backgroundTop": [ - 125, - 125, - 125, - 255 - ], - "displayGradient": true - }, - "Generic": { - "isolate_view": true, - "off_screen": true, - "pan_zoom": false - }, - "Renderer": { - "rendererName": "vp2Renderer" - }, - "Resolution": { - "width": 1920, - "height": 1080 - }, - "Viewport Options": { - "override_viewport_options": true, - "displayLights": "default", - "displayTextures": true, - "textureMaxResolution": 1024, - "renderDepthOfField": true, - "shadows": true, - "twoSidedLighting": true, - "lineAAEnable": true, - "multiSample": 8, - "loadTextures": false, - "useDefaultMaterial": false, - "wireframeOnShaded": false, - "xray": false, - "jointXray": false, - "backfaceCulling": false, - "ssaoEnable": false, - "ssaoAmount": 1, - "ssaoRadius": 16, - "ssaoFilterRadius": 16, - "ssaoSamples": 16, - "fogging": false, - "hwFogFalloff": "0", - "hwFogDensity": 0.0, - "hwFogStart": 0, - "hwFogEnd": 100, - "hwFogAlpha": 0, - "hwFogColorR": 1.0, - "hwFogColorG": 1.0, - "hwFogColorB": 1.0, - "motionBlurEnable": false, - "motionBlurSampleCount": 8, - "motionBlurShutterOpenFraction": 0.2, - "cameras": false, - "clipGhosts": false, - "deformers": false, - "dimensions": false, - "dynamicConstraints": false, - "dynamics": false, - "fluids": false, - "follicles": false, - "greasePencils": false, - "grid": false, - "hairSystems": true, - "handles": false, - "headsUpDisplay": false, - "ikHandles": false, - "imagePlane": true, - "joints": false, - "lights": false, - "locators": false, - "manipulators": false, - "motionTrails": false, - "nCloths": false, - "nParticles": false, - "nRigids": false, - "controlVertices": false, - "nurbsCurves": false, - "hulls": false, - "nurbsSurfaces": false, - "particleInstancers": false, - "pivots": false, - "planes": false, - "pluginShapes": false, - "polymeshes": true, - "strokes": false, - "subdivSurfaces": false, - "textures": false, - "pluginObjects": { - "gpuCacheDisplayFilter": false - } - }, - "Camera Options": { - "displayGateMask": false, - "displayResolution": false, - "displayFilmGate": false, - "displayFieldChart": false, - "displaySafeAction": false, - "displaySafeTitle": false, - "displayFilmPivot": false, - "displayFilmOrigin": false, - "overscan": 1.0 - } - }, - "profiles": [] - }, - "ExtractMayaSceneRaw": { - "enabled": true, - "add_for_families": [ - "layout" - ] - }, - "ExtractCameraAlembic": { - "enabled": true, - "optional": true, - "active": true, - "bake_attributes": [] - }, - "ExtractCameraMayaScene": { - "enabled": true, - "optional": true, - "active": true, - "keep_image_planes": false - }, - "ExtractGLB": { - "enabled": true, - "active": true, - "ogsfx_path": "/maya2glTF/PBR/shaders/glTF_PBR.ogsfx" - }, - "ExtractLook": { - "maketx_arguments": [] - }, - "ExtractGPUCache": { - "enabled": false, - "families": [ - "model", - "animation", - "pointcache" - ], - "step": 1.0, - "stepSave": 1, - "optimize": true, - "optimizationThreshold": 40000, - "optimizeAnimationsForMotionBlur": true, - "writeMaterials": true, - "useBaseTessellation": true - } - }, - "load": { - "colors": { - "model": [ - 209, - 132, - 30, - 255 - ], - "rig": [ - 59, - 226, - 235, - 255 - ], - "pointcache": [ - 94, - 209, - 30, - 255 - ], - "animation": [ - 94, - 209, - 30, - 255 - ], - "ass": [ - 249, - 135, - 53, - 255 - ], - "camera": [ - 136, - 114, - 244, - 255 - ], - "fbx": [ - 215, - 166, - 255, - 255 - ], - "mayaAscii": [ - 67, - 174, - 255, - 255 - ], - "mayaScene": [ - 67, - 174, - 255, - 255 - ], - "setdress": [ - 255, - 250, - 90, - 255 - ], - "layout": [ - 255, - 250, - 90, - 255 - ], - "vdbcache": [ - 249, - 54, - 0, - 255 - ], - "vrayproxy": [ - 255, - 150, - 12, - 255 - ], - "vrayscene_layer": [ - 255, - 150, - 12, - 255 - ], - "yeticache": [ - 99, - 206, - 220, - 255 - ], - "yetiRig": [ - 0, - 205, - 125, - 255 - ] - }, - "reference_loader": { - "namespace": "{asset_name}_{subset}_##_", - "group_name": "_GRP", - "display_handle": true - }, - "import_loader": { - "namespace": "{asset_name}_{subset}_##_", - "group_name": "_GRP" - } - }, - "workfile_build": { - "profiles": [ - { - "task_types": [], - "tasks": [ - "Lighting" - ], - "current_context": [ - { - "subset_name_filters": [ - ".+[Mm]ain" - ], - "families": [ - "model" - ], - "repre_names": [ - "abc", - "ma" - ], - "loaders": [ - "ReferenceLoader" - ] - }, - { - "subset_name_filters": [], - "families": [ - "animation", - "pointcache", - "proxyAbc" - ], - "repre_names": [ - "abc" - ], - "loaders": [ - "ReferenceLoader" - ] - }, - { - "subset_name_filters": [], - "families": [ - "rendersetup" - ], - "repre_names": [ - "json" - ], - "loaders": [ - "RenderSetupLoader" - ] - }, - { - "subset_name_filters": [], - "families": [ - "camera" - ], - "repre_names": [ - "abc" - ], - "loaders": [ - "ReferenceLoader" - ] - } - ], - "linked_assets": [ - { - "subset_name_filters": [], - "families": [ - "sedress" - ], - "repre_names": [ - "ma" - ], - "loaders": [ - "ReferenceLoader" - ] - }, - { - "subset_name_filters": [], - "families": [ - "ArnoldStandin" - ], - "repre_names": [ - "ass" - ], - "loaders": [ - "assLoader" - ] - } - ] - } - ] - }, - "templated_workfile_build": { - "profiles": [] - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/nuke.json b/client/ayon_core/settings/defaults/project_settings/nuke.json deleted file mode 100644 index 11b2988c67..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/nuke.json +++ /dev/null @@ -1,544 +0,0 @@ -{ - "general": { - "menu": { - "create": "ctrl+alt+c", - "publish": "ctrl+alt+p", - "load": "ctrl+alt+l", - "manage": "ctrl+alt+m", - "build_workfile": "ctrl+alt+b" - } - }, - "imageio": { - "activate_host_color_management": true, - "ocio_config": { - "override_global_config": false, - "filepath": [] - }, - "file_rules": { - "activate_host_rules": false, - "rules": {} - }, - "viewer": { - "viewerProcess": "sRGB (default)" - }, - "baking": { - "viewerProcess": "rec709 (default)" - }, - "workfile": { - "colorManagement": "OCIO", - "OCIO_config": "nuke-default", - "workingSpaceLUT": "scene_linear", - "monitorLut": "sRGB (default)" - }, - "nodes": { - "requiredNodes": [ - { - "plugins": [ - "CreateWriteRender" - ], - "nukeNodeClass": "Write", - "knobs": [ - { - "type": "text", - "name": "file_type", - "value": "exr" - }, - { - "type": "text", - "name": "datatype", - "value": "16 bit half" - }, - { - "type": "text", - "name": "compression", - "value": "Zip (1 scanline)" - }, - { - "type": "bool", - "name": "autocrop", - "value": true - }, - { - "type": "color_gui", - "name": "tile_color", - "value": [ - 186, - 35, - 35, - 255 - ] - }, - { - "type": "text", - "name": "channels", - "value": "rgb" - }, - { - "type": "text", - "name": "colorspace", - "value": "scene_linear" - }, - { - "type": "bool", - "name": "create_directories", - "value": true - } - ] - }, - { - "plugins": [ - "CreateWritePrerender" - ], - "nukeNodeClass": "Write", - "knobs": [ - { - "type": "text", - "name": "file_type", - "value": "exr" - }, - { - "type": "text", - "name": "datatype", - "value": "16 bit half" - }, - { - "type": "text", - "name": "compression", - "value": "Zip (1 scanline)" - }, - { - "type": "bool", - "name": "autocrop", - "value": true - }, - { - "type": "color_gui", - "name": "tile_color", - "value": [ - 171, - 171, - 10, - 255 - ] - }, - { - "type": "text", - "name": "channels", - "value": "rgb" - }, - { - "type": "text", - "name": "colorspace", - "value": "scene_linear" - }, - { - "type": "bool", - "name": "create_directories", - "value": true - } - ] - }, - { - "plugins": [ - "CreateWriteImage" - ], - "nukeNodeClass": "Write", - "knobs": [ - { - "type": "text", - "name": "file_type", - "value": "tiff" - }, - { - "type": "text", - "name": "datatype", - "value": "16 bit" - }, - { - "type": "text", - "name": "compression", - "value": "Deflate" - }, - { - "type": "color_gui", - "name": "tile_color", - "value": [ - 56, - 162, - 7, - 255 - ] - }, - { - "type": "text", - "name": "channels", - "value": "rgb" - }, - { - "type": "text", - "name": "colorspace", - "value": "texture_paint" - }, - { - "type": "bool", - "name": "create_directories", - "value": true - } - ] - } - ], - "overrideNodes": [] - }, - "regexInputs": { - "inputs": [ - { - "regex": "(beauty).*(?=.exr)", - "colorspace": "scene_linear" - } - ] - } - }, - "nuke-dirmap": { - "enabled": false, - "paths": { - "source-path": [], - "destination-path": [] - } - }, - "scriptsmenu": { - "name": "OpenPype Tools", - "definition": [ - { - "type": "action", - "sourcetype": "python", - "title": "OpenPype Docs", - "command": "import webbrowser;webbrowser.open(url='https://openpype.io/docs/artist_hosts_nuke_tut')", - "tooltip": "Open the OpenPype Nuke user doc page" - }, - { - "type": "action", - "sourcetype": "python", - "title": "Set Frame Start (Read Node)", - "command": "from ayon_core.hosts.nuke.startup.frame_setting_for_read_nodes import main;main();", - "tooltip": "Set frame start for read node(s)" - }, - { - "type": "action", - "sourcetype": "python", - "title": "Set non publish output for Write Node", - "command": "from ayon_core.hosts.nuke.startup.custom_write_node import main;main();", - "tooltip": "Open the OpenPype Nuke user doc page" - } - ] - }, - "gizmo": [ - { - "toolbar_menu_name": "OpenPype Gizmo", - "gizmo_source_dir": { - "windows": [], - "darwin": [], - "linux": [] - }, - "toolbar_icon_path": { - "windows": "", - "darwin": "", - "linux": "" - }, - "gizmo_definition": [ - { - "gizmo_toolbar_path": "/path/to/menu", - "sub_gizmo_list": [ - { - "sourcetype": "python", - "title": "Gizmo Note", - "command": "nuke.nodes.StickyNote(label='You can create your own toolbar menu in the Nuke GizmoMenu of OpenPype')", - "icon": "", - "shortcut": "" - } - ] - } - ] - } - ], - "create": { - "CreateWriteRender": { - "temp_rendering_path_template": "{work}/renders/nuke/{subset}/{subset}.{frame}.{ext}", - "default_variants": [ - "Main", - "Mask" - ], - "instance_attributes": [ - "reviewable", - "farm_rendering" - ], - "prenodes": { - "Reformat01": { - "nodeclass": "Reformat", - "dependent": "", - "knobs": [ - { - "type": "text", - "name": "resize", - "value": "none" - }, - { - "type": "bool", - "name": "black_outside", - "value": true - } - ] - } - } - }, - "CreateWritePrerender": { - "temp_rendering_path_template": "{work}/renders/nuke/{subset}/{subset}.{frame}.{ext}", - "default_variants": [ - "Key01", - "Bg01", - "Fg01", - "Branch01", - "Part01" - ], - "instance_attributes": [ - "farm_rendering", - "use_range_limit" - ], - "prenodes": {} - }, - "CreateWriteImage": { - "temp_rendering_path_template": "{work}/renders/nuke/{subset}/{subset}.{ext}", - "default_variants": [ - "StillFrame", - "MPFrame", - "LayoutFrame" - ], - "instance_attributes": [ - "use_range_limit" - ], - "prenodes": { - "FrameHold01": { - "nodeclass": "FrameHold", - "dependent": "", - "knobs": [ - { - "type": "expression", - "name": "first_frame", - "expression": "parent.first" - } - ] - } - } - } - }, - "publish": { - "CollectInstanceData": { - "sync_workfile_version_on_families": [ - "nukenodes", - "camera", - "gizmo", - "source", - "render", - "write" - ] - }, - "ValidateCorrectAssetContext": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateContainers": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateKnobs": { - "enabled": false, - "knobs": { - "render": { - "review": true - } - } - }, - "ValidateOutputResolution": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateBackdrop": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateGizmo": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateScriptAttributes": { - "enabled": true, - "optional": true, - "active": true - }, - "ExtractReviewData": { - "enabled": false - }, - "ExtractReviewDataLut": { - "enabled": false - }, - "ExtractReviewDataMov": { - "enabled": true, - "viewer_lut_raw": false, - "outputs": { - "baking": { - "filter": { - "task_types": [], - "families": [], - "subsets": [] - }, - "read_raw": false, - "viewer_process_override": "", - "bake_viewer_process": true, - "bake_viewer_input_process": true, - "reformat_nodes_config": { - "enabled": false, - "reposition_nodes": [ - { - "node_class": "Reformat", - "knobs": [ - { - "type": "text", - "name": "type", - "value": "to format" - }, - { - "type": "text", - "name": "format", - "value": "HD_1080" - }, - { - "type": "text", - "name": "filter", - "value": "Lanczos6" - }, - { - "type": "bool", - "name": "black_outside", - "value": true - }, - { - "type": "bool", - "name": "pbb", - "value": false - } - ] - } - ] - }, - "extension": "mov", - "add_custom_tags": [] - } - } - }, - "ExtractReviewIntermediates": { - "enabled": true, - "viewer_lut_raw": false, - "outputs": { - "baking": { - "filter": { - "task_types": [], - "families": [], - "subsets": [] - }, - "read_raw": false, - "viewer_process_override": "", - "bake_viewer_process": true, - "bake_viewer_input_process": true, - "reformat_nodes_config": { - "enabled": false, - "reposition_nodes": [ - { - "node_class": "Reformat", - "knobs": [ - { - "type": "text", - "name": "type", - "value": "to format" - }, - { - "type": "text", - "name": "format", - "value": "HD_1080" - }, - { - "type": "text", - "name": "filter", - "value": "Lanczos6" - }, - { - "type": "bool", - "name": "black_outside", - "value": true - }, - { - "type": "bool", - "name": "pbb", - "value": false - } - ] - } - ] - }, - "extension": "mov", - "add_custom_tags": [] - } - } - }, - "ExtractSlateFrame": { - "viewer_lut_raw": false, - "key_value_mapping": { - "f_submission_note": [ - true, - "{comment}" - ], - "f_submitting_for": [ - true, - "{intent[value]}" - ], - "f_vfx_scope_of_work": [ - false, - "" - ] - } - }, - "IncrementScriptVersion": { - "enabled": true, - "optional": true, - "active": true - } - }, - "load": { - "LoadImage": { - "enabled": true, - "_representations": [], - "node_name_template": "{class_name}_{ext}" - }, - "LoadClip": { - "enabled": true, - "_representations": [], - "node_name_template": "{class_name}_{ext}", - "options_defaults": { - "start_at_workfile": true, - "add_retime": true - } - } - }, - "workfile_builder": { - "create_first_version": false, - "custom_templates": [], - "builder_on_start": false, - "profiles": [] - }, - "templated_workfile_build": { - "profiles": [] - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/photoshop.json b/client/ayon_core/settings/defaults/project_settings/photoshop.json deleted file mode 100644 index 71f94f5bfc..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/photoshop.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "imageio": { - "activate_host_color_management": true, - "remapping": { - "rules": [] - }, - "ocio_config": { - "override_global_config": false, - "filepath": [] - }, - "file_rules": { - "activate_host_rules": false, - "rules": {} - } - }, - "create": { - "ImageCreator": { - "enabled": true, - "active_on_create": true, - "mark_for_review": false, - "default_variants": [ - "Main" - ] - }, - "AutoImageCreator": { - "enabled": false, - "active_on_create": true, - "mark_for_review": false, - "default_variant": "" - }, - "ReviewCreator": { - "enabled": true, - "active_on_create": true, - "default_variant": "" - }, - "WorkfileCreator": { - "enabled": true, - "active_on_create": true, - "default_variant": "Main" - } - }, - "publish": { - "CollectColorCodedInstances": { - "enabled": true, - "create_flatten_image": "no", - "flatten_subset_template": "", - "color_code_mapping": [] - }, - "CollectReview": { - "enabled": true - }, - "CollectVersion": { - "enabled": false - }, - "ValidateContainers": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateNaming": { - "invalid_chars": "[ \\\\/+\\*\\?\\(\\)\\[\\]\\{\\}:,;]", - "replace_char": "_" - }, - "ExtractImage": { - "formats": [ - "png", - "jpg" - ] - }, - "ExtractReview": { - "make_image_sequence": false, - "max_downscale_size": 8192, - "jpg_options": { - "tags": [ - "review", - "ftrackreview" - ] - }, - "mov_options": { - "tags": [ - "review", - "ftrackreview" - ] - } - } - }, - "workfile_builder": { - "create_first_version": false, - "custom_templates": [] - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/resolve.json b/client/ayon_core/settings/defaults/project_settings/resolve.json deleted file mode 100644 index 95b3cc66b3..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/resolve.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "launch_openpype_menu_on_start": false, - "imageio": { - "activate_host_color_management": true, - "remapping": { - "rules": [] - }, - "ocio_config": { - "override_global_config": false, - "filepath": [] - }, - "file_rules": { - "activate_host_rules": false, - "rules": {} - } - }, - "create": { - "CreateShotClip": { - "hierarchy": "{folder}/{sequence}", - "clipRename": true, - "clipName": "{track}{sequence}{shot}", - "countFrom": 10, - "countSteps": 10, - "folder": "shots", - "episode": "ep01", - "sequence": "sq01", - "track": "{_track_}", - "shot": "sh###", - "vSyncOn": false, - "workfileFrameStart": 1001, - "handleStart": 10, - "handleEnd": 10 - } - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/royalrender.json b/client/ayon_core/settings/defaults/project_settings/royalrender.json deleted file mode 100644 index 14e36058aa..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/royalrender.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "rr_paths": [ - "default" - ], - "publish": { - "CollectSequencesFromJob": { - "review": true - } - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/shotgrid.json b/client/ayon_core/settings/defaults/project_settings/shotgrid.json deleted file mode 100644 index 83b6f69074..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/shotgrid.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "shotgrid_project_id": 0, - "shotgrid_server": "", - "event": { - "enabled": false - }, - "fields": { - "asset": { - "type": "sg_asset_type" - }, - "sequence": { - "episode_link": "episode" - }, - "shot": { - "episode_link": "sg_episode", - "sequence_link": "sg_sequence" - }, - "task": { - "step": "step" - } - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/slack.json b/client/ayon_core/settings/defaults/project_settings/slack.json deleted file mode 100644 index 910f099d04..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/slack.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "token": "", - "publish": { - "CollectSlackFamilies": { - "enabled": true, - "optional": true, - "profiles": [ - { - "families": [], - "hosts": [], - "task_types": [], - "tasks": [], - "subsets": [], - "review_upload_limit": 50.0, - "channel_messages": [] - } - ] - } - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/standalonepublisher.json b/client/ayon_core/settings/defaults/project_settings/standalonepublisher.json deleted file mode 100644 index 44982133eb..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/standalonepublisher.json +++ /dev/null @@ -1,299 +0,0 @@ -{ - "create": { - "create_workfile": { - "name": "workfile", - "label": "Workfile", - "family": "workfile", - "icon": "cube", - "defaults": [ - "Main" - ], - "help": "Working scene backup" - }, - "create_model": { - "name": "model", - "label": "Model", - "family": "model", - "icon": "cube", - "defaults": [ - "Main" - ], - "help": "Polygonal static geometry" - }, - "create_rig": { - "name": "rig", - "label": "Rig", - "family": "rig", - "icon": "wheelchair", - "defaults": [ - "Main", - "Cloth" - ], - "help": "Artist-friendly rig with controls" - }, - "create_pointcache": { - "name": "pointcache", - "label": "Pointcache", - "family": "pointcache", - "icon": "gears", - "defaults": [ - "Main" - ], - "help": "Alembic pointcache for animated data" - }, - "create_plate": { - "name": "plate", - "label": "Plate", - "family": "plate", - "icon": "camera", - "defaults": [ - "Main", - "BG", - "Animatic", - "Reference", - "Offline" - ], - "help": "Footage for composting or reference" - }, - "create_camera": { - "name": "camera", - "label": "Camera", - "family": "camera", - "icon": "camera", - "defaults": [ - "Main" - ], - "help": "video-camera" - }, - "create_editorial": { - "name": "editorial", - "label": "Editorial", - "family": "editorial", - "icon": "image", - "defaults": [ - "Main" - ], - "help": "Editorial files to generate shots." - }, - "create_image": { - "name": "image", - "label": "Image file", - "family": "image", - "icon": "image", - "defaults": [ - "Reference", - "Texture", - "ConceptArt", - "MattePaint" - ], - "help": "Holder for all kinds of image data" - }, - "create_matchmove": { - "name": "matchmove", - "label": "Matchmove Scripts", - "family": "matchmove", - "icon": "empire", - "defaults": [ - "Camera", - "Object", - "Mocap" - ], - "help": "Script exported from matchmoving application" - }, - "create_render": { - "name": "render", - "label": "Render", - "family": "render", - "icon": "image", - "defaults": [ - "Animation", - "Lighting", - "Lookdev", - "Compositing" - ], - "help": "Rendered images or video files" - }, - "create_mov_batch": { - "name": "mov_batch", - "label": "Batch Mov", - "family": "render_mov_batch", - "icon": "image", - "defaults": [ - "Main" - ], - "help": "Process multiple Mov files and publish them for layout and comp." - }, - "create_texture_batch": { - "name": "texture_batch", - "label": "Texture Batch", - "family": "texture_batch", - "icon": "image", - "defaults": [ - "Main" - ], - "help": "Texture files with UDIM together with worfile" - }, - "create_vdb": { - "name": "vdb", - "label": "VDB Volumetric Data", - "family": "vdbcache", - "icon": "cloud", - "defaults": [], - "help": "Hierarchical data structure for the efficient storage and manipulation of sparse volumetric data discretized on three-dimensional grids" - }, - "__dynamic_keys_labels__": { - "create_workfile": "Workfile", - "create_model": "Model", - "create_rig": "Rig", - "create_pointcache": "Pointcache", - "create_plate": "Plate", - "create_camera": "Camera", - "create_editorial": "Editorial", - "create_image": "Image", - "create_matchmove": "Matchmove", - "create_render": "Render", - "create_mov_batch": "Batch Mov", - "create_texture_batch": "Batch Texture", - "create_simple_unreal_texture": "Simple Unreal Texture", - "create_vdb": "VDB Cache" - } - }, - "publish": { - "CollectTextures": { - "enabled": true, - "active": true, - "main_workfile_extensions": [ - "mra" - ], - "other_workfile_extensions": [ - "spp", - "psd" - ], - "texture_extensions": [ - "exr", - "dpx", - "jpg", - "jpeg", - "png", - "tiff", - "tga", - "gif", - "svg" - ], - "workfile_families": [], - "texture_families": [], - "color_space": [ - "sRGB", - "Raw", - "ACEScg" - ], - "input_naming_patterns": { - "workfile": [ - "^([^.]+)(_[^_.]*)?_v([0-9]{3,}).+" - ], - "textures": [ - "^([^_.]+)_([^_.]+)_v([0-9]{3,})_([^_.]+)_({color_space})_(1[0-9]{3}).+" - ] - }, - "input_naming_groups": { - "workfile": [ - "asset", - "filler", - "version" - ], - "textures": [ - "asset", - "shader", - "version", - "channel", - "color_space", - "udim" - ] - }, - "workfile_subset_template": "textures{Subset}Workfile", - "texture_subset_template": "textures{Subset}_{Shader}_{Channel}" - }, - "ValidateSceneSettings": { - "enabled": true, - "optional": true, - "active": true, - "check_extensions": [ - "exr", - "dpx", - "jpg", - "jpeg", - "png", - "tiff", - "tga", - "gif", - "svg" - ], - "families": [ - "render" - ], - "skip_timelines_check": [] - }, - "ExtractThumbnailSP": { - "ffmpeg_args": { - "input": [ - "-apply_trc gamma22" - ], - "output": [] - } - }, - "CollectEditorial": { - "source_dir": "", - "extensions": [ - "mov", - "mp4" - ] - }, - "CollectHierarchyInstance": { - "shot_rename": true, - "shot_rename_template": "{project[code]}_{_sequence_}_{_shot_}", - "shot_rename_search_patterns": { - "_sequence_": "(sc\\d{3})", - "_shot_": "(sh\\d{3})" - }, - "shot_add_hierarchy": { - "enabled": true, - "parents_path": "{project}/{folder}/{sequence}", - "parents": { - "project": "{project[name]}", - "sequence": "{_sequence_}", - "folder": "shots" - } - }, - "shot_add_tasks": {} - }, - "CollectInstances": { - "custom_start_frame": 0, - "timeline_frame_start": 900000, - "timeline_frame_offset": 0, - "subsets": { - "referenceMain": { - "family": "review", - "families": [ - "clip" - ], - "extensions": [ - "mp4" - ], - "version": 0, - "keepSequence": false - }, - "audioMain": { - "family": "audio", - "families": [ - "clip" - ], - "extensions": [ - "wav" - ], - "version": 0, - "keepSequence": false - } - } - } - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/substancepainter.json b/client/ayon_core/settings/defaults/project_settings/substancepainter.json deleted file mode 100644 index 2f9344d435..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/substancepainter.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "imageio": { - "activate_host_color_management": true, - "ocio_config": { - "override_global_config": false, - "filepath": [] - }, - "file_rules": { - "activate_host_rules": false, - "rules": {} - } - }, - "shelves": {} -} diff --git a/client/ayon_core/settings/defaults/project_settings/traypublisher.json b/client/ayon_core/settings/defaults/project_settings/traypublisher.json deleted file mode 100644 index 7d2f358cb2..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/traypublisher.json +++ /dev/null @@ -1,352 +0,0 @@ -{ - "imageio": { - "activate_host_color_management": true, - "ocio_config": { - "override_global_config": false, - "filepath": [] - }, - "file_rules": { - "activate_host_rules": false, - "rules": {} - } - }, - "simple_creators": [ - { - "family": "workfile", - "identifier": "", - "label": "Workfile", - "icon": "fa.file", - "default_variants": [ - "Main" - ], - "description": "Backup of a working scene", - "detailed_description": "Workfiles are full scenes from any application that are directly edited by artists. They represent a state of work on a task at a given point and are usually not directly referenced into other scenes.", - "allow_sequences": false, - "allow_multiple_items": false, - "allow_version_control": false, - "extensions": [ - ".ma", - ".mb", - ".nk", - ".hrox", - ".hip", - ".hiplc", - ".hipnc", - ".blend", - ".scn", - ".tvpp", - ".comp", - ".zip", - ".prproj", - ".drp", - ".psd", - ".psb", - ".aep" - ] - }, - { - "family": "model", - "identifier": "", - "label": "Model", - "icon": "fa.cubes", - "default_variants": [ - "Main", - "Proxy", - "Sculpt" - ], - "description": "Clean models", - "detailed_description": "Models should only contain geometry data, without any extras like cameras, locators or bones.\n\nKeep in mind that models published from tray publisher are not validated for correctness. ", - "allow_sequences": false, - "allow_multiple_items": true, - "allow_version_control": false, - "extensions": [ - ".ma", - ".mb", - ".obj", - ".abc", - ".fbx", - ".bgeo", - ".bgeogz", - ".bgeosc", - ".usd", - ".blend" - ] - }, - { - "family": "pointcache", - "identifier": "", - "label": "Pointcache", - "icon": "fa.gears", - "default_variants": [ - "Main" - ], - "description": "Geometry Caches", - "detailed_description": "Alembic or bgeo cache of animated data", - "allow_sequences": true, - "allow_multiple_items": true, - "allow_version_control": false, - "extensions": [ - ".abc", - ".bgeo", - ".bgeogz", - ".bgeosc" - ] - }, - { - "family": "plate", - "identifier": "", - "label": "Plate", - "icon": "mdi.camera-image", - "default_variants": [ - "Main", - "BG", - "Animatic", - "Reference", - "Offline" - ], - "description": "Footage Plates", - "detailed_description": "Any type of image seqeuence coming from outside of the studio. Usually camera footage, but could also be animatics used for reference.", - "allow_sequences": true, - "allow_multiple_items": true, - "allow_version_control": false, - "extensions": [ - ".exr", - ".png", - ".dpx", - ".jpg", - ".tiff", - ".tif", - ".mov", - ".mp4", - ".avi" - ] - }, - { - "family": "render", - "identifier": "", - "label": "Render", - "icon": "mdi.folder-multiple-image", - "default_variants": [], - "description": "Rendered images or video", - "detailed_description": "Sequence or single file renders", - "allow_sequences": true, - "allow_multiple_items": true, - "allow_version_control": false, - "extensions": [ - ".exr", - ".png", - ".dpx", - ".jpg", - ".jpeg", - ".tiff", - ".tif", - ".mov", - ".mp4", - ".avi" - ] - }, - { - "family": "camera", - "identifier": "", - "label": "Camera", - "icon": "fa.video-camera", - "default_variants": [], - "description": "3d Camera", - "detailed_description": "Ideally this should be only camera itself with baked animation, however, it can technically also include helper geometry.", - "allow_sequences": false, - "allow_multiple_items": true, - "allow_version_control": false, - "extensions": [ - ".abc", - ".ma", - ".hip", - ".blend", - ".fbx", - ".usd" - ] - }, - { - "family": "image", - "identifier": "", - "label": "Image", - "icon": "fa.image", - "default_variants": [ - "Reference", - "Texture", - "Concept", - "Background" - ], - "description": "Single image", - "detailed_description": "Any image data can be published as image family. References, textures, concept art, matte paints. This is a fallback 2d family for everything that doesn't fit more specific family.", - "allow_sequences": false, - "allow_multiple_items": true, - "allow_version_control": false, - "extensions": [ - ".exr", - ".jpg", - ".jpeg", - ".dpx", - ".bmp", - ".tif", - ".tiff", - ".png", - ".psb", - ".psd" - ] - }, - { - "family": "vdb", - "identifier": "", - "label": "VDB Volumes", - "icon": "fa.cloud", - "default_variants": [], - "description": "Sparse volumetric data", - "detailed_description": "Hierarchical data structure for the efficient storage and manipulation of sparse volumetric data discretized on three-dimensional grids", - "allow_sequences": true, - "allow_multiple_items": true, - "allow_version_control": false, - "extensions": [ - ".vdb" - ] - }, - { - "family": "matchmove", - "identifier": "", - "label": "Matchmove", - "icon": "fa.empire", - "default_variants": [ - "Camera", - "Object", - "Mocap" - ], - "description": "Matchmoving script", - "detailed_description": "Script exported from matchmoving application to be later processed into a tracked camera with additional data", - "allow_sequences": false, - "allow_multiple_items": true, - "allow_version_control": false, - "extensions": [] - }, - { - "family": "rig", - "identifier": "", - "label": "Rig", - "icon": "fa.wheelchair", - "default_variants": [], - "description": "CG rig file", - "detailed_description": "CG rigged character or prop. Rig should be clean of any extra data and directly loadable into it's respective application\t", - "allow_sequences": false, - "allow_multiple_items": false, - "allow_version_control": false, - "extensions": [ - ".ma", - ".blend", - ".hip", - ".hda" - ] - }, - { - "family": "audio", - "identifier": "", - "label": "Audio ", - "icon": "fa5s.file-audio", - "default_variants": [ - "Main" - ], - "description": "Audio product", - "detailed_description": "Audio files for review or final delivery", - "allow_sequences": false, - "allow_multiple_items": false, - "allow_version_control": false, - "extensions": [ - ".wav" - ] - } - ], - "editorial_creators": { - "editorial_simple": { - "default_variants": [ - "Main" - ], - "clip_name_tokenizer": { - "_sequence_": "(sc\\d{3})", - "_shot_": "(sh\\d{3})" - }, - "shot_rename": { - "enabled": true, - "shot_rename_template": "{project[code]}_{_sequence_}_{_shot_}" - }, - "shot_hierarchy": { - "enabled": true, - "parents_path": "{project}/{folder}/{sequence}", - "parents": [ - { - "type": "Project", - "name": "project", - "value": "{project[name]}" - }, - { - "type": "Folder", - "name": "folder", - "value": "shots" - }, - { - "type": "Sequence", - "name": "sequence", - "value": "{_sequence_}" - } - ] - }, - "shot_add_tasks": {}, - "family_presets": [ - { - "family": "review", - "variant": "Reference", - "review": true, - "output_file_type": ".mp4" - }, - { - "family": "plate", - "variant": "", - "review": false, - "output_file_type": ".mov" - }, - { - "family": "audio", - "variant": "", - "review": false, - "output_file_type": ".wav" - } - ] - } - }, - "create": { - "BatchMovieCreator": { - "default_variants": [ - "Main" - ], - "default_tasks": [ - "Compositing" - ], - "extensions": [ - ".mov" - ] - } - }, - "publish": { - "CollectSequenceFrameData": { - "enabled": true, - "optional": true, - "active": false - }, - "ValidateFrameRange": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateExistingVersion": { - "enabled": true, - "optional": true, - "active": true - } - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/tvpaint.json b/client/ayon_core/settings/defaults/project_settings/tvpaint.json deleted file mode 100644 index d03b8b7227..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/tvpaint.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "imageio": { - "activate_host_color_management": true, - "ocio_config": { - "override_global_config": false, - "filepath": [] - }, - "file_rules": { - "activate_host_rules": false, - "rules": {} - } - }, - "stop_timer_on_application_exit": false, - "create": { - "create_workfile": { - "enabled": true, - "default_variant": "Main", - "default_variants": [] - }, - "create_review": { - "enabled": true, - "active_on_create": true, - "default_variant": "Main", - "default_variants": [] - }, - "create_render_scene": { - "enabled": true, - "active_on_create": false, - "mark_for_review": true, - "default_pass_name": "beauty", - "default_variant": "Main", - "default_variants": [] - }, - "create_render_layer": { - "mark_for_review": false, - "default_pass_name": "beauty", - "default_variant": "Main", - "default_variants": [] - }, - "create_render_pass": { - "mark_for_review": false, - "default_variant": "Main", - "default_variants": [] - }, - "auto_detect_render": { - "enabled": false, - "allow_group_rename": true, - "group_name_template": "L{group_index}", - "group_idx_offset": 10, - "group_idx_padding": 3 - } - }, - "publish": { - "CollectRenderInstances": { - "ignore_render_pass_transparency": false - }, - "ExtractSequence": { - "review_bg": [ - 255, - 255, - 255, - 255 - ] - }, - "ValidateProjectSettings": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateMarks": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateStartFrame": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateAssetName": { - "enabled": true, - "optional": true, - "active": true - }, - "ExtractConvertToEXR": { - "enabled": false, - "replace_pngs": true, - "exr_compression": "ZIP" - } - }, - "load": { - "LoadImage": { - "defaults": { - "stretch": true, - "timestretch": true, - "preload": true - } - }, - "ImportImage": { - "defaults": { - "stretch": true, - "timestretch": true, - "preload": true - } - } - }, - "workfile_builder": { - "create_first_version": false, - "custom_templates": [] - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/unreal.json b/client/ayon_core/settings/defaults/project_settings/unreal.json deleted file mode 100644 index 20e55c74f0..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/unreal.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "imageio": { - "activate_host_color_management": true, - "ocio_config": { - "override_global_config": false, - "filepath": [] - }, - "file_rules": { - "activate_host_rules": false, - "rules": {} - } - }, - "level_sequences_for_layouts": false, - "delete_unmatched_assets": false, - "render_config_path": "", - "preroll_frames": 0, - "render_format": "png", - "project_setup": { - "dev_mode": false - } -} diff --git a/client/ayon_core/settings/defaults/project_settings/webpublisher.json b/client/ayon_core/settings/defaults/project_settings/webpublisher.json deleted file mode 100644 index e451bcfc17..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/webpublisher.json +++ /dev/null @@ -1,145 +0,0 @@ -{ - "imageio": { - "activate_host_color_management": true, - "ocio_config": { - "override_global_config": false, - "filepath": [] - }, - "file_rules": { - "activate_host_rules": false, - "rules": {} - } - }, - "timeout_profiles": [ - { - "hosts": [ - "photoshop" - ], - "task_types": [], - "timeout": 600 - } - ], - "publish": { - "CollectPublishedFiles": { - "sync_next_version": false, - "task_type_to_family": { - "Animation": [ - { - "is_sequence": false, - "extensions": [ - "tvp" - ], - "families": [], - "tags": [], - "result_family": "workfile" - }, - { - "is_sequence": true, - "extensions": [ - "png", - "exr", - "tiff", - "tif" - ], - "families": [ - "review" - ], - "tags": [ - "review" - ], - "result_family": "render" - } - ], - "Compositing": [ - { - "is_sequence": false, - "extensions": [ - "aep" - ], - "families": [], - "tags": [], - "result_family": "workfile" - }, - { - "is_sequence": true, - "extensions": [ - "png", - "exr", - "tiff", - "tif" - ], - "families": [ - "review" - ], - "tags": [ - "review" - ], - "result_family": "render" - } - ], - "Layout": [ - { - "is_sequence": false, - "extensions": [ - "psd" - ], - "families": [], - "tags": [], - "result_family": "workfile" - }, - { - "is_sequence": false, - "extensions": [ - "png", - "jpg", - "jpeg", - "tiff", - "tif" - ], - "families": [ - "review" - ], - "tags": [ - "review" - ], - "result_family": "image" - } - ], - "default_task_type": [ - { - "is_sequence": false, - "extensions": [ - "tvp", - "psd" - ], - "families": [], - "tags": [], - "result_family": "workfile" - }, - { - "is_sequence": true, - "extensions": [ - "png", - "exr", - "tiff", - "tif" - ], - "families": [ - "review" - ], - "tags": [ - "review" - ], - "result_family": "render" - } - ], - "__dynamic_keys_labels__": { - "default_task_type": "Default task type" - } - } - }, - "CollectTVPaintInstances": { - "layer_name_regex": "(?PL[0-9]{3}_\\w+)_(?P.+)" - } - } -} diff --git a/client/ayon_core/settings/lib.py b/client/ayon_core/settings/lib.py index 8ad4dc1f93..3ddba85c9b 100644 --- a/client/ayon_core/settings/lib.py +++ b/client/ayon_core/settings/lib.py @@ -1,196 +1,131 @@ import os import json import logging +import collections import copy +import time -from .constants import ( - M_OVERRIDDEN_KEY, - - METADATA_KEYS, - - PROJECT_SETTINGS_KEY, -) - -from .ayon_settings import ( - get_ayon_project_settings, - get_ayon_system_settings, - get_ayon_settings, -) +from ayon_core.client import get_ayon_server_api_connection log = logging.getLogger(__name__) -# Py2 + Py3 json decode exception -JSON_EXC = getattr(json.decoder, "JSONDecodeError", ValueError) + +class CacheItem: + lifetime = 10 + + def __init__(self, value, outdate_time=None): + self._value = value + if outdate_time is None: + outdate_time = time.time() + self.lifetime + self._outdate_time = outdate_time + + @classmethod + def create_outdated(cls): + return cls({}, 0) + + def get_value(self): + return copy.deepcopy(self._value) + + def update_value(self, value): + self._value = value + self._outdate_time = time.time() + self.lifetime + + @property + def is_outdated(self): + return time.time() > self._outdate_time -# Path to default settings -DEFAULTS_DIR = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "defaults" -) +class _AyonSettingsCache: + use_bundles = None + variant = None + addon_versions = CacheItem.create_outdated() + studio_settings = CacheItem.create_outdated() + cache_by_project_name = collections.defaultdict( + CacheItem.create_outdated) -# Variable where cache of default settings are stored -_DEFAULT_SETTINGS = None + @classmethod + def _use_bundles(cls): + if _AyonSettingsCache.use_bundles is None: + con = get_ayon_server_api_connection() + major, minor, _, _, _ = con.get_server_version_tuple() + use_bundles = True + if (major, minor) < (0, 3): + use_bundles = False + _AyonSettingsCache.use_bundles = use_bundles + return _AyonSettingsCache.use_bundles + @classmethod + def _get_variant(cls): + if _AyonSettingsCache.variant is None: + from ayon_core.lib import is_staging_enabled, is_dev_mode_enabled -def clear_metadata_from_settings(values): - """Remove all metadata keys from loaded settings.""" - if isinstance(values, dict): - for key in tuple(values.keys()): - if key in METADATA_KEYS: - values.pop(key) + variant = "production" + if is_dev_mode_enabled(): + variant = cls._get_bundle_name() + elif is_staging_enabled(): + variant = "staging" + + # Cache variant + _AyonSettingsCache.variant = variant + + # Set the variant to global ayon api connection + con = get_ayon_server_api_connection() + con.set_default_settings_variant(variant) + return _AyonSettingsCache.variant + + @classmethod + def _get_bundle_name(cls): + return os.environ["AYON_BUNDLE_NAME"] + + @classmethod + def get_value_by_project(cls, project_name): + cache_item = _AyonSettingsCache.cache_by_project_name[project_name] + if cache_item.is_outdated: + con = get_ayon_server_api_connection() + if cls._use_bundles(): + value = con.get_addons_settings( + bundle_name=cls._get_bundle_name(), + project_name=project_name, + variant=cls._get_variant() + ) else: - clear_metadata_from_settings(values[key]) - elif isinstance(values, list): - for item in values: - clear_metadata_from_settings(item) + value = con.get_addons_settings(project_name) + cache_item.update_value(value) + return cache_item.get_value() - -def load_openpype_default_settings(): - """Load openpype default settings.""" - return load_jsons_from_dir(DEFAULTS_DIR) - - -def reset_default_settings(): - """Reset cache of default settings. Can't be used now.""" - global _DEFAULT_SETTINGS - _DEFAULT_SETTINGS = None - - -def _get_default_settings(): - return load_openpype_default_settings() - - -def get_default_settings(): - """Get default settings. - - Todo: - Cache loaded defaults. - - Returns: - dict: Loaded default settings. - """ - global _DEFAULT_SETTINGS - if _DEFAULT_SETTINGS is None: - _DEFAULT_SETTINGS = _get_default_settings() - return copy.deepcopy(_DEFAULT_SETTINGS) - - -def load_json_file(fpath): - # Load json data - try: - with open(fpath, "r") as opened_file: - return json.load(opened_file) - - except JSON_EXC: - log.warning( - "File has invalid json format \"{}\"".format(fpath), - exc_info=True + @classmethod + def _get_addon_versions_from_bundle(cls): + con = get_ayon_server_api_connection() + expected_bundle = cls._get_bundle_name() + bundles = con.get_bundles()["bundles"] + bundle = next( + ( + bundle + for bundle in bundles + if bundle["name"] == expected_bundle + ), + None ) - return {} + if bundle is not None: + return bundle["addons"] + return {} + @classmethod + def get_addon_versions(cls): + cache_item = _AyonSettingsCache.addon_versions + if cache_item.is_outdated: + if cls._use_bundles(): + addons = cls._get_addon_versions_from_bundle() + else: + con = get_ayon_server_api_connection() + settings_data = con.get_addons_settings( + only_values=False, + variant=cls._get_variant() + ) + addons = settings_data["versions"] + cache_item.update_value(addons) -def load_jsons_from_dir(path, *args, **kwargs): - """Load all .json files with content from entered folder path. - - Data are loaded recursively from a directory and recreate the - hierarchy as a dictionary. - - Entered path hierarchy: - |_ folder1 - | |_ data1.json - |_ folder2 - |_ subfolder1 - |_ data2.json - - Will result in: - ```javascript - { - "folder1": { - "data1": "CONTENT OF FILE" - }, - "folder2": { - "subfolder1": { - "data2": "CONTENT OF FILE" - } - } - } - ``` - - Args: - path (str): Path to the root folder where the json hierarchy starts. - - Returns: - dict: Loaded data. - """ - output = {} - - path = os.path.normpath(path) - if not os.path.exists(path): - # TODO warning - return output - - sub_keys = list(kwargs.pop("subkeys", args)) - for sub_key in tuple(sub_keys): - _path = os.path.join(path, sub_key) - if not os.path.exists(_path): - break - - path = _path - sub_keys.pop(0) - - base_len = len(path) + 1 - for base, _directories, filenames in os.walk(path): - base_items_str = base[base_len:] - if not base_items_str: - base_items = [] - else: - base_items = base_items_str.split(os.path.sep) - - for filename in filenames: - basename, ext = os.path.splitext(filename) - if ext == ".json": - full_path = os.path.join(base, filename) - value = load_json_file(full_path) - dict_keys = base_items + [basename] - output = subkey_merge(output, value, dict_keys) - - for sub_key in sub_keys: - output = output[sub_key] - return output - - -def subkey_merge(_dict, value, keys): - key = keys.pop(0) - if not keys: - _dict[key] = value - return _dict - - if key not in _dict: - _dict[key] = {} - _dict[key] = subkey_merge(_dict[key], value, keys) - - return _dict - - -def merge_overrides(source_dict, override_dict): - """Merge data from override_dict to source_dict.""" - - if M_OVERRIDDEN_KEY in override_dict: - overridden_keys = set(override_dict.pop(M_OVERRIDDEN_KEY)) - else: - overridden_keys = set() - - for key, value in override_dict.items(): - if (key in overridden_keys or key not in source_dict): - source_dict[key] = value - - elif isinstance(value, dict) and isinstance(source_dict[key], dict): - source_dict[key] = merge_overrides(source_dict[key], value) - - else: - source_dict[key] = value - return source_dict + return cache_item.get_value() def get_site_local_overrides(project_name, site_name, local_settings=None): @@ -209,6 +144,38 @@ def get_site_local_overrides(project_name, site_name, local_settings=None): return {} +def get_ayon_settings(project_name=None): + """AYON studio settings. + + Raw AYON settings values. + + Args: + project_name (Optional[str]): Project name. + + Returns: + dict[str, Any]: AYON settings. + """ + + return _AyonSettingsCache.get_value_by_project(project_name) + + +def get_studio_settings(*args, **kwargs): + return _AyonSettingsCache.get_value_by_project(None) + + +# Backward compatibility +get_system_settings = get_studio_settings + + +def get_project_settings(project_name, *args, **kwargs): + return _AyonSettingsCache.get_value_by_project(project_name) + + +def get_general_environments(): + settings = get_ayon_settings() + return json.loads(settings["core"]["environments"]) + + def get_current_project_settings(): """Project settings for current context project. @@ -225,15 +192,4 @@ def get_current_project_settings(): return get_project_settings(project_name) -def get_general_environments(): - settings = get_ayon_settings() - return json.loads(settings["core"]["environments"]) - -def get_system_settings(*args, **kwargs): - return get_ayon_system_settings() - - -def get_project_settings(project_name, *args, **kwargs): - default_settings = get_default_settings()[PROJECT_SETTINGS_KEY] - return get_ayon_project_settings(default_settings, project_name) From 7813be26955373f006b473aeb091ce7b2ac4ef3e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 15:07:58 +0100 Subject: [PATCH 274/573] publisher using product name and type --- client/ayon_core/style/style.css | 4 +- client/ayon_core/tools/publisher/constants.py | 8 +- client/ayon_core/tools/publisher/control.py | 43 +-- .../ayon_core/tools/publisher/control_qt.py | 14 +- .../publish_report_viewer/report_items.py | 6 +- .../publisher/widgets/card_view_widgets.py | 36 +-- .../tools/publisher/widgets/create_widget.py | 134 +++++----- .../publisher/widgets/list_view_widgets.py | 16 +- .../publisher/widgets/overview_widget.py | 244 +++++++++--------- .../tools/publisher/widgets/report_page.py | 2 +- .../tools/publisher/widgets/widgets.py | 70 ++--- client/ayon_core/tools/publisher/window.py | 2 +- 12 files changed, 292 insertions(+), 287 deletions(-) diff --git a/client/ayon_core/style/style.css b/client/ayon_core/style/style.css index d2bd4ec135..ce467663fc 100644 --- a/client/ayon_core/style/style.css +++ b/client/ayon_core/style/style.css @@ -1025,7 +1025,7 @@ PixmapButton:disabled { background: {color:bg-view-selection}; } -#ListViewSubsetName[state="invalid"] { +#ListViewProductName[state="invalid"] { color: {color:publisher:error}; } @@ -1192,7 +1192,7 @@ ValidationArtistMessage QLabel { #PublishCommentInput { padding: 0.2em; } -#FamilyIconLabel { +#ProductTypeIconLabel { font-size: 14pt; } #ArrowBtn, #ArrowBtn:disabled, #ArrowBtn:hover { diff --git a/client/ayon_core/tools/publisher/constants.py b/client/ayon_core/tools/publisher/constants.py index 4630eb144b..6676f14c3d 100644 --- a/client/ayon_core/tools/publisher/constants.py +++ b/client/ayon_core/tools/publisher/constants.py @@ -6,9 +6,9 @@ CONTEXT_LABEL = "Context" # Not showed anywhere - used as identifier CONTEXT_GROUP = "__ContextGroup__" -CONVERTOR_ITEM_GROUP = "Incompatible subsets" +CONVERTOR_ITEM_GROUP = "Incompatible products" -# Allowed symbols for subset name (and variant) +# Allowed symbols for product name (and variant) # - characters, numbers, unsercore and dash VARIANT_TOOLTIP = ( "Variant may contain alphabetical characters (a-Z)" @@ -24,7 +24,7 @@ SORT_VALUE_ROLE = QtCore.Qt.UserRole + 2 IS_GROUP_ROLE = QtCore.Qt.UserRole + 3 CREATOR_IDENTIFIER_ROLE = QtCore.Qt.UserRole + 4 CREATOR_THUMBNAIL_ENABLED_ROLE = QtCore.Qt.UserRole + 5 -FAMILY_ROLE = QtCore.Qt.UserRole + 6 +PRODUCT_TYPE_ROLE = QtCore.Qt.UserRole + 6 GROUP_ROLE = QtCore.Qt.UserRole + 7 CONVERTER_IDENTIFIER_ROLE = QtCore.Qt.UserRole + 8 CREATOR_SORT_ROLE = QtCore.Qt.UserRole + 9 @@ -48,7 +48,7 @@ __all__ = ( "CREATOR_IDENTIFIER_ROLE", "CREATOR_THUMBNAIL_ENABLED_ROLE", "CREATOR_SORT_ROLE", - "FAMILY_ROLE", + "PRODUCT_TYPE_ROLE", "GROUP_ROLE", "CONVERTER_IDENTIFIER_ROLE", diff --git a/client/ayon_core/tools/publisher/control.py b/client/ayon_core/tools/publisher/control.py index 5ccd428551..db7bf7db69 100644 --- a/client/ayon_core/tools/publisher/control.py +++ b/client/ayon_core/tools/publisher/control.py @@ -351,7 +351,8 @@ class PublishReportMaker: return { "name": instance.data.get("name"), "label": get_publish_instance_label(instance), - "family": instance.data["family"], + "product_type": instance.data.get("productType"), + "family": instance.data.get("family"), "families": instance.data.get("families") or [], "exists": exists, "creator_identifier": instance.data.get("creator_identifier"), @@ -879,7 +880,7 @@ class CreatorItem: self, identifier, creator_type, - family, + product_type, label, group_label, icon, @@ -894,7 +895,7 @@ class CreatorItem: ): self.identifier = identifier self.creator_type = creator_type - self.family = family + self.product_type = product_type self.label = label self.group_label = group_label self.icon = icon @@ -943,7 +944,7 @@ class CreatorItem: return cls( identifier, creator_type, - creator.family, + creator.product_type, creator.label or identifier, creator.get_group_label(), creator.get_icon(), @@ -967,7 +968,7 @@ class CreatorItem: return { "identifier": self.identifier, "creator_type": str(self.creator_type), - "family": self.family, + "product_type": self.product_type, "label": self.label, "group_label": self.group_label, "icon": self.icon, @@ -1117,7 +1118,7 @@ class AbstractPublisherController(object): pass @abstractmethod - def get_existing_subset_names(self, asset_name): + def get_existing_product_names(self, asset_name): pass @abstractmethod @@ -1152,7 +1153,7 @@ class AbstractPublisherController(object): pass @abstractmethod - def get_subset_name( + def get_product_name( self, creator_identifier, variant, @@ -1160,15 +1161,15 @@ class AbstractPublisherController(object): asset_name, instance_id=None ): - """Get subset name based on passed data. + """Get product name based on passed data. Args: creator_identifier (str): Identifier of creator which should be - responsible for subset name creation. + responsible for product name creation. variant (str): Variant value from user's input. task_name (str): Name of task for which is instance created. asset_name (str): Name of asset for which is instance created. - instance_id (Union[str, None]): Existing instance id when subset + instance_id (Union[str, None]): Existing instance id when product name is updated. """ @@ -1176,7 +1177,7 @@ class AbstractPublisherController(object): @abstractmethod def create( - self, creator_identifier, subset_name, instance_data, options + self, creator_identifier, product_name, instance_data, options ): """Trigger creation by creator identifier. @@ -1184,7 +1185,7 @@ class AbstractPublisherController(object): Args: creator_identifier (str): Identifier of Creator plugin. - subset_name (str): Calculated subset name. + product_name (str): Calculated product name. instance_data (Dict[str, Any]): Base instance data with variant, asset name and task name. options (Dict[str, Any]): Data from pre-create attributes. @@ -1830,7 +1831,7 @@ class PublisherController(BasePublisherController): ) return result - def get_existing_subset_names(self, asset_name): + def get_existing_product_names(self, asset_name): project_name = self.project_name asset_doc = self._asset_docs_cache.get_asset_by_name(asset_name) if not asset_doc: @@ -1927,7 +1928,7 @@ class PublisherController(BasePublisherController): self._emit_event( "convertors.find.failed", { - "title": "Collection of unsupported subset failed", + "title": "Collection of unsupported product failed", "failed_info": exc.failed_info } ) @@ -2069,7 +2070,7 @@ class PublisherController(BasePublisherController): )) return output - def get_subset_name( + def get_product_name( self, creator_identifier, variant, @@ -2077,15 +2078,15 @@ class PublisherController(BasePublisherController): asset_name, instance_id=None ): - """Get subset name based on passed data. + """Get product name based on passed data. Args: creator_identifier (str): Identifier of creator which should be - responsible for subset name creation. + responsible for product name creation. variant (str): Variant value from user's input. task_name (str): Name of task for which is instance created. asset_name (str): Name of asset for which is instance created. - instance_id (Union[str, None]): Existing instance id when subset + instance_id (Union[str, None]): Existing instance id when product name is updated. """ @@ -2096,7 +2097,7 @@ class PublisherController(BasePublisherController): if instance_id: instance = self.instances[instance_id] - return creator.get_subset_name( + return creator.get_product_name( variant, task_name, asset_doc, project_name, instance=instance ) @@ -2133,14 +2134,14 @@ class PublisherController(BasePublisherController): self.reset() def create( - self, creator_identifier, subset_name, instance_data, options + self, creator_identifier, product_name, instance_data, options ): """Trigger creation and refresh of instances in UI.""" success = True try: self._create_context.create_with_unified_error( - creator_identifier, subset_name, instance_data, options + creator_identifier, product_name, instance_data, options ) except CreatorsOperationFailed as exc: diff --git a/client/ayon_core/tools/publisher/control_qt.py b/client/ayon_core/tools/publisher/control_qt.py index 3d56c08131..2ddd676ec3 100644 --- a/client/ayon_core/tools/publisher/control_qt.py +++ b/client/ayon_core/tools/publisher/control_qt.py @@ -260,7 +260,7 @@ class QtRemotePublishController(BasePublisherController): def get_task_names_by_asset_names(self, asset_names): pass - def get_existing_subset_names(self, asset_name): + def get_existing_product_names(self, asset_name): pass @property @@ -300,7 +300,7 @@ class QtRemotePublishController(BasePublisherController): pass @abstractmethod - def get_subset_name( + def get_product_name( self, creator_identifier, variant, @@ -308,15 +308,15 @@ class QtRemotePublishController(BasePublisherController): asset_name, instance_id=None ): - """Get subset name based on passed data. + """Get product name based on passed data. Args: creator_identifier (str): Identifier of creator which should be - responsible for subset name creation. + responsible for product name creation. variant (str): Variant value from user's input. task_name (str): Name of task for which is instance created. asset_name (str): Name of asset for which is instance created. - instance_id (Union[str, None]): Existing instance id when subset + instance_id (Union[str, None]): Existing instance id when product name is updated. """ @@ -324,7 +324,7 @@ class QtRemotePublishController(BasePublisherController): @abstractmethod def create( - self, creator_identifier, subset_name, instance_data, options + self, creator_identifier, product_name, instance_data, options ): """Trigger creation by creator identifier. @@ -332,7 +332,7 @@ class QtRemotePublishController(BasePublisherController): Args: creator_identifier (str): Identifier of Creator plugin. - subset_name (str): Calculated subset name. + product_name (str): Calculated product name. instance_data (Dict[str, Any]): Base instance data with variant, asset name and task name. options (Dict[str, Any]): Data from pre-create attributes. diff --git a/client/ayon_core/tools/publisher/publish_report_viewer/report_items.py b/client/ayon_core/tools/publisher/publish_report_viewer/report_items.py index 206f999bac..a605b83d1b 100644 --- a/client/ayon_core/tools/publisher/publish_report_viewer/report_items.py +++ b/client/ayon_core/tools/publisher/publish_report_viewer/report_items.py @@ -33,7 +33,11 @@ class InstanceItem: def __init__(self, instance_id, instance_data, logs_by_instance_id): self._id = instance_id self.label = instance_data.get("label") or instance_data.get("name") - self.family = instance_data.get("family") + + family = instance_data.get("productType") + if not family: + family = instance_data.get("family") + self.family = family self.removed = not instance_data.get("exists", True) logs = logs_by_instance_id.get(instance_id) or [] diff --git a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py index 3396110121..47c5399cf7 100644 --- a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py @@ -179,7 +179,7 @@ class ConvertorItemsGroupWidget(BaseGroupWidget): # Remove instance widgets that are not in passed instances self._remove_all_except(items_by_id.keys()) - # Sort instances by subset name + # Sort instances by product name sorted_labels = list(sorted(items_by_label.keys())) # Add new instances to widget @@ -226,24 +226,24 @@ class InstanceGroupWidget(BaseGroupWidget): CreateContext. """ - # Store instances by id and by subset name + # Store instances by id and by product name instances_by_id = {} - instances_by_subset_name = collections.defaultdict(list) + instances_by_product_name = collections.defaultdict(list) for instance in instances: instances_by_id[instance.id] = instance - subset_name = instance["subset"] - instances_by_subset_name[subset_name].append(instance) + product_name = instance["productName"] + instances_by_product_name[product_name].append(instance) # Remove instance widgets that are not in passed instances self._remove_all_except(instances_by_id.keys()) - # Sort instances by subset name - sorted_subset_names = list(sorted(instances_by_subset_name.keys())) + # Sort instances by product name + sorted_product_names = list(sorted(instances_by_product_name.keys())) # Add new instances to widget widget_idx = 1 - for subset_names in sorted_subset_names: - for instance in instances_by_subset_name[subset_names]: + for product_names in sorted_product_names: + for instance in instances_by_product_name[product_names]: if instance.id in self._widgets_by_id: widget = self._widgets_by_id[instance.id] widget.update_instance(instance) @@ -326,7 +326,7 @@ class ContextCardWidget(CardWidget): self._group_identifier = CONTEXT_GROUP icon_widget = PublishPixmapLabel(None, self) - icon_widget.setObjectName("FamilyIconLabel") + icon_widget.setObjectName("ProductTypeIconLabel") label_widget = QtWidgets.QLabel(CONTEXT_LABEL, self) @@ -357,7 +357,7 @@ class ConvertorItemCardWidget(CardWidget): self._group_identifier = CONVERTOR_ITEM_GROUP icon_widget = IconValuePixmapLabel("fa.magic", self) - icon_widget.setObjectName("FamilyIconLabel") + icon_widget.setObjectName("ProductTypeIconLabel") label_widget = QtWidgets.QLabel(item.label, self) @@ -391,12 +391,12 @@ class InstanceCardWidget(CardWidget): self.instance = instance - self._last_subset_name = None + self._last_product_name = None self._last_variant = None self._last_label = None icon_widget = IconValuePixmapLabel(group_icon, self) - icon_widget.setObjectName("FamilyIconLabel") + icon_widget.setObjectName("ProductTypeIconLabel") context_warning = ContextWarningLabel(self) icon_layout = QtWidgets.QHBoxLayout() @@ -475,19 +475,19 @@ class InstanceCardWidget(CardWidget): self._icon_widget.setVisible(valid) self._context_warning.setVisible(not valid) - def _update_subset_name(self): + def _update_product_name(self): variant = self.instance["variant"] - subset_name = self.instance["subset"] + product_name = self.instance["productName"] label = self.instance.label if ( variant == self._last_variant - and subset_name == self._last_subset_name + and product_name == self._last_product_name and label == self._last_label ): return self._last_variant = variant - self._last_subset_name = subset_name + self._last_product_name = product_name self._last_label = label # Make `variant` bold label = html_escape(self.instance.label) @@ -506,7 +506,7 @@ class InstanceCardWidget(CardWidget): def update_instance_values(self): """Update instance data""" - self._update_subset_name() + self._update_product_name() self.set_active(self.instance["active"]) self._validate_context() diff --git a/client/ayon_core/tools/publisher/widgets/create_widget.py b/client/ayon_core/tools/publisher/widgets/create_widget.py index 8eae205882..e573e554d8 100644 --- a/client/ayon_core/tools/publisher/widgets/create_widget.py +++ b/client/ayon_core/tools/publisher/widgets/create_widget.py @@ -3,7 +3,7 @@ import re from qtpy import QtWidgets, QtCore, QtGui from ayon_core.pipeline.create import ( - SUBSET_NAME_ALLOWED_SYMBOLS, + PRODUCT_NAME_ALLOWED_SYMBOLS, PRE_CREATE_THUMBNAIL_KEY, DEFAULT_VARIANT_VALUE, TaskNotSetError, @@ -19,7 +19,7 @@ from .tasks_widget import CreateWidgetTasksWidget from .precreate_widget import PreCreateWidget from ..constants import ( VARIANT_TOOLTIP, - FAMILY_ROLE, + PRODUCT_TYPE_ROLE, CREATOR_IDENTIFIER_ROLE, CREATOR_THUMBNAIL_ENABLED_ROLE, CREATOR_SORT_ROLE, @@ -45,13 +45,13 @@ class CreatorShortDescWidget(QtWidgets.QWidget): # --- Short description widget --- icon_widget = IconValuePixmapLabel(None, self) - icon_widget.setObjectName("FamilyIconLabel") + icon_widget.setObjectName("ProductTypeIconLabel") # --- Short description inputs --- short_desc_input_widget = QtWidgets.QWidget(self) - family_label = QtWidgets.QLabel(short_desc_input_widget) - family_label.setAlignment( + product_type_label = QtWidgets.QLabel(short_desc_input_widget) + product_type_label.setAlignment( QtCore.Qt.AlignBottom | QtCore.Qt.AlignLeft ) @@ -64,7 +64,7 @@ class CreatorShortDescWidget(QtWidgets.QWidget): short_desc_input_widget ) short_desc_input_layout.setSpacing(0) - short_desc_input_layout.addWidget(family_label) + short_desc_input_layout.addWidget(product_type_label) short_desc_input_layout.addWidget(description_label) # -------------------------------- @@ -75,13 +75,13 @@ class CreatorShortDescWidget(QtWidgets.QWidget): # -------------------------------- self._icon_widget = icon_widget - self._family_label = family_label + self._product_type_label = product_type_label self._description_label = description_label def set_creator_item(self, creator_item=None): if not creator_item: self._icon_widget.set_icon_def(None) - self._family_label.setText("") + self._product_type_label.setText("") self._description_label.setText("") return @@ -89,8 +89,8 @@ class CreatorShortDescWidget(QtWidgets.QWidget): description = creator_item.description or "" self._icon_widget.set_icon_def(plugin_icon) - self._family_label.setText("{}".format(creator_item.family)) - self._family_label.setTextInteractionFlags(QtCore.Qt.NoTextInteraction) + self._product_type_label.setText("{}".format(creator_item.product_type)) + self._product_type_label.setTextInteractionFlags(QtCore.Qt.NoTextInteraction) self._description_label.setText(description) @@ -110,12 +110,12 @@ class CreateWidget(QtWidgets.QWidget): self._controller = controller self._asset_name = None - self._subset_names = None + self._product_names = None self._selected_creator = None self._prereq_available = False - name_pattern = "^[{}]*$".format(SUBSET_NAME_ALLOWED_SYMBOLS) + name_pattern = "^[{}]*$".format(PRODUCT_NAME_ALLOWED_SYMBOLS) self._name_pattern = name_pattern self._compiled_name_pattern = re.compile(name_pattern) @@ -164,19 +164,19 @@ class CreateWidget(QtWidgets.QWidget): # --- Creator attr defs --- creators_attrs_widget = QtWidgets.QWidget(creators_splitter) - # Top part - variant / subset name + thumbnail + # Top part - variant / product name + thumbnail creators_attrs_top = QtWidgets.QWidget(creators_attrs_widget) - # Basics - variant / subset name + # Basics - variant / product name creator_basics_widget = ResizeControlWidget(creators_attrs_top) - variant_subset_label = QtWidgets.QLabel( + product_variant_label = QtWidgets.QLabel( "Create options", creator_basics_widget ) - variant_subset_widget = QtWidgets.QWidget(creator_basics_widget) - # Variant and subset input - variant_widget = ResizeControlWidget(variant_subset_widget) + product_variant_widget = QtWidgets.QWidget(creator_basics_widget) + # Variant and product input + variant_widget = ResizeControlWidget(product_variant_widget) variant_widget.setObjectName("VariantInputsWidget") variant_input = QtWidgets.QLineEdit(variant_widget) @@ -196,20 +196,20 @@ class CreateWidget(QtWidgets.QWidget): variant_layout.addWidget(variant_input, 1) variant_layout.addWidget(variant_hints_btn, 0, QtCore.Qt.AlignVCenter) - subset_name_input = QtWidgets.QLineEdit(variant_subset_widget) - subset_name_input.setEnabled(False) + product_name_input = QtWidgets.QLineEdit(product_variant_widget) + product_name_input.setEnabled(False) - variant_subset_layout = QtWidgets.QFormLayout(variant_subset_widget) - variant_subset_layout.setContentsMargins(0, 0, 0, 0) - variant_subset_layout.setHorizontalSpacing(INPUTS_LAYOUT_HSPACING) - variant_subset_layout.setVerticalSpacing(INPUTS_LAYOUT_VSPACING) - variant_subset_layout.addRow("Variant", variant_widget) - variant_subset_layout.addRow("Product", subset_name_input) + product_variant_layout = QtWidgets.QFormLayout(product_variant_widget) + product_variant_layout.setContentsMargins(0, 0, 0, 0) + product_variant_layout.setHorizontalSpacing(INPUTS_LAYOUT_HSPACING) + product_variant_layout.setVerticalSpacing(INPUTS_LAYOUT_VSPACING) + product_variant_layout.addRow("Variant", variant_widget) + product_variant_layout.addRow("Product", product_name_input) creator_basics_layout = QtWidgets.QVBoxLayout(creator_basics_widget) creator_basics_layout.setContentsMargins(0, 0, 0, 0) - creator_basics_layout.addWidget(variant_subset_label, 0) - creator_basics_layout.addWidget(variant_subset_widget, 0) + creator_basics_layout.addWidget(product_variant_label, 0) + creator_basics_layout.addWidget(product_variant_widget, 0) thumbnail_widget = ThumbnailWidget(controller, creators_attrs_top) @@ -302,7 +302,7 @@ class CreateWidget(QtWidgets.QWidget): self._assets_widget = assets_widget self._tasks_widget = tasks_widget - self.subset_name_input = subset_name_input + self.product_name_input = product_name_input self.variant_input = variant_input self.variant_hints_btn = variant_hints_btn @@ -467,20 +467,20 @@ class CreateWidget(QtWidgets.QWidget): if self._asset_name and self._asset_name == asset_name: return - # Make sure `_asset_name` and `_subset_names` variables are reset + # Make sure `_asset_name` and `_product_names` variables are reset self._asset_name = asset_name - self._subset_names = None + self._product_names = None if asset_name is None: return - subset_names = self._controller.get_existing_subset_names(asset_name) + product_names = self._controller.get_existing_product_names(asset_name) - self._subset_names = subset_names - if subset_names is None: - self.subset_name_input.setText("< Asset is not set >") + self._product_names = product_names + if product_names is None: + self.product_name_input.setText("< Asset is not set >") def _refresh_creators(self): - # Refresh creators and add their families to list + # Refresh creators and add their product types to list existing_items = {} old_creators = set() for row in range(self._creators_model.rowCount()): @@ -489,7 +489,7 @@ class CreateWidget(QtWidgets.QWidget): existing_items[identifier] = item old_creators.add(identifier) - # Add new families + # Add new create plugins new_creators = set() creator_items_by_identifier = self._controller.creator_items for identifier, creator_item in creator_items_by_identifier.items(): @@ -515,11 +515,11 @@ class CreateWidget(QtWidgets.QWidget): creator_item.create_allow_thumbnail, CREATOR_THUMBNAIL_ENABLED_ROLE ) - item.setData(creator_item.family, FAMILY_ROLE) + item.setData(creator_item.product_type, PRODUCT_TYPE_ROLE) if is_new: self._creators_model.appendRow(item) - # Remove families that are no more available + # Remove create plugins that are no more available for identifier in (old_creators - new_creators): item = existing_items[identifier] self._creators_model.takeRow(item.row()) @@ -641,7 +641,7 @@ class CreateWidget(QtWidgets.QWidget): self.variant_hints_menu.addAction(variant) variant_text = default_variant or DEFAULT_VARIANT_VALUE - # Make sure subset name is updated to new plugin + # Make sure product name is updated to new plugin if variant_text == self.variant_input.text(): self._on_variant_change() else: @@ -666,8 +666,8 @@ class CreateWidget(QtWidgets.QWidget): # This should probably never happen? if not self._selected_creator: - if self.subset_name_input.text(): - self.subset_name_input.setText("") + if self.product_name_input.text(): + self.product_name_input.setText("") return if variant_value is None: @@ -676,52 +676,52 @@ class CreateWidget(QtWidgets.QWidget): 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 >") + self.product_name_input.setText("< Invalid variant >") return if not self._context_change_is_enabled(): self._create_btn.setEnabled(True) self._set_variant_state_property("") - self.subset_name_input.setText("< Valid variant >") + self.product_name_input.setText("< Valid variant >") return asset_name = self._get_asset_name() task_name = self._get_task_name() creator_idenfier = self._selected_creator.identifier - # Calculate subset name with Creator plugin + # Calculate product name with Creator plugin try: - subset_name = self._controller.get_subset_name( + product_name = self._controller.get_product_name( creator_idenfier, variant_value, task_name, asset_name ) except TaskNotSetError: self._create_btn.setEnabled(False) self._set_variant_state_property("invalid") - self.subset_name_input.setText("< Missing task >") + self.product_name_input.setText("< Missing task >") return - self.subset_name_input.setText(subset_name) + self.product_name_input.setText(product_name) self._create_btn.setEnabled(True) - self._validate_subset_name(subset_name, variant_value) + self._validate_product_name(product_name, variant_value) - def _validate_subset_name(self, subset_name, variant_value): - # Get all subsets of the current asset - if self._subset_names: - existing_subset_names = set(self._subset_names) + def _validate_product_name(self, product_name, variant_value): + # Get all products of the current asset + if self._product_names: + existing_product_names = set(self._product_names) else: - existing_subset_names = set() - existing_subset_names_low = set( + existing_product_names = set() + existing_product_names_low = set( _name.lower() - for _name in existing_subset_names + for _name in existing_product_names ) # Replace compare_regex = re.compile(re.sub( - variant_value, "(.+)", subset_name, flags=re.IGNORECASE + variant_value, "(.+)", product_name, flags=re.IGNORECASE )) variant_hints = set() if variant_value: - for _name in existing_subset_names: + for _name in existing_product_names: _result = compare_regex.search(_name) if _result: variant_hints |= set(_result.groups()) @@ -741,12 +741,12 @@ class CreateWidget(QtWidgets.QWidget): action = self.variant_hints_menu.addAction(variant_hint) self.variant_hints_group.addAction(action) - # Indicate subset existence + # Indicate product existence if not variant_value: property_value = "empty" - elif subset_name.lower() in existing_subset_names_low: - # validate existence of subset name with lowered text + elif product_name.lower() in existing_product_names_low: + # validate existence of product name with lowered text # - "renderMain" vs. "rendermain" mean same path item for # windows property_value = "exists" @@ -794,14 +794,14 @@ class CreateWidget(QtWidgets.QWidget): index = indexes[0] creator_identifier = index.data(CREATOR_IDENTIFIER_ROLE) - family = index.data(FAMILY_ROLE) + product_type = index.data(PRODUCT_TYPE_ROLE) variant = self.variant_input.text() - # Care about subset name only if context change is enabled - subset_name = None + # Care about product name only if context change is enabled + product_name = None asset_name = None task_name = None if self._context_change_is_enabled(): - subset_name = self.subset_name_input.text() + product_name = self.product_name_input.text() asset_name = self._get_asset_name() task_name = self._get_task_name() @@ -817,12 +817,12 @@ class CreateWidget(QtWidgets.QWidget): "folderPath": asset_name, "task": task_name, "variant": variant, - "family": family + "productType": product_type } success = self._controller.create( creator_identifier, - subset_name, + product_name, instance_data, pre_create_data ) diff --git a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py index fc76c47334..3322a73be6 100644 --- a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py @@ -123,8 +123,8 @@ class InstanceListItemWidget(QtWidgets.QWidget): instance_label = html_escape(instance_label) - subset_name_label = QtWidgets.QLabel(instance_label, self) - subset_name_label.setObjectName("ListViewSubsetName") + product_name_label = QtWidgets.QLabel(instance_label, self) + product_name_label.setObjectName("ListViewProductName") active_checkbox = NiceCheckbox(parent=self) active_checkbox.setChecked(instance["active"]) @@ -132,17 +132,17 @@ class InstanceListItemWidget(QtWidgets.QWidget): layout = QtWidgets.QHBoxLayout(self) content_margins = layout.contentsMargins() layout.setContentsMargins(content_margins.left() + 2, 0, 2, 0) - layout.addWidget(subset_name_label) + layout.addWidget(product_name_label) layout.addStretch(1) layout.addWidget(active_checkbox) self.setAttribute(QtCore.Qt.WA_TranslucentBackground) - subset_name_label.setAttribute(QtCore.Qt.WA_TranslucentBackground) + product_name_label.setAttribute(QtCore.Qt.WA_TranslucentBackground) active_checkbox.setAttribute(QtCore.Qt.WA_TranslucentBackground) active_checkbox.stateChanged.connect(self._on_active_change) - self._instance_label_widget = subset_name_label + self._instance_label_widget = product_name_label self._active_checkbox = active_checkbox self._has_valid_context = None @@ -185,7 +185,7 @@ class InstanceListItemWidget(QtWidgets.QWidget): def update_instance_values(self): """Update instance data propagated to widgets.""" - # Check subset name + # Check product name label = self.instance.label if label != self._instance_label_widget.text(): self._instance_label_widget.setText(html_escape(label)) @@ -631,8 +631,8 @@ class InstanceListView(AbstractInstanceView): # Create new item and store it as new item = QtGui.QStandardItem() - item.setData(instance["subset"], SORT_VALUE_ROLE) - item.setData(instance["subset"], GROUP_ROLE) + item.setData(instance["productName"], SORT_VALUE_ROLE) + item.setData(instance["productName"], GROUP_ROLE) item.setData(instance_id, INSTANCE_ID_ROLE) new_items.append(item) new_items_with_instance.append((item, instance)) diff --git a/client/ayon_core/tools/publisher/widgets/overview_widget.py b/client/ayon_core/tools/publisher/widgets/overview_widget.py index f1b271850a..dd82185830 100644 --- a/client/ayon_core/tools/publisher/widgets/overview_widget.py +++ b/client/ayon_core/tools/publisher/widgets/overview_widget.py @@ -5,7 +5,7 @@ from .border_label_widget import BorderedLabelWidget from .card_view_widgets import InstanceCardView from .list_view_widgets import InstanceListView from .widgets import ( - SubsetAttributesWidget, + ProductAttributesWidget, CreateInstanceBtn, RemoveInstanceBtn, ChangeViewBtn, @@ -28,72 +28,72 @@ class OverviewWidget(QtWidgets.QFrame): self._refreshing_instances = False self._controller = controller - subset_content_widget = QtWidgets.QWidget(self) + product_content_widget = QtWidgets.QWidget(self) - create_widget = CreateWidget(controller, subset_content_widget) + create_widget = CreateWidget(controller, product_content_widget) - # --- Created Subsets/Instances --- + # --- Created Products/Instances --- # Common widget for creation and overview - subset_views_widget = BorderedLabelWidget( + product_views_widget = BorderedLabelWidget( "Products to publish", - subset_content_widget + product_content_widget ) - subset_view_cards = InstanceCardView(controller, subset_views_widget) - subset_list_view = InstanceListView(controller, subset_views_widget) + product_view_cards = InstanceCardView(controller, product_views_widget) + product_list_view = InstanceListView(controller, product_views_widget) - subset_views_layout = QtWidgets.QStackedLayout() - subset_views_layout.addWidget(subset_view_cards) - subset_views_layout.addWidget(subset_list_view) - subset_views_layout.setCurrentWidget(subset_view_cards) + product_views_layout = QtWidgets.QStackedLayout() + product_views_layout.addWidget(product_view_cards) + product_views_layout.addWidget(product_list_view) + product_views_layout.setCurrentWidget(product_view_cards) - # Buttons at the bottom of subset view - create_btn = CreateInstanceBtn(subset_views_widget) - delete_btn = RemoveInstanceBtn(subset_views_widget) - change_view_btn = ChangeViewBtn(subset_views_widget) + # Buttons at the bottom of product view + create_btn = CreateInstanceBtn(product_views_widget) + delete_btn = RemoveInstanceBtn(product_views_widget) + change_view_btn = ChangeViewBtn(product_views_widget) # --- Overview --- - # Subset details widget - subset_attributes_wrap = BorderedLabelWidget( - "Publish options", subset_content_widget + # pProduct details widget + product_attributes_wrap = BorderedLabelWidget( + "Publish options", product_content_widget ) - subset_attributes_widget = SubsetAttributesWidget( - controller, subset_attributes_wrap + product_attributes_widget = ProductAttributesWidget( + controller, product_attributes_wrap ) - subset_attributes_wrap.set_center_widget(subset_attributes_widget) + product_attributes_wrap.set_center_widget(product_attributes_widget) - # Layout of buttons at the bottom of subset view - subset_view_btns_layout = QtWidgets.QHBoxLayout() - subset_view_btns_layout.setContentsMargins(0, 5, 0, 0) - subset_view_btns_layout.addWidget(create_btn) - subset_view_btns_layout.addSpacing(5) - subset_view_btns_layout.addWidget(delete_btn) - subset_view_btns_layout.addStretch(1) - subset_view_btns_layout.addWidget(change_view_btn) + # Layout of buttons at the bottom of product view + product_view_btns_layout = QtWidgets.QHBoxLayout() + product_view_btns_layout.setContentsMargins(0, 5, 0, 0) + product_view_btns_layout.addWidget(create_btn) + product_view_btns_layout.addSpacing(5) + product_view_btns_layout.addWidget(delete_btn) + product_view_btns_layout.addStretch(1) + product_view_btns_layout.addWidget(change_view_btn) # Layout of view and buttons - # - widget 'subset_view_widget' is necessary + # - widget 'product_view_widget' is necessary # - only layout won't be resized automatically to minimum size hint # on child resize request! - subset_view_widget = QtWidgets.QWidget(subset_views_widget) - subset_view_layout = QtWidgets.QVBoxLayout(subset_view_widget) - subset_view_layout.setContentsMargins(0, 0, 0, 0) - subset_view_layout.addLayout(subset_views_layout, 1) - subset_view_layout.addLayout(subset_view_btns_layout, 0) + product_view_widget = QtWidgets.QWidget(product_views_widget) + product_view_layout = QtWidgets.QVBoxLayout(product_view_widget) + product_view_layout.setContentsMargins(0, 0, 0, 0) + product_view_layout.addLayout(product_views_layout, 1) + product_view_layout.addLayout(product_view_btns_layout, 0) - subset_views_widget.set_center_widget(subset_view_widget) + product_views_widget.set_center_widget(product_view_widget) - # Whole subset layout with attributes and details - subset_content_layout = QtWidgets.QHBoxLayout(subset_content_widget) - subset_content_layout.setContentsMargins(0, 0, 0, 0) - subset_content_layout.addWidget(create_widget, 7) - subset_content_layout.addWidget(subset_views_widget, 3) - subset_content_layout.addWidget(subset_attributes_wrap, 7) + # Whole product layout with attributes and details + product_content_layout = QtWidgets.QHBoxLayout(product_content_widget) + product_content_layout.setContentsMargins(0, 0, 0, 0) + product_content_layout.addWidget(create_widget, 7) + product_content_layout.addWidget(product_views_widget, 3) + product_content_layout.addWidget(product_attributes_wrap, 7) - # Subset frame layout + # Product frame layout main_layout = QtWidgets.QVBoxLayout(self) main_layout.setContentsMargins(0, 0, 0, 0) - main_layout.addWidget(subset_content_widget, 1) + main_layout.addWidget(product_content_widget, 1) change_anim = QtCore.QVariantAnimation() change_anim.setStartValue(float(0)) @@ -101,7 +101,7 @@ class OverviewWidget(QtWidgets.QFrame): change_anim.setDuration(self.anim_duration) change_anim.setEasingCurve(QtCore.QEasingCurve.InOutQuad) - # --- Calbacks for instances/subsets view --- + # --- Calbacks for instances/products view --- create_btn.clicked.connect(self._on_create_clicked) delete_btn.clicked.connect(self._on_delete_clicked) change_view_btn.clicked.connect(self._on_change_view_clicked) @@ -110,24 +110,24 @@ class OverviewWidget(QtWidgets.QFrame): change_anim.finished.connect(self._on_change_anim_finished) # Selection changed - subset_list_view.selection_changed.connect( - self._on_subset_change + product_list_view.selection_changed.connect( + self._on_product_change ) - subset_view_cards.selection_changed.connect( - self._on_subset_change + product_view_cards.selection_changed.connect( + self._on_product_change ) # Active instances changed - subset_list_view.active_changed.connect( + product_list_view.active_changed.connect( self._on_active_changed ) - subset_view_cards.active_changed.connect( + product_view_cards.active_changed.connect( self._on_active_changed ) # Instance context has changed - subset_attributes_widget.instance_context_changed.connect( + product_attributes_widget.instance_context_changed.connect( self._on_instance_context_change ) - subset_attributes_widget.convert_requested.connect( + product_attributes_widget.convert_requested.connect( self._on_convert_requested ) @@ -145,26 +145,26 @@ class OverviewWidget(QtWidgets.QFrame): "instances.refresh.finished", self._on_instances_refresh ) - self._subset_content_widget = subset_content_widget - self._subset_content_layout = subset_content_layout + self._product_content_widget = product_content_widget + self._product_content_layout = product_content_layout - self._subset_view_cards = subset_view_cards - self._subset_list_view = subset_list_view - self._subset_views_layout = subset_views_layout + self._product_view_cards = product_view_cards + self._product_list_view = product_list_view + self._product_views_layout = product_views_layout self._create_btn = create_btn self._delete_btn = delete_btn - self._subset_attributes_widget = subset_attributes_widget + self._product_attributes_widget = product_attributes_widget self._create_widget = create_widget - self._subset_views_widget = subset_views_widget - self._subset_attributes_wrap = subset_attributes_wrap + self._product_views_widget = product_views_widget + self._product_attributes_wrap = product_attributes_wrap self._change_anim = change_anim # Start in create mode self._current_state = "create" - subset_attributes_wrap.setVisible(False) + product_attributes_wrap.setVisible(False) def make_sure_animation_is_finished(self): if self._change_anim.state() == QtCore.QAbstractAnimation.Running: @@ -193,18 +193,18 @@ class OverviewWidget(QtWidgets.QFrame): self._start_animation() def _start_animation(self): - views_geo = self._subset_views_widget.geometry() - layout_spacing = self._subset_content_layout.spacing() + views_geo = self._product_views_widget.geometry() + layout_spacing = self._product_content_layout.spacing() if self._create_widget.isVisible(): create_geo = self._create_widget.geometry() - subset_geo = QtCore.QRect(create_geo) - subset_geo.moveTop(views_geo.top()) - subset_geo.moveLeft(views_geo.right() + layout_spacing) - self._subset_attributes_wrap.setVisible(True) + product_geo = QtCore.QRect(create_geo) + product_geo.moveTop(views_geo.top()) + product_geo.moveLeft(views_geo.right() + layout_spacing) + self._product_attributes_wrap.setVisible(True) - elif self._subset_attributes_wrap.isVisible(): - subset_geo = self._subset_attributes_wrap.geometry() - create_geo = QtCore.QRect(subset_geo) + elif self._product_attributes_wrap.isVisible(): + product_geo = self._product_attributes_wrap.geometry() + create_geo = QtCore.QRect(product_geo) create_geo.moveTop(views_geo.top()) create_geo.moveRight(views_geo.left() - (layout_spacing + 1)) self._create_widget.setVisible(True) @@ -212,26 +212,26 @@ class OverviewWidget(QtWidgets.QFrame): self._change_anim.start() return - while self._subset_content_layout.count(): - self._subset_content_layout.takeAt(0) - self._subset_views_widget.setGeometry(views_geo) + while self._product_content_layout.count(): + self._product_content_layout.takeAt(0) + self._product_views_widget.setGeometry(views_geo) self._create_widget.setGeometry(create_geo) - self._subset_attributes_wrap.setGeometry(subset_geo) + self._product_attributes_wrap.setGeometry(product_geo) self._change_anim.start() - def get_subset_views_geo(self): - parent = self._subset_views_widget.parent() - global_pos = parent.mapToGlobal(self._subset_views_widget.pos()) + def get_product_views_geo(self): + parent = self._product_views_widget.parent() + global_pos = parent.mapToGlobal(self._product_views_widget.pos()) return QtCore.QRect( global_pos.x(), global_pos.y(), - self._subset_views_widget.width(), - self._subset_views_widget.height() + self._product_views_widget.width(), + self._product_views_widget.height() ) def has_items(self): - view = self._subset_views_layout.currentWidget() + view = self._product_views_layout.currentWidget() return view.has_items() def _on_create_clicked(self): @@ -272,7 +272,7 @@ class OverviewWidget(QtWidgets.QFrame): def _on_change_view_clicked(self): self._change_view_type() - def _on_subset_change(self, *_args): + def _on_product_change(self, *_args): # Ignore changes if in middle of refreshing if self._refreshing_instances: return @@ -289,7 +289,7 @@ class OverviewWidget(QtWidgets.QFrame): instances_by_id[instance_id] for instance_id in instance_ids ] - self._subset_attributes_widget.set_current_instances( + self._product_attributes_widget.set_current_instances( instances, context_selected, convertor_identifiers ) @@ -300,59 +300,59 @@ class OverviewWidget(QtWidgets.QFrame): def _on_change_anim(self, value): self._create_widget.setVisible(True) - self._subset_attributes_wrap.setVisible(True) - layout_spacing = self._subset_content_layout.spacing() + self._product_attributes_wrap.setVisible(True) + layout_spacing = self._product_content_layout.spacing() content_width = ( - self._subset_content_widget.width() - (layout_spacing * 2) + self._product_content_widget.width() - (layout_spacing * 2) ) - content_height = self._subset_content_widget.height() + content_height = self._product_content_widget.height() views_width = max( int(content_width * 0.3), - self._subset_views_widget.minimumWidth() + self._product_views_widget.minimumWidth() ) width = content_width - views_width # Visible widths of other widgets - subset_attrs_width = int((float(width) / self.anim_end_value) * value) - create_width = width - subset_attrs_width + product_attrs_width = int((float(width) / self.anim_end_value) * value) + create_width = width - product_attrs_width views_geo = QtCore.QRect( create_width + layout_spacing, 0, views_width, content_height ) create_geo = QtCore.QRect(0, 0, width, content_height) - subset_attrs_geo = QtCore.QRect(create_geo) + product_attrs_geo = QtCore.QRect(create_geo) create_geo.moveRight(views_geo.left() - (layout_spacing + 1)) - subset_attrs_geo.moveLeft(views_geo.right() + layout_spacing) + product_attrs_geo.moveLeft(views_geo.right() + layout_spacing) - self._subset_views_widget.setGeometry(views_geo) + self._product_views_widget.setGeometry(views_geo) self._create_widget.setGeometry(create_geo) - self._subset_attributes_wrap.setGeometry(subset_attrs_geo) + self._product_attributes_wrap.setGeometry(product_attrs_geo) def _on_change_anim_finished(self): self._change_visibility_for_state() - self._subset_content_layout.addWidget(self._create_widget, 7) - self._subset_content_layout.addWidget(self._subset_views_widget, 3) - self._subset_content_layout.addWidget(self._subset_attributes_wrap, 7) + self._product_content_layout.addWidget(self._create_widget, 7) + self._product_content_layout.addWidget(self._product_views_widget, 3) + self._product_content_layout.addWidget(self._product_attributes_wrap, 7) def _change_visibility_for_state(self): self._create_widget.setVisible( self._current_state == "create" ) - self._subset_attributes_wrap.setVisible( + self._product_attributes_wrap.setVisible( self._current_state == "publish" ) def _on_instance_context_change(self): - current_idx = self._subset_views_layout.currentIndex() - for idx in range(self._subset_views_layout.count()): + current_idx = self._product_views_layout.currentIndex() + for idx in range(self._product_views_layout.count()): if idx == current_idx: continue - widget = self._subset_views_layout.widget(idx) + widget = self._product_views_layout.widget(idx) if widget.refreshed: widget.set_refreshed(False) - current_widget = self._subset_views_layout.widget(current_idx) + current_widget = self._product_views_layout.widget(current_idx) current_widget.refresh_instance_states() self.instance_context_changed.emit() @@ -369,7 +369,7 @@ class OverviewWidget(QtWidgets.QFrame): convertor plugins. """ - view = self._subset_views_layout.currentWidget() + view = self._product_views_layout.currentWidget() return view.get_selected_items() def get_selected_legacy_convertors(self): @@ -384,11 +384,11 @@ class OverviewWidget(QtWidgets.QFrame): return convertor_identifiers def _change_view_type(self): - idx = self._subset_views_layout.currentIndex() - new_idx = (idx + 1) % self._subset_views_layout.count() + idx = self._product_views_layout.currentIndex() + new_idx = (idx + 1) % self._product_views_layout.count() - old_view = self._subset_views_layout.currentWidget() - new_view = self._subset_views_layout.widget(new_idx) + old_view = self._product_views_layout.currentWidget() + new_view = self._product_views_layout.widget(new_idx) if not new_view.refreshed: new_view.refresh() @@ -403,9 +403,9 @@ class OverviewWidget(QtWidgets.QFrame): instance_ids, context_selected, convertor_identifiers ) - self._subset_views_layout.setCurrentIndex(new_idx) + self._product_views_layout.setCurrentIndex(new_idx) - self._on_subset_change() + self._on_product_change() def _refresh_instances(self): if self._refreshing_instances: @@ -413,41 +413,41 @@ class OverviewWidget(QtWidgets.QFrame): self._refreshing_instances = True - for idx in range(self._subset_views_layout.count()): - widget = self._subset_views_layout.widget(idx) + for idx in range(self._product_views_layout.count()): + widget = self._product_views_layout.widget(idx) widget.set_refreshed(False) - view = self._subset_views_layout.currentWidget() + view = self._product_views_layout.currentWidget() view.refresh() view.set_refreshed(True) self._refreshing_instances = False # Force to change instance and refresh details - self._on_subset_change() + self._on_product_change() def _on_publish_start(self): """Publish started.""" self._create_btn.setEnabled(False) - self._subset_attributes_wrap.setEnabled(False) - for idx in range(self._subset_views_layout.count()): - widget = self._subset_views_layout.widget(idx) + self._product_attributes_wrap.setEnabled(False) + for idx in range(self._product_views_layout.count()): + widget = self._product_views_layout.widget(idx) widget.set_active_toggle_enabled(False) def _on_controller_reset_start(self): """Controller reset started.""" - for idx in range(self._subset_views_layout.count()): - widget = self._subset_views_layout.widget(idx) + for idx in range(self._product_views_layout.count()): + widget = self._product_views_layout.widget(idx) widget.set_active_toggle_enabled(True) def _on_publish_reset(self): """Context in controller has been reseted.""" self._create_btn.setEnabled(True) - self._subset_attributes_wrap.setEnabled(True) - self._subset_content_widget.setEnabled(self._controller.host_is_valid) + self._product_attributes_wrap.setEnabled(True) + self._product_content_widget.setEnabled(self._controller.host_is_valid) def _on_instances_refresh(self): """Controller refreshed instances.""" @@ -457,5 +457,5 @@ class OverviewWidget(QtWidgets.QFrame): # Give a change to process Resize Request QtWidgets.QApplication.processEvents() # Trigger update geometry of - widget = self._subset_views_layout.currentWidget() + widget = self._product_views_layout.currentWidget() widget.updateGeometry() diff --git a/client/ayon_core/tools/publisher/widgets/report_page.py b/client/ayon_core/tools/publisher/widgets/report_page.py index c4a37da887..1bbe8033f9 100644 --- a/client/ayon_core/tools/publisher/widgets/report_page.py +++ b/client/ayon_core/tools/publisher/widgets/report_page.py @@ -744,7 +744,7 @@ class PublishInstanceCardWidget(BaseClickableFrame): self.setObjectName("CardViewWidget") icon_widget = IconValuePixmapLabel(icon, self) - icon_widget.setObjectName("FamilyIconLabel") + icon_widget.setObjectName("ProductTypeIconLabel") label_widget = QtWidgets.QLabel(instance.label, self) diff --git a/client/ayon_core/tools/publisher/widgets/widgets.py b/client/ayon_core/tools/publisher/widgets/widgets.py index bd5ab250bd..c95f7d8045 100644 --- a/client/ayon_core/tools/publisher/widgets/widgets.py +++ b/client/ayon_core/tools/publisher/widgets/widgets.py @@ -22,7 +22,7 @@ from ayon_core.tools.utils import ( ) from ayon_core.style import get_objected_colors from ayon_core.pipeline.create import ( - SUBSET_NAME_ALLOWED_SYMBOLS, + PRODUCT_NAME_ALLOWED_SYMBOLS, TaskNotSetError, ) from .thumbnail_widget import ThumbnailWidget @@ -127,7 +127,7 @@ class ContextWarningLabel(PublishPixmapLabel): self.setToolTip( "Contain invalid context. Please check details." ) - self.setObjectName("FamilyIconLabel") + self.setObjectName("ProductTypeIconLabel") class PublishIconBtn(IconButton): @@ -901,7 +901,7 @@ class VariantInputWidget(PlaceholderLineEdit): self.setObjectName("VariantInput") self.setToolTip(VARIANT_TOOLTIP) - name_pattern = "^[{}]*$".format(SUBSET_NAME_ALLOWED_SYMBOLS) + name_pattern = "^[{}]*$".format(PRODUCT_NAME_ALLOWED_SYMBOLS) self._name_pattern = name_pattern self._compiled_name_pattern = re.compile(name_pattern) @@ -1077,10 +1077,10 @@ class MultipleItemWidget(QtWidgets.QWidget): class GlobalAttrsWidget(QtWidgets.QWidget): - """Global attributes mainly to define context and subset name of instances. + """Global attributes mainly to define context and product name of instances. - Subset name is or may be affected on context. Gives abiity to modify - context and subset name of instance. This change is not autopromoted but + product name is or may be affected on context. Gives abiity to modify + context and product name of instance. This change is not autopromoted but must be submitted. Warning: Until artist hit `Submit` changes must not be propagated to @@ -1088,10 +1088,10 @@ class GlobalAttrsWidget(QtWidgets.QWidget): Global attributes contain these widgets: Variant: [ text input ] - Asset: [ asset dialog ] + Folder: [ folder dialog ] Task: [ combobox ] - Family: [ immutable ] - Subset name: [ immutable ] + Product type: [ immutable ] + product name: [ immutable ] [Submit] [Cancel] """ instance_context_changed = QtCore.Signal() @@ -1108,8 +1108,8 @@ class GlobalAttrsWidget(QtWidgets.QWidget): variant_input = VariantInputWidget(self) asset_value_widget = AssetsField(controller, self) task_value_widget = TasksCombobox(controller, self) - family_value_widget = MultipleItemWidget(self) - subset_value_widget = MultipleItemWidget(self) + product_type_value_widget = MultipleItemWidget(self) + product_value_widget = MultipleItemWidget(self) variant_input.set_multiselection_text(self.multiselection_text) asset_value_widget.set_multiselection_text(self.multiselection_text) @@ -1118,8 +1118,8 @@ class GlobalAttrsWidget(QtWidgets.QWidget): variant_input.set_value() asset_value_widget.set_selected_items() task_value_widget.set_selected_items() - family_value_widget.set_value() - subset_value_widget.set_value() + product_type_value_widget.set_value() + product_value_widget.set_value() submit_btn = QtWidgets.QPushButton("Confirm", self) cancel_btn = QtWidgets.QPushButton("Cancel", self) @@ -1139,8 +1139,8 @@ class GlobalAttrsWidget(QtWidgets.QWidget): main_layout.addRow("Variant", variant_input) main_layout.addRow("Folder", asset_value_widget) main_layout.addRow("Task", task_value_widget) - main_layout.addRow("Product type", family_value_widget) - main_layout.addRow("Product name", subset_value_widget) + main_layout.addRow("Product type", product_type_value_widget) + main_layout.addRow("Product name", product_value_widget) main_layout.addRow(btns_layout) variant_input.value_changed.connect(self._on_variant_change) @@ -1152,8 +1152,8 @@ class GlobalAttrsWidget(QtWidgets.QWidget): self.variant_input = variant_input self.asset_value_widget = asset_value_widget self.task_value_widget = task_value_widget - self.family_value_widget = family_value_widget - self.subset_value_widget = subset_value_widget + self.product_type_value_widget = product_type_value_widget + self.product_value_widget = product_value_widget self.submit_btn = submit_btn self.cancel_btn = cancel_btn @@ -1172,7 +1172,7 @@ class GlobalAttrsWidget(QtWidgets.QWidget): if self.task_value_widget.has_value_changed(): task_name = self.task_value_widget.get_selected_items()[0] - subset_names = set() + product_names = set() invalid_tasks = False asset_names = [] for instance in self._current_instances: @@ -1190,7 +1190,7 @@ class GlobalAttrsWidget(QtWidgets.QWidget): asset_names.append(new_asset_name) try: - new_subset_name = self._controller.get_subset_name( + new_product_name = self._controller.get_product_name( instance.creator_identifier, new_variant_value, new_task_name, @@ -1201,10 +1201,10 @@ class GlobalAttrsWidget(QtWidgets.QWidget): except TaskNotSetError: invalid_tasks = True instance.set_task_invalid(True) - subset_names.add(instance["subset"]) + product_names.add(instance["productName"]) continue - subset_names.add(new_subset_name) + product_names.add(new_product_name) if variant_value is not None: instance["variant"] = variant_value @@ -1216,12 +1216,12 @@ class GlobalAttrsWidget(QtWidgets.QWidget): instance["task"] = task_name or None instance.set_task_invalid(False) - instance["subset"] = new_subset_name + instance["productName"] = new_product_name if invalid_tasks: self.task_value_widget.set_invalid_empty_task() - self.subset_value_widget.set_value(subset_names) + self.product_value_widget.set_value(product_names) self._set_btns_enabled(False) self._set_btns_visible(invalid_tasks) @@ -1292,8 +1292,8 @@ class GlobalAttrsWidget(QtWidgets.QWidget): asset_names = set() variants = set() - families = set() - subset_names = set() + product_types = set() + product_names = set() editable = True if len(instances) == 0: @@ -1306,12 +1306,12 @@ class GlobalAttrsWidget(QtWidgets.QWidget): editable = False variants.add(instance.get("variant") or self.unknown_value) - families.add(instance.get("family") or self.unknown_value) + product_types.add(instance.get("productType") or self.unknown_value) asset_name = instance.get("folderPath") or 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) + product_names.add(instance.get("productName") or self.unknown_value) self.variant_input.set_value(variants) @@ -1319,8 +1319,8 @@ class GlobalAttrsWidget(QtWidgets.QWidget): self.asset_value_widget.set_selected_items(asset_names) # Set context of task widget self.task_value_widget.set_selected_items(asset_task_combinations) - self.family_value_widget.set_value(families) - self.subset_value_widget.set_value(subset_names) + self.product_type_value_widget.set_value(product_types) + self.product_value_widget.set_value(product_names) self.variant_input.setEnabled(editable) self.asset_value_widget.setEnabled(editable) @@ -1476,7 +1476,7 @@ class PublishPluginAttrsWidget(QtWidgets.QWidget): Widgets are disabled if context of instance is not valid. Definitions are shown for all instance no matter if they have different - families. Similar definitions are merged into one (different label + product types. Similar definitions are merged into one (different label does not count). """ @@ -1624,7 +1624,7 @@ class PublishPluginAttrsWidget(QtWidgets.QWidget): plugin_val[attr_def.key] = value -class SubsetAttributesWidget(QtWidgets.QWidget): +class ProductAttributesWidget(QtWidgets.QWidget): """Wrapper widget where attributes of instance/s are modified. ┌─────────────────┬─────────────┐ │ Global │ │ @@ -1640,7 +1640,7 @@ class SubsetAttributesWidget(QtWidgets.QWidget): convert_requested = QtCore.Signal() def __init__(self, controller, parent): - super(SubsetAttributesWidget, self).__init__(parent) + super(ProductAttributesWidget, self).__init__(parent) # TOP PART top_widget = QtWidgets.QWidget(self) @@ -1666,9 +1666,9 @@ class SubsetAttributesWidget(QtWidgets.QWidget): # Set the label text with 'setText' to apply html convert_label.setText( ( - "Found old publishable subsets" + "Found old publishable products" " incompatible with new publisher." - "

Press the update subsets button" + "

Press the update products button" " to automatically update them" " to be able to publish again." ) @@ -1677,7 +1677,7 @@ class SubsetAttributesWidget(QtWidgets.QWidget): convert_label.setAlignment(QtCore.Qt.AlignCenter) convert_btn = QtWidgets.QPushButton( - "Update subsets", convert_widget + "Update products", convert_widget ) convert_separator = QtWidgets.QFrame(convert_widget) convert_separator.setObjectName("Separator") diff --git a/client/ayon_core/tools/publisher/window.py b/client/ayon_core/tools/publisher/window.py index f4dadf7f67..123864ff6c 100644 --- a/client/ayon_core/tools/publisher/window.py +++ b/client/ayon_core/tools/publisher/window.py @@ -1029,7 +1029,7 @@ class PublisherWindow(QtWidgets.QDialog): under_mouse = False my_pos = self.mapFromGlobal(global_pos) if self.rect().contains(my_pos): - widget_geo = self._overview_widget.get_subset_views_geo() + widget_geo = self._overview_widget.get_product_views_geo() widget_x = widget_geo.left() + (widget_geo.width() * 0.5) under_mouse = widget_x < global_pos.x() self._create_overlay_button.set_under_mouse(under_mouse) From 591819b5583428fd47a5c33bbbdcbb0637da88dc Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 15:26:25 +0100 Subject: [PATCH 275/573] deadline using product name and type --- .../publish/submit_aftereffects_deadline.py | 2 +- .../publish/submit_celaction_deadline.py | 2 +- .../plugins/publish/submit_fusion_deadline.py | 2 +- .../publish/submit_houdini_render_deadline.py | 18 +++---- .../plugins/publish/submit_nuke_deadline.py | 12 +++-- .../publish/submit_publish_cache_job.py | 47 ++++++++++--------- .../plugins/publish/submit_publish_job.py | 46 ++++++++++-------- 7 files changed, 72 insertions(+), 57 deletions(-) diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_aftereffects_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_aftereffects_deadline.py index 3537c3099d..a284464009 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_aftereffects_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_aftereffects_deadline.py @@ -112,7 +112,7 @@ class AfterEffectsSubmitDeadline( 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 + # expects filename in format project_folder_product_version.FRAME.ext render_dir = os.path.dirname(render_path) file_name = os.path.basename(render_path) hashed = '[{}]'.format(len(frame) * "#") diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_celaction_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_celaction_deadline.py index 47a0a25755..bc3636da63 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_celaction_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_celaction_deadline.py @@ -75,7 +75,7 @@ class CelactionSubmitDeadline(pyblish.api.InstancePlugin): script_name = os.path.basename(script_path) for item in instance.context: - if "workfile" in item.data["family"]: + if "workfile" in item.data["productType"]: msg = "Workfile (scene) must be published along" assert item.data["publish"] is True, msg diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_fusion_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_fusion_deadline.py index 9c125db174..837ed91c60 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_fusion_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_fusion_deadline.py @@ -104,7 +104,7 @@ class FusionSubmitDeadline( # Collect all saver instances in context that are to be rendered saver_instances = [] for instance in context: - if instance.data["family"] != "render": + if instance.data["productType"] != "render": # Allow only saver family instances continue diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py index 45c252dd56..486fdfd634 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py @@ -145,7 +145,9 @@ class HoudiniSubmitDeadline( if split_render_job and not is_export_job: # Convert from family to Deadline plugin name # i.e., arnold_rop -> Arnold - plugin = instance.data["family"].replace("_rop", "").capitalize() + plugin = ( + instance.data["productType"].replace("_rop", "").capitalize() + ) else: plugin = "Houdini" if split_render_job: @@ -252,21 +254,21 @@ class HoudiniSubmitDeadline( # Output driver to render if job_type == "render": - family = instance.data.get("family") - if family == "arnold_rop": + product_type = instance.data.get("productType") + if product_type == "arnold_rop": plugin_info = ArnoldRenderDeadlinePluginInfo( InputFile=instance.data["ifdFile"] ) - elif family == "mantra_rop": + elif product_type == "mantra_rop": plugin_info = MantraRenderDeadlinePluginInfo( SceneFile=instance.data["ifdFile"], Version=hou_major_minor, ) - elif family == "vray_rop": + elif product_type == "vray_rop": plugin_info = VrayRenderPluginInfo( InputFilename=instance.data["ifdFile"], ) - elif family == "redshift_rop": + elif product_type == "redshift_rop": plugin_info = RedshiftRenderPluginInfo( SceneFile=instance.data["ifdFile"] ) @@ -287,8 +289,8 @@ class HoudiniSubmitDeadline( else: self.log.error( - "Family '%s' not supported yet to split render job", - family + "Product type '%s' not supported yet to split render job", + product_type ) return else: diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_nuke_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_nuke_deadline.py index b314232a42..a3111454b3 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_nuke_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_nuke_deadline.py @@ -183,11 +183,13 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin, b_job_response.json()["_id"]) # redefinition of families - if "render" in instance.data["family"]: - instance.data['family'] = 'write' + if "render" in instance.data["productType"]: + instance.data["family"] = "write" + instance.data["productType"] = "write" families.insert(0, "render2d") - elif "prerender" in instance.data["family"]: - instance.data['family'] = 'write' + elif "prerender" in instance.data["productType"]: + instance.data["family"] = "write" + instance.data["productType"] = "write" families.insert(0, "prerender") instance.data["families"] = families @@ -196,7 +198,7 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin, AbstractSubmitDeadline""" for instance in context: if ( - instance.data["family"] != "workfile" + instance.data["productType"] != "workfile" # Disabled instances won't be integrated or instance.data("publish") is False ): diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py index 0b0e293943..3e95049e56 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py @@ -18,7 +18,7 @@ from ayon_core.pipeline.version_start import get_versioning_start from ayon_core.pipeline.farm.pyblish_functions import ( create_skeleton_instance_cache, create_instances_for_cache, - attach_instances_to_subset, + attach_instances_to_product, prepare_cache_representations, create_metadata_path ) @@ -97,12 +97,12 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin, (str): deadline_publish_job_id """ data = instance.data.copy() - subset = data["subset"] - job_name = "Publish - {subset}".format(subset=subset) + product_name = data["productName"] + job_name = "Publish - {}".format(product_name) anatomy = instance.context.data['anatomy'] - # instance.data.get("subset") != instances[0]["subset"] + # instance.data.get("productName") != instances[0]["productName"] # 'Main' vs 'renderMain' override_version = None instance_version = instance.data.get("version") # take this if exists @@ -113,9 +113,9 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin, anatomy, deepcopy(instance.data["anatomyData"]), instance.data.get("folderPath"), - instance.data["subset"], + instance.data["productName"], instance.context, - instance.data["family"], + instance.data["productType"], override_version ) @@ -259,7 +259,7 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin, } ] - This will create instances for `beauty` and `Z` subset + This will create instances for `beauty` and `Z` product adding those files to their respective representations. If we have only list of files, we collect all file sequences. @@ -297,9 +297,9 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin, instance_skeleton_data["representations"] += representations instances = [instance_skeleton_data] - # attach instances to subset + # attach instances to product if instance.data.get("attachTo"): - instances = attach_instances_to_subset( + instances = attach_instances_to_product( instance.data.get("attachTo"), instances ) @@ -382,23 +382,24 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin, json.dump(publish_job, f, indent=4, sort_keys=True) def _get_publish_folder(self, anatomy, template_data, - asset, subset, context, - family, version=None): + asset, product_name, context, + product_type, version=None): """ Extracted logic to pre-calculate real publish folder, which is calculated in IntegrateNew inside of Deadline process. This should match logic in: 'collect_anatomy_instance_data' - to - get correct anatomy, family, version for subset and + get correct anatomy, family, version for product and 'collect_resources_path' get publish_path Args: anatomy (ayon_core.pipeline.anatomy.Anatomy): template_data (dict): pre-calculated collected data for process - asset (string): asset name - subset (string): subset name (actually group name of subset) - family (string): for current deadline process it's always 'render' + asset (str): asset name + product_name (str): Product name (actually group name of product). + product_type (str): for current deadline process it's always + 'render' TODO - for generic use family needs to be dynamically calculated like IntegrateNew does version (int): override version from instance if exists @@ -413,7 +414,7 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin, if not version: version = get_last_version_by_subset_name( project_name, - subset, + product_name, asset_name=asset ) if version: @@ -424,8 +425,8 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin, template_data["app"], task_name=template_data["task"]["name"], task_type=template_data["task"]["type"], - family="render", - subset=subset, + product_type="render", + product_name=product_name, project_settings=context.data["project_settings"] ) @@ -435,14 +436,18 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin, template_name = publish.get_publish_template_name( project_name, host_name, - family, + product_type, task_info.get("name"), task_info.get("type"), ) - template_data["subset"] = subset - template_data["family"] = family + template_data["subset"] = product_name + template_data["family"] = product_type template_data["version"] = version + template_data["product"] = { + "name": product_name, + "type": product_type, + } render_templates = anatomy.templates_obj[template_name] if "folder" in render_templates: diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py index 77094fd8bd..7bc13ae4b6 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py @@ -19,7 +19,7 @@ from ayon_core.pipeline.version_start import get_versioning_start from ayon_core.pipeline.farm.pyblish_functions import ( create_skeleton_instance, create_instances_for_aov, - attach_instances_to_subset, + attach_instances_to_product, prepare_representations, create_metadata_path ) @@ -174,12 +174,12 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, (str): deadline_publish_job_id """ data = instance.data.copy() - subset = data["subset"] - job_name = "Publish - {subset}".format(subset=subset) + product_name = data["productName"] + job_name = "Publish - {}".format(product_name) anatomy = instance.context.data['anatomy'] - # instance.data.get("subset") != instances[0]["subset"] + # instance.data.get("productName") != instances[0]["productName"] # 'Main' vs 'renderMain' override_version = None instance_version = instance.data.get("version") # take this if exists @@ -190,9 +190,9 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, anatomy, deepcopy(instance.data["anatomyData"]), instance.data.get("folderPath"), - instances[0]["subset"], + instances[0]["productName"], instance.context, - instances[0]["family"], + instances[0]["productType"], override_version ) @@ -356,7 +356,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, } ] - This will create instances for `beauty` and `Z` subset + This will create instances for `beauty` and `Z` product adding those files to their respective representations. If we have only list of files, we collect all file sequences. @@ -411,9 +411,9 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, instance_skeleton_data["representations"] += representations instances = [instance_skeleton_data] - # attach instances to subset + # attach instances to product if instance.data.get("attachTo"): - instances = attach_instances_to_subset( + instances = attach_instances_to_product( instance.data.get("attachTo"), instances ) @@ -503,14 +503,14 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, json.dump(publish_job, f, indent=4, sort_keys=True) def _get_publish_folder(self, anatomy, template_data, - asset, subset, context, - family, version=None): + asset, product_name, context, + product_type, version=None): """ Extracted logic to pre-calculate real publish folder, which is calculated in IntegrateNew inside of Deadline process. This should match logic in: 'collect_anatomy_instance_data' - to - get correct anatomy, family, version for subset and + get correct anatomy, family, version for product name and 'collect_resources_path' get publish_path @@ -518,8 +518,10 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, anatomy (ayon_core.pipeline.anatomy.Anatomy): template_data (dict): pre-calculated collected data for process asset (string): asset name - subset (string): subset name (actually group name of subset) - family (string): for current deadline process it's always 'render' + product_name (string): Product name (actually group name + of product) + product_type (string): for current deadline process it's always + 'render' TODO - for generic use family needs to be dynamically calculated like IntegrateNew does version (int): override version from instance if exists @@ -535,7 +537,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, if not version: version = get_last_version_by_subset_name( project_name, - subset, + product_name, asset_name=asset ) if version: @@ -546,8 +548,8 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, host_name, task_name=template_data["task"]["name"], task_type=template_data["task"]["type"], - family="render", - subset=subset, + product_type="render", + product_name=product_name, project_settings=context.data["project_settings"] ) @@ -557,14 +559,18 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, template_name = publish.get_publish_template_name( project_name, host_name, - family, + product_type, task_info.get("name"), task_info.get("type"), ) - template_data["subset"] = subset - template_data["family"] = family template_data["version"] = version + template_data["subset"] = product_name + template_data["family"] = product_type + template_data["product"] = { + "name": product_name, + "type": product_type, + } render_templates = anatomy.templates_obj[template_name] if "folder" in render_templates: From bd50593b3489ec73e667e5fe20a6f34cc10596a6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 15:26:34 +0100 Subject: [PATCH 276/573] royalrender using product name and type --- .../plugins/publish/collect_sequences_from_job.py | 14 ++++++++------ .../plugins/publish/create_nuke_royalrender_job.py | 6 ++++-- .../publish/create_publish_royalrender_job.py | 10 +++++----- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/client/ayon_core/modules/royalrender/plugins/publish/collect_sequences_from_job.py b/client/ayon_core/modules/royalrender/plugins/publish/collect_sequences_from_job.py index 4460e11004..fe966cf292 100644 --- a/client/ayon_core/modules/royalrender/plugins/publish/collect_sequences_from_job.py +++ b/client/ayon_core/modules/royalrender/plugins/publish/collect_sequences_from_job.py @@ -150,8 +150,8 @@ class CollectSequencesFromJob(pyblish.api.ContextPlugin): self.log.info("Found collections: {}".format(collections)) - if data.get("subset") and len(collections) > 1: - self.log.error("Forced subset can only work with a single " + if data.get("productName") and len(collections) > 1: + self.log.error("Forced produce can only work with a single " "found sequence") raise RuntimeError("Invalid sequence") @@ -174,8 +174,10 @@ class CollectSequencesFromJob(pyblish.api.ContextPlugin): # Ensure each instance gets a unique reference to the data data = copy.deepcopy(data) - # If no subset provided, get it from collection's head - subset = data.get("subset", collection.head.rstrip("_. ")) + # If no product provided, get it from collection's head + product_name = ( + data.get("productName", collection.head.rstrip("_. ")) + ) # If no start or end frame provided, get it from collection indices = list(collection.indexes) @@ -186,9 +188,9 @@ class CollectSequencesFromJob(pyblish.api.ContextPlugin): instance.data.update({ "name": str(collection), - "family": families[0], # backwards compatibility / pyblish + "productType": families[0], # backwards compatibility / pyblish "families": list(families), - "subset": subset, + "productName": product_name, "folderPath": data.get( "folderPath", context.data["folderPath"] ), diff --git a/client/ayon_core/modules/royalrender/plugins/publish/create_nuke_royalrender_job.py b/client/ayon_core/modules/royalrender/plugins/publish/create_nuke_royalrender_job.py index 4234535b23..9a3bf3624b 100644 --- a/client/ayon_core/modules/royalrender/plugins/publish/create_nuke_royalrender_job.py +++ b/client/ayon_core/modules/royalrender/plugins/publish/create_nuke_royalrender_job.py @@ -15,10 +15,12 @@ class CreateNukeRoyalRenderJob(lib.BaseCreateRoyalRenderJob): super(CreateNukeRoyalRenderJob, self).process(instance) # redefinition of families - if "render" in instance.data["family"]: + if "render" in instance.data["productType"]: + instance.data["productType"] = "write" instance.data["family"] = "write" instance.data["families"].insert(0, "render2d") - elif "prerender" in instance.data["family"]: + elif "prerender" in instance.data["productType"]: + instance.data["productType"] = "write" instance.data["family"] = "write" instance.data["families"].insert(0, "prerender") diff --git a/client/ayon_core/modules/royalrender/plugins/publish/create_publish_royalrender_job.py b/client/ayon_core/modules/royalrender/plugins/publish/create_publish_royalrender_job.py index 256c8b7a83..5d177fec07 100644 --- a/client/ayon_core/modules/royalrender/plugins/publish/create_publish_royalrender_job.py +++ b/client/ayon_core/modules/royalrender/plugins/publish/create_publish_royalrender_job.py @@ -16,7 +16,7 @@ from ayon_core.pipeline.publish import KnownPublishError from ayon_core.pipeline.farm.pyblish_functions import ( create_skeleton_instance, create_instances_for_aov, - attach_instances_to_subset, + attach_instances_to_product, prepare_representations, create_metadata_path ) @@ -113,9 +113,9 @@ class CreatePublishRoyalRenderJob(pyblish.api.InstancePlugin, instance_skeleton_data["representations"] += representations instances = [instance_skeleton_data] - # attach instances to subset + # attach instances to product if instance.data.get("attachTo"): - instances = attach_instances_to_subset( + instances = attach_instances_to_product( instance.data.get("attachTo"), instances ) @@ -168,8 +168,8 @@ class CreatePublishRoyalRenderJob(pyblish.api.InstancePlugin, """ data = instance.data.copy() - subset = data["subset"] - jobname = "Publish - {subset}".format(subset=subset) + product_name = data["productName"] + jobname = "Publish - {}".format(product_name) # Transfer the environment from the original job to this dependent # job, so they use the same environment From d3275099c55f5e86bf349c38d14b4b779edf95cb Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 15:26:50 +0100 Subject: [PATCH 277/573] fix kwarg name --- .../ayon_core/plugins/publish/collect_anatomy_instance_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py index 6e1108e733..b62935dd6a 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py @@ -160,7 +160,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): subset_docs = [] if names_by_folder_ids: subset_docs = list(get_subsets( - project_name, names_by_folder_ids=names_by_folder_ids + project_name, names_by_asset_ids=names_by_folder_ids )) product_ids = { From 4a7bc9a571e5aafa54368038edd0bbbff732af16 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 15:27:03 +0100 Subject: [PATCH 278/573] 'prepare_template_data' supports subdict values --- client/ayon_core/lib/plugin_tools.py | 149 ++++++++++++++++++++------- 1 file changed, 113 insertions(+), 36 deletions(-) diff --git a/client/ayon_core/lib/plugin_tools.py b/client/ayon_core/lib/plugin_tools.py index d204fc2c8f..5ad4da88b9 100644 --- a/client/ayon_core/lib/plugin_tools.py +++ b/client/ayon_core/lib/plugin_tools.py @@ -1,54 +1,131 @@ # -*- coding: utf-8 -*- -"""Avalon/Pyblish plugin tools.""" +"""AYON plugin tools.""" import os import logging import re +import collections log = logging.getLogger(__name__) +CAPITALIZE_REGEX = re.compile(r"[a-zA-Z0-9]") + + +def _capitalize_value(value): + """Capitalize first char of value. + + Function finds first available character or number in passed string + and uppers the character. + + Example: + >>> _capitalize_value("host") + 'Host' + >>> _capitalize_value("01_shot") + '01_shot' + >>> _capitalize_value("_shot") + '_Shot' + + Args: + value (str): Value where to capitalize first character. + """ + + # - conditions are because of possible index errors + # - regex is to skip symbols that are not chars or numbers + # - e.g. "{key}" which starts with curly bracket + capitalized = "" + for idx in range(len(value or "")): + char = value[idx] + if not CAPITALIZE_REGEX.match(char): + capitalized += char + else: + capitalized += char.upper() + capitalized += value[idx + 1:] + break + return capitalized + + +def _separate_keys_and_value(data): + valid_items = [] + hierachy_queue = collections.deque() + hierachy_queue.append((data, [])) + while hierachy_queue: + item = hierachy_queue.popleft() + src_data, keys = item + if src_data is None: + continue + + if isinstance(src_data, (list, tuple, set)): + for idx, item in enumerate(src_data): + hierachy_queue.append((item, keys + [idx])) + continue + + if isinstance(src_data, dict): + for key, value in src_data.items(): + hierachy_queue.append((value, keys + [key])) + continue + + if keys: + valid_items.append((keys, src_data)) + return valid_items + def prepare_template_data(fill_pairs): + """Prepares formatted data for filling template. + + It produces multiple variants of keys (key, Key, KEY) to control + format of filled template. + + Example: + >>> src_data = { + ... "host": "maya", + ... } + >>> output = prepare_template_data(src_data) + >>> sorted(list(output.items())) # sort & list conversion for tests + [('HOST', 'MAYA'), ('Host', 'Maya'), ('host', 'maya')] + + Args: + fill_pairs (Union[dict[str, Any], Iterable[Tuple[str, Any]]]): The + value that are prepared for template. + + Returns: + dict[str, str]: Prepared values for template. """ - Prepares formatted data for filling template. - It produces multiple variants of keys (key, Key, KEY) to control - format of filled template. + valid_items = _separate_keys_and_value(fill_pairs) + output = {} + for item in valid_items: + keys, value = item + upper_value = value.upper() + capitalized_value = _capitalize_value(value) - Args: - fill_pairs (iterable) of tuples (key, value) - Returns: - (dict) - ('host', 'maya') > {'host':'maya', 'Host': 'Maya', 'HOST': 'MAYA'} - - """ - fill_data = {} - regex = re.compile(r"[a-zA-Z0-9]") - for key, value in dict(fill_pairs).items(): - # Handle cases when value is `None` (standalone publisher) - if value is None: + first_key = keys.pop(0) + if not keys: + output[first_key] = value + output[first_key.upper()] = upper_value + output[first_key.capitalize()] = capitalized_value continue - # Keep value as it is - fill_data[key] = value - # Both key and value are with upper case - fill_data[key.upper()] = value.upper() - # Capitalize only first char of value - # - conditions are because of possible index errors - # - regex is to skip symbols that are not chars or numbers - # - e.g. "{key}" which starts with curly bracket - capitalized = "" - for idx in range(len(value or "")): - char = value[idx] - if not regex.match(char): - capitalized += char + # Prepare 'normal', 'upper' and 'capitalized' variables + normal = output.setdefault(first_key, {}) + capitalized = output.setdefault(first_key.capitalize(), {}) + upper = output.setdefault(first_key.upper(), {}) + + keys_deque = collections.deque(keys) + while keys_deque: + key = keys_deque.popleft() + upper_key = key + if isinstance(key, str): + upper_key = key.upper() + + if not keys_deque: + # Fill value on last key + upper[upper_key] = upper_value + capitalized[key] = capitalized_value + normal[key] = value else: - capitalized += char.upper() - capitalized += value[idx + 1:] - break - - fill_data[key.capitalize()] = capitalized - - return fill_data + normal = normal.setdefault(key, {}) + capitalized = capitalized.setdefault(key, {}) + upper = upper.setdefault(upper_key, {}) + return output def source_hash(filepath, *args): From 3afa0e3bca7a67da8b1be375ed3b9ef250fbcfc4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 15:53:00 +0100 Subject: [PATCH 279/573] fill both 'family' and 'productType' --- .../aftereffects/plugins/publish/collect_render.py | 6 ++++-- .../plugins/publish/collect_celaction_instances.py | 2 ++ .../plugins/publish/collect_timeline_instances.py | 1 + .../flame/plugins/publish/collect_timeline_otio.py | 1 + .../hosts/fusion/plugins/publish/collect_render.py | 3 ++- .../harmony/plugins/publish/collect_farm_render.py | 1 + .../harmony/plugins/publish/collect_instances.py | 1 + .../harmony/plugins/publish/collect_palettes.py | 1 + .../harmony/plugins/publish/collect_workfile.py | 1 + .../plugins/publish/collect_frame_tag_instances.py | 1 + .../hiero/plugins/publish/precollect_instances.py | 2 ++ .../hiero/plugins/publish/precollect_workfile.py | 1 + .../houdini/plugins/publish/collect_usd_bootstrap.py | 4 +++- .../houdini/plugins/publish/collect_usd_layers.py | 1 + .../hosts/max/plugins/publish/collect_render.py | 6 ++++-- .../hosts/maya/plugins/publish/collect_vrayscene.py | 6 ++++-- .../nuke/plugins/publish/extract_render_local.py | 3 +++ .../plugins/publish/extract_review_intermediates.py | 2 +- .../photoshop/plugins/publish/collect_auto_image.py | 1 + .../photoshop/plugins/publish/collect_auto_review.py | 1 + .../plugins/publish/collect_auto_workfile.py | 1 + .../plugins/publish/collect_color_coded_instances.py | 1 + .../resolve/plugins/publish/precollect_instances.py | 1 + .../resolve/plugins/publish/precollect_workfile.py | 3 ++- .../plugins/publish/collect_textureset_images.py | 6 ++++-- .../plugins/publish/collect_render_instances.py | 6 ++++-- .../plugins/publish/collect_sequences_from_job.py | 3 ++- client/ayon_core/pipeline/create/context.py | 12 ++++++------ client/ayon_core/pipeline/farm/pyblish_functions.py | 4 +++- .../pipeline/publish/abstract_collect_render.py | 1 + client/ayon_core/pipeline/publish/lib.py | 8 ++++---- .../plugins/publish/collect_from_create_context.py | 1 + .../publisher/publish_report_viewer/report_items.py | 6 +----- 33 files changed, 67 insertions(+), 31 deletions(-) diff --git a/client/ayon_core/hosts/aftereffects/plugins/publish/collect_render.py b/client/ayon_core/hosts/aftereffects/plugins/publish/collect_render.py index 8a03717668..afd58ca758 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/publish/collect_render.py +++ b/client/ayon_core/hosts/aftereffects/plugins/publish/collect_render.py @@ -88,11 +88,13 @@ class CollectAERender(publish.AbstractCollectRender): raise ValueError("No file extension set in Render Queue") render_item = render_q[0] + product_type = "render" instance_families = inst.data.get("families", []) - instance_families.append("render") + instance_families.append(product_type) product_name = inst.data["productName"] instance = AERenderInstance( - productType="render", + productType=product_type, + family=product_type, families=instance_families, version=version, time="", diff --git a/client/ayon_core/hosts/celaction/plugins/publish/collect_celaction_instances.py b/client/ayon_core/hosts/celaction/plugins/publish/collect_celaction_instances.py index 538f1c28c4..4306a53bfe 100644 --- a/client/ayon_core/hosts/celaction/plugins/publish/collect_celaction_instances.py +++ b/client/ayon_core/hosts/celaction/plugins/publish/collect_celaction_instances.py @@ -56,6 +56,7 @@ class CollectCelactionInstances(pyblish.api.ContextPlugin): "label": scene_file, "productName": product_name, "productType": product_type, + "family": product_type, "families": [product_type], "representations": [] }) @@ -86,6 +87,7 @@ class CollectCelactionInstances(pyblish.api.ContextPlugin): instance.data.update({ "label": "{} - farm".format(product_name), "productType": product_type, + "family": product_type, "families": [product_type], "productName": product_name }) diff --git a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_instances.py b/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_instances.py index 60fe221441..9d6560023c 100644 --- a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_instances.py +++ b/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_instances.py @@ -356,6 +356,7 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): "productName": product_name, "folderPath": folder_path, "productType": product_type, + "family": product_type, "families": [product_type] }) diff --git a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_otio.py b/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_otio.py index c095c1d333..6d04d53cea 100644 --- a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_otio.py +++ b/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_otio.py @@ -45,6 +45,7 @@ class CollecTimelineOTIO(pyblish.api.ContextPlugin): "folderPath": folder_path, "productName": product_name, "productType": product_type, + "family": product_type, "families": [product_type] } diff --git a/client/ayon_core/hosts/fusion/plugins/publish/collect_render.py b/client/ayon_core/hosts/fusion/plugins/publish/collect_render.py index 0eea061827..36102d02cb 100644 --- a/client/ayon_core/hosts/fusion/plugins/publish/collect_render.py +++ b/client/ayon_core/hosts/fusion/plugins/publish/collect_render.py @@ -59,9 +59,10 @@ class CollectFusionRender( instance_families = inst.data.get("families", []) product_name = inst.data["productName"] instance = FusionRenderInstance( - productType=product_type, tool=tool, workfileComp=comp, + productType=product_type, + family=product_type, families=instance_families, version=version, time="", diff --git a/client/ayon_core/hosts/harmony/plugins/publish/collect_farm_render.py b/client/ayon_core/hosts/harmony/plugins/publish/collect_farm_render.py index dea13ffa27..156e2ac6ba 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/collect_farm_render.py +++ b/client/ayon_core/hosts/harmony/plugins/publish/collect_farm_render.py @@ -154,6 +154,7 @@ class CollectFarmRender(publish.AbstractCollectRender): name=node.split("/")[1], productType="render.farm", + family="render.farm", families=["render.farm"], farm=True, diff --git a/client/ayon_core/hosts/harmony/plugins/publish/collect_instances.py b/client/ayon_core/hosts/harmony/plugins/publish/collect_instances.py index fd394c0b43..5aad7d4751 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/collect_instances.py +++ b/client/ayon_core/hosts/harmony/plugins/publish/collect_instances.py @@ -53,6 +53,7 @@ class CollectInstances(pyblish.api.ContextPlugin): if product_type is None: product_type = data["family"] data["productType"] = product_type + data["family"] = product_type # skip render farm product type as it is collected separately if product_type == "renderFarm": diff --git a/client/ayon_core/hosts/harmony/plugins/publish/collect_palettes.py b/client/ayon_core/hosts/harmony/plugins/publish/collect_palettes.py index 6e8583c340..9e0b500663 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/collect_palettes.py +++ b/client/ayon_core/hosts/harmony/plugins/publish/collect_palettes.py @@ -39,6 +39,7 @@ class CollectPalettes(pyblish.api.ContextPlugin): instance.data.update({ "id": id, "productType": product_type, + "family": product_type, "families": [product_type], "folderPath": folder_path, "productName": "{}{}".format("palette", name) diff --git a/client/ayon_core/hosts/harmony/plugins/publish/collect_workfile.py b/client/ayon_core/hosts/harmony/plugins/publish/collect_workfile.py index 54a2fd3b48..b1010cfb57 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/collect_workfile.py +++ b/client/ayon_core/hosts/harmony/plugins/publish/collect_workfile.py @@ -34,6 +34,7 @@ class CollectWorkfile(pyblish.api.ContextPlugin): "label": basename, "name": basename, "productType": product_type, + "family": product_type, "families": [product_type], "representations": [], "folderPath": context.data["folderPath"] diff --git a/client/ayon_core/hosts/hiero/plugins/publish/collect_frame_tag_instances.py b/client/ayon_core/hosts/hiero/plugins/publish/collect_frame_tag_instances.py index 0cdb3a5478..d73b5d4667 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/collect_frame_tag_instances.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/collect_frame_tag_instances.py @@ -138,6 +138,7 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin): "name": name, "label": "{} {}".format(name, product_data["frames"]), "productType": product_type, + "family": product_type, "families": [product_type, "frame"], "folderPath": product_data["folderPath"], "productName": name, diff --git a/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py b/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py index 0b905064b3..f75f2a800e 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py @@ -209,6 +209,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin): "label": label, "productName": product_name, "productType": product_type, + "family": product_type, "families": [product_type] }) @@ -267,6 +268,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin): "label": label, "productName": product_name, "productType": product_type, + "family": product_type, "families": [product_type, "clip"] }) # remove review track attr if any diff --git a/client/ayon_core/hosts/hiero/plugins/publish/precollect_workfile.py b/client/ayon_core/hosts/hiero/plugins/publish/precollect_workfile.py index e4cde297de..8df6cd4261 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/precollect_workfile.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/precollect_workfile.py @@ -71,6 +71,7 @@ class PrecollectWorkfile(pyblish.api.ContextPlugin): ), "item": project, "productType": product_type, + "family": product_type, "families": [product_type], "representations": [workfile_representation, thumb_representation] } diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py index 380d476e0f..24ac9f22c3 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py @@ -83,10 +83,12 @@ class CollectUsdBootstrap(pyblish.api.InstancePlugin): ) ) + product_type = "usd.bootstrap" new = instance.context.create_instance(product_name) new.data["productName"] = product_name new.data["label"] = "{0} ({1})".format(product_name, asset_name) - new.data["productType"] = "usd.bootstrap" + new.data["productType"] = product_type + new.data["family"] = product_type new.data["comment"] = "Automated bootstrap USD file." new.data["publishFamilies"] = ["usd"] diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_layers.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_layers.py index f4a96852ff..f085b6ca41 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_layers.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_layers.py @@ -52,6 +52,7 @@ class CollectUsdLayers(pyblish.api.InstancePlugin): layer_inst = context.create_instance(name) layer_inst.data["productType"] = product_type + layer_inst.data["family"] = product_type layer_inst.data["families"] = [product_type] layer_inst.data["productName"] = "__stub__" layer_inst.data["label"] = label diff --git a/client/ayon_core/hosts/max/plugins/publish/collect_render.py b/client/ayon_core/hosts/max/plugins/publish/collect_render.py index 4515fe1a0c..4ad9dfb3a3 100644 --- a/client/ayon_core/hosts/max/plugins/publish/collect_render.py +++ b/client/ayon_core/hosts/max/plugins/publish/collect_render.py @@ -92,6 +92,7 @@ class CollectRender(pyblish.api.InstancePlugin): instance.data["attachTo"] = [] renderer_class = get_current_renderer() renderer = str(renderer_class).split(":")[0] + product_type = "maxrender" # also need to get the render dir for conversion data = { "folderPath": instance.data["folderPath"], @@ -99,8 +100,9 @@ class CollectRender(pyblish.api.InstancePlugin): "publish": True, "maxversion": str(get_max_version()), "imageFormat": img_format, - "productType": 'maxrender', - "families": ['maxrender'], + "productType": product_type, + "family": product_type, + "families": [product_type], "renderer": renderer, "source": filepath, "plugin": "3dsmax", diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_vrayscene.py b/client/ayon_core/hosts/maya/plugins/publish/collect_vrayscene.py index 57dd49e2d9..9548cd9387 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/collect_vrayscene.py +++ b/client/ayon_core/hosts/maya/plugins/publish/collect_vrayscene.py @@ -56,6 +56,7 @@ class CollectVrayScene(pyblish.api.InstancePlugin): frame_end_handle = frame_end_render # Get layer specific settings, might be overrides + product_type = "vrayscene_layer" data = { "productName": layer_name, "layer": layer_name, @@ -75,8 +76,9 @@ class CollectVrayScene(pyblish.api.InstancePlugin): layer=layer_name)), "renderer": renderer, # instance product type - "productType": "vrayscene_layer", - "families": ["vrayscene_layer"], + "productType": product_type, + "family": product_type, + "families": [product_type], "time": get_formatted_current_time(), "author": context.data["user"], # Add source to allow tracing back to the scene from diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_render_local.py b/client/ayon_core/hosts/nuke/plugins/publish/extract_render_local.py index 25b9f67fba..c8be2a5564 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/extract_render_local.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/extract_render_local.py @@ -128,18 +128,21 @@ class NukeRenderLocal(publish.Extractor, anatomy_data = instance.data["anatomyData"] # redefinition of families if "render.local" in families: + instance.data["family"] = "render" instance.data["productType"] = "render" families.remove("render.local") families.insert(0, "render2d") anatomy_data["family"] = "render" anatomy_data["product"]["type"] = "render" elif "prerender.local" in families: + instance.data["family"] = "prerender" instance.data["productType"] = "prerender" families.remove("prerender.local") families.insert(0, "prerender") anatomy_data["family"] = "prerender" anatomy_data["product"]["type"] = "prerender" elif "image.local" in families: + instance.data["family"] = "image" instance.data["productType"] = "image" families.remove("image.local") anatomy_data["family"] = "image" diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py b/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py index d40513b7c8..8ac07c641c 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py @@ -53,7 +53,7 @@ class ExtractReviewIntermediates(publish.Extractor): # TODO 'families' should not be included for filtering of outputs families = set(instance.data["families"]) - # add main family to make sure all families are compared + # Add product type to families families.add(instance.data["productType"]) task_type = instance.context.data["taskType"] diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image.py index 8e27539425..7773b444d2 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image.py @@ -95,6 +95,7 @@ class CollectAutoImage(pyblish.api.ContextPlugin): instance.data["ids"] = publishable_ids instance.data["publish"] = True instance.data["creator_identifier"] = "auto_image" + instance.data["family"] = product_type instance.data["families"] = [product_type] if auto_creator["mark_for_review"]: diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_review.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_review.py index fc8246c269..14f2f23985 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_review.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_review.py @@ -85,6 +85,7 @@ class CollectAutoReview(pyblish.api.ContextPlugin): "name": product_name, "productName": product_name, "productType": product_type, + "family": product_type, "families": [product_type], "representations": [], "folderPath": folder_path, diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_workfile.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_workfile.py index af9d6cdf75..0b12195603 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_workfile.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_workfile.py @@ -89,6 +89,7 @@ class CollectAutoWorkfile(pyblish.api.ContextPlugin): "name": base_name, "productName": product_name, "productType": product_type, + "family": product_type, "families": [product_type], "representations": [], "folderPath": folder_path diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_color_coded_instances.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_color_coded_instances.py index ab38398b30..f11ba4383a 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_color_coded_instances.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_color_coded_instances.py @@ -188,6 +188,7 @@ class CollectColorCodedInstances(pyblish.api.ContextPlugin): instance.data["folderPath"] = folder_path instance.data["task"] = task_name instance.data["layer"] = layer + instance.data["family"] = product_type instance.data["families"] = [product_type] return instance diff --git a/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py b/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py index 025b64878e..3154e0c3e5 100644 --- a/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py +++ b/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py @@ -139,6 +139,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin): "folderPath": asset, "productName": product_name, "productType": product_type, + "family": product_type, "families": [product_type], "publish": get_publish_attribute(timeline_item) }) diff --git a/client/ayon_core/hosts/resolve/plugins/publish/precollect_workfile.py b/client/ayon_core/hosts/resolve/plugins/publish/precollect_workfile.py index 4526b812a0..a147c9a905 100644 --- a/client/ayon_core/hosts/resolve/plugins/publish/precollect_workfile.py +++ b/client/ayon_core/hosts/resolve/plugins/publish/precollect_workfile.py @@ -28,10 +28,11 @@ class PrecollectWorkfile(pyblish.api.ContextPlugin): instance_data = { "name": "{}_{}".format(asset_name, product_name), "label": "{} {}".format(current_asset_name, product_name), + "item": project, "folderPath": current_asset_name, "productName": product_name, - "item": project, "productType": "workfile", + "family": "workfile", "families": [] } diff --git a/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py b/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py index d977ccff68..9cd77e8f90 100644 --- a/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py +++ b/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py @@ -113,14 +113,16 @@ class CollectTextureSet(pyblish.api.InstancePlugin): representation["stagingDir"] = staging_dir # Clone the instance + product_type = "image" image_instance = context.create_instance(image_product_name) image_instance[:] = instance[:] image_instance.data.update(copy.deepcopy(dict(instance.data))) image_instance.data["name"] = image_product_name image_instance.data["label"] = image_product_name image_instance.data["productName"] = image_product_name - image_instance.data["productType"] = "image" - image_instance.data["families"] = ["image", "textures"] + image_instance.data["productType"] = product_type + image_instance.data["family"] = product_type + image_instance.data["families"] = [product_type, "textures"] image_instance.data["representations"] = [representation] # Group the textures together in the loader diff --git a/client/ayon_core/hosts/unreal/plugins/publish/collect_render_instances.py b/client/ayon_core/hosts/unreal/plugins/publish/collect_render_instances.py index 0f22ea8085..4d5ace56ac 100644 --- a/client/ayon_core/hosts/unreal/plugins/publish/collect_render_instances.py +++ b/client/ayon_core/hosts/unreal/plugins/publish/collect_render_instances.py @@ -57,6 +57,7 @@ class CollectRenderInstances(pyblish.api.InstancePlugin): seq = s.get('sequence') seq_name = seq.get_name() + product_type = "render" new_product_name = f"{data.get('productName')}_{seq_name}" new_instance = context.create_instance( new_product_name @@ -67,9 +68,10 @@ class CollectRenderInstances(pyblish.api.InstancePlugin): new_data["folderPath"] = seq_name new_data["setMembers"] = seq_name - new_data["productType"] = "render" new_data["productName"] = new_product_name - new_data["families"] = ["render", "review"] + new_data["productType"] = product_type + new_data["family"] = product_type + new_data["families"] = [product_type, "review"] new_data["parent"] = data.get("parent") new_data["level"] = data.get("level") new_data["output"] = s.get('output') diff --git a/client/ayon_core/modules/royalrender/plugins/publish/collect_sequences_from_job.py b/client/ayon_core/modules/royalrender/plugins/publish/collect_sequences_from_job.py index fe966cf292..7f7b89590c 100644 --- a/client/ayon_core/modules/royalrender/plugins/publish/collect_sequences_from_job.py +++ b/client/ayon_core/modules/royalrender/plugins/publish/collect_sequences_from_job.py @@ -188,7 +188,8 @@ class CollectSequencesFromJob(pyblish.api.ContextPlugin): instance.data.update({ "name": str(collection), - "productType": families[0], # backwards compatibility / pyblish + "productType": families[0], + "family": families[0], "families": list(families), "productName": product_name, "folderPath": data.get( diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index 1a551f04c3..a74bb8a690 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -1193,14 +1193,14 @@ class CreatedInstance: instance_data = copy.deepcopy(instance_data) - product_type = instance_data.get("productType", None) - if not product_type: - product_type = instance_data.get("family", None) + product_type = instance_data.get("productType") + if product_type is None: + product_type = instance_data.get("family") if product_type is None: product_type = creator.product_type - product_name = instance_data.get("productName", None) - if not product_name: - product_name = instance_data.get("subset", None) + product_name = instance_data.get("productName") + if product_name is None: + product_name = instance_data.get("subset") return cls( product_type, product_name, instance_data, creator diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 8d875ca56e..c669d95c1e 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -801,8 +801,9 @@ def create_skeleton_instance_cache(instance): families = ["render", product_type] instance_skeleton_data = { - "productType": product_type, "productName": data["productName"], + "productType": product_type, + "family": product_type, "families": families, "folderPath": data["folderPath"], "frameStart": time_data.start, @@ -1085,6 +1086,7 @@ def attach_instances_to_product(attach_to, instances): new_inst["version"] = attach_instance.get("version") new_inst["productName"] = attach_instance.get("productName") new_inst["productType"] = attach_instance.get("productType") + new_inst["family"] = attach_instance.get("family") new_inst["append"] = True # don't set subsetGroup if we are attaching new_inst.pop("subsetGroup") diff --git a/client/ayon_core/pipeline/publish/abstract_collect_render.py b/client/ayon_core/pipeline/publish/abstract_collect_render.py index 4ec38bbdd5..745632ca0a 100644 --- a/client/ayon_core/pipeline/publish/abstract_collect_render.py +++ b/client/ayon_core/pipeline/publish/abstract_collect_render.py @@ -28,6 +28,7 @@ class RenderInstance(object): 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 + family = attr.ib() # product type for pyblish filtering productType = attr.ib() # product type productName = attr.ib() # product name folderPath = attr.ib() # folder path diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index c61bac7f9a..a098352898 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -954,11 +954,11 @@ def get_publish_instance_families(instance): list[str]: List of families. """ - product_type = instance.data.get("productType") + family = instance.data.get("family") families = set(instance.data.get("families") or []) output = [] - if product_type: - output.append(product_type) - families.discard(product_type) + if family: + output.append(family) + families.discard(family) output.extend(families) return output diff --git a/client/ayon_core/plugins/publish/collect_from_create_context.py b/client/ayon_core/plugins/publish/collect_from_create_context.py index 353f4f69cd..8218806c4c 100644 --- a/client/ayon_core/plugins/publish/collect_from_create_context.py +++ b/client/ayon_core/plugins/publish/collect_from_create_context.py @@ -87,6 +87,7 @@ class CollectFromCreateContext(pyblish.api.ContextPlugin): "task": in_data["task"], "productName": product_name, "productType": in_data["productType"], + "family": in_data["productType"], "families": instance_families, "representations": [], "thumbnailSource": thumbnail_path diff --git a/client/ayon_core/tools/publisher/publish_report_viewer/report_items.py b/client/ayon_core/tools/publisher/publish_report_viewer/report_items.py index a605b83d1b..206f999bac 100644 --- a/client/ayon_core/tools/publisher/publish_report_viewer/report_items.py +++ b/client/ayon_core/tools/publisher/publish_report_viewer/report_items.py @@ -33,11 +33,7 @@ class InstanceItem: def __init__(self, instance_id, instance_data, logs_by_instance_id): self._id = instance_id self.label = instance_data.get("label") or instance_data.get("name") - - family = instance_data.get("productType") - if not family: - family = instance_data.get("family") - self.family = family + self.family = instance_data.get("family") self.removed = not instance_data.get("exists", True) logs = logs_by_instance_id.get(instance_id) or [] From f5e5b7ad1b4c8fa9fa6c9ec1aa2057c5b7cece18 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 23 Feb 2024 17:01:42 +0100 Subject: [PATCH 280/573] Refactor get_write_node_template_attr function - Handle missing product name gracefully in return statement. --- client/ayon_core/hosts/nuke/api/lib.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index 426f93a187..61afaebb35 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -2153,10 +2153,13 @@ def get_write_node_template_attr(node): identifier = node_data["creator_identifier"] # return template data + product_name = node_data.get("productName") + if product_name is None: + product_name = node_data["subset"] return get_imageio_node_setting( node_class="Write", plugin_name=plugin_names_mapping[identifier], - product_name=node_data["productName"] + product_name=product_name ) From ab7857f3e9ffe2686848b90b9614cac7e5284043 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 17:32:59 +0100 Subject: [PATCH 281/573] use 'AYONProductName' to store 'productName' in houdini --- client/ayon_core/hosts/houdini/api/plugin.py | 4 ++++ .../ayon_core/hosts/houdini/plugins/create/create_review.py | 2 +- .../hosts/houdini/plugins/publish/validate_subset_name.py | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/houdini/api/plugin.py b/client/ayon_core/hosts/houdini/api/plugin.py index 82d36b3d07..918de43441 100644 --- a/client/ayon_core/hosts/houdini/api/plugin.py +++ b/client/ayon_core/hosts/houdini/api/plugin.py @@ -246,6 +246,8 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase): node_path = instance.path() node_data["instance_id"] = node_path node_data["instance_node"] = node_path + if "AYONProductName" in node_data: + node_data["productName"] = node_data.pop("AYONProductName") created_instance = CreatedInstance.from_existing( node_data, self @@ -269,6 +271,8 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase): def imprint(self, node, values, update=False): # Never store instance node and instance id since that data comes # from the node's path + if "AYONProductName" in values: + values["productName"] = values.pop("AYONProductName") values.pop("instance_node", None) values.pop("instance_id", None) imprint(node, values, update=update) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_review.py b/client/ayon_core/hosts/houdini/plugins/create/create_review.py index 7dc0171b1a..fe15a21c91 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_review.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_review.py @@ -34,7 +34,7 @@ class CreateReview(plugin.HoudiniCreator): filepath = "{root}/{product_name}/{product_name}.$F4.{ext}".format( root=hou.text.expandString("$HIP/pyblish"), # keep dynamic link to product name - product_name="`chs(\"productName\")`", + product_name="`chs(\"AYONProductName\")`", ext=pre_create_data.get("image_format") or "png" ) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py index c4a9951d92..afe4cf109b 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py @@ -91,7 +91,7 @@ class ValidateSubsetName(pyblish.api.InstancePlugin, ) instance.data["productName"] = product_name - rop_node.parm("productName").set(product_name) + rop_node.parm("AYONProductName").set(product_name) cls.log.debug( "Product name on rop node '%s' has been set to '%s'.", From ab052b4e572d84f8e347fbfb9f41fe3a88afcfe9 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Fri, 23 Feb 2024 17:43:02 +0000 Subject: [PATCH 282/573] Use folderpath when collecting the render instance --- .../hosts/unreal/plugins/publish/collect_render_instances.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/unreal/plugins/publish/collect_render_instances.py b/client/ayon_core/hosts/unreal/plugins/publish/collect_render_instances.py index 8bbf5a5c62..48c1fe6673 100644 --- a/client/ayon_core/hosts/unreal/plugins/publish/collect_render_instances.py +++ b/client/ayon_core/hosts/unreal/plugins/publish/collect_render_instances.py @@ -64,7 +64,7 @@ class CollectRenderInstances(pyblish.api.InstancePlugin): new_data = new_instance.data - new_data["folderPath"] = seq_name + new_data["folderPath"] = f"/{s.get('output')}" new_data["setMembers"] = seq_name new_data["family"] = "render" new_data["families"] = ["render", "review"] From 197279b91334c057e989771b534e6a54a9a28b5a Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 23 Feb 2024 22:38:44 +0200 Subject: [PATCH 283/573] fix a typo --- .../hosts/houdini/plugins/publish/validate_subset_name.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py index afe4cf109b..13522b7482 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py @@ -56,7 +56,7 @@ class ValidateSubsetName(pyblish.api.InstancePlugin, # Check product name asset_doc = instance.data["assetEntity"] product_name = get_product_name( - instance.context.data["projctName"], + instance.context.data["projectName"], asset_doc, instance.data["task"], instance.context.data["hostName"], From 22d336535dcafac3e23b738688fd93e0cbda021e Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 23 Feb 2024 22:40:00 +0200 Subject: [PATCH 284/573] fix bug with using new parameter AYONproductName --- client/ayon_core/hosts/houdini/api/plugin.py | 4 ++-- .../ayon_core/hosts/houdini/plugins/create/create_workfile.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/hosts/houdini/api/plugin.py b/client/ayon_core/hosts/houdini/api/plugin.py index 918de43441..2dd06d8d5e 100644 --- a/client/ayon_core/hosts/houdini/api/plugin.py +++ b/client/ayon_core/hosts/houdini/api/plugin.py @@ -271,8 +271,8 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase): def imprint(self, node, values, update=False): # Never store instance node and instance id since that data comes # from the node's path - if "AYONProductName" in values: - values["productName"] = values.pop("AYONProductName") + if "productName" in values: + values["AYONProductName"] = values.pop("productName") values.pop("instance_node", None) values.pop("instance_id", None) imprint(node, values, update=update) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py b/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py index b751bce78b..2bf196bcf6 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py @@ -67,7 +67,7 @@ class CreateWorkfile(plugin.HoudiniCreatorBase, AutoCreator): ) current_instance["folderPath"] = asset_name current_instance["task"] = task_name - current_instance["productName"] = product_name + current_instance["AYONProductName"] = product_name # write workfile information to context container. op_ctx = hou.node(CONTEXT_CONTAINER) From ea5acd07b21ebcf4dbb159abc358a0fc759ef042 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 24 Feb 2024 00:46:38 +0100 Subject: [PATCH 285/573] fix change of product name in houdini --- .../ayon_core/hosts/houdini/plugins/create/create_workfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py b/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py index 2bf196bcf6..b751bce78b 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py @@ -67,7 +67,7 @@ class CreateWorkfile(plugin.HoudiniCreatorBase, AutoCreator): ) current_instance["folderPath"] = asset_name current_instance["task"] = task_name - current_instance["AYONProductName"] = product_name + current_instance["productName"] = product_name # write workfile information to context container. op_ctx = hou.node(CONTEXT_CONTAINER) From 344e244a993a3082b8836afdb87f5883e63ff0e8 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 26 Feb 2024 18:04:40 +0800 Subject: [PATCH 286/573] validate no animation for model family in Max --- .../plugins/publish/validate_no_animation.py | 67 +++++++++++++++++++ .../max/server/settings/publishers.py | 9 +++ server_addon/max/server/version.py | 2 +- 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 client/ayon_core/hosts/max/plugins/publish/validate_no_animation.py diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_no_animation.py b/client/ayon_core/hosts/max/plugins/publish/validate_no_animation.py new file mode 100644 index 0000000000..9f859a1b28 --- /dev/null +++ b/client/ayon_core/hosts/max/plugins/publish/validate_no_animation.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +import pyblish.api +from pymxs import runtime as rt +from ayon_core.pipeline import ( + PublishValidationError, + OptionalPyblishPluginMixin +) +from ayon_core.hosts.max.api.action import SelectInvalidAction + + +class ValidateNoAnimation(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): + """Validates No Animation + + Ensure no keyframes on nodes in the Instance + """ + + order = pyblish.api.ValidatorOrder + families = ["model"] + hosts = ["max"] + optional = True + label = "Validate No Animation" + actions = [SelectInvalidAction] + + def process(self, instance): + if not self.is_active(instance.data): + return + invalid = self.get_invalid(instance) + if invalid: + bullet_point_invalid_statement = "\n".join( + "- {}: {}".format(obj, message) + for obj, message in invalid + ) + raise PublishValidationError( + "Keyframes found on:\n\n{0}".format( + bullet_point_invalid_statement) + , + title="Keyframes on model" + ) + + @staticmethod + def get_invalid(instance): + invalid = [] + selected_objects = instance.data["members"] + for sel in selected_objects: + sel_pos_ctl = rt.getPropertyController( + sel.controller, 'Position') + ctl_count = (sel_pos_ctl.keys).count + if len(ctl_count) > 0: + invalid.append( + (sel), f"Object Position(s) has {ctl_count} keyframe(s)") + sel_rot_ctl = rt.getPropertyController( + sel.controller, "Rotation" + ) + ctl_count = (sel_rot_ctl.keys).count + if len(ctl_count) > 0: + invalid.append( + (sel), f"Object Rotation(s) has {ctl_count} keyframe(s)") + sel_scale_ctl = rt.getPropertyController( + sel.controller, "Scale" + ) + ctl_count = (sel_scale_ctl.keys).count + if len(ctl_count) > 0: + invalid.append( + (sel), f"Object Rotation(s) has {ctl_count} keyframe(s)") + + return invalid diff --git a/server_addon/max/server/settings/publishers.py b/server_addon/max/server/settings/publishers.py index 5e28c1b467..8f9f1009f4 100644 --- a/server_addon/max/server/settings/publishers.py +++ b/server_addon/max/server/settings/publishers.py @@ -82,6 +82,10 @@ class PublishersModel(BaseSettingsModel): "the system automatically skips checking it" ) ) + ValidateNoAnimation: BasicValidateModel = SettingsField( + default_factory=BasicValidateModel, + title="Validate No Animation" + ) ValidateLoadedPlugin: ValidateLoadedPluginModel = SettingsField( default_factory=ValidateLoadedPluginModel, title="Validate Loaded Plugin" @@ -134,6 +138,11 @@ DEFAULT_PUBLISH_SETTINGS = { "optional": True, "family_plugins_mapping": [] }, + "ValidateNoAnimation": { + "enabled": True, + "optional": True, + "active": False, + }, "ExtractModelObj": { "enabled": True, "optional": True, diff --git a/server_addon/max/server/version.py b/server_addon/max/server/version.py index 1276d0254f..0a8da88258 100644 --- a/server_addon/max/server/version.py +++ b/server_addon/max/server/version.py @@ -1 +1 @@ -__version__ = "0.1.5" +__version__ = "0.1.6" From 6d566576cb66a20da7058c7344b09bd3ebe31408 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 11:22:17 +0100 Subject: [PATCH 287/573] fix 'self.get_product_name' calls --- .../blender/plugins/create/create_workfile.py | 14 ++++++++++++-- client/ayon_core/hosts/photoshop/lib.py | 9 ++++----- .../plugins/create/create_flatten_image.py | 8 ++++---- .../plugins/create/create_colorspace_look.py | 2 +- 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/client/ayon_core/hosts/blender/plugins/create/create_workfile.py b/client/ayon_core/hosts/blender/plugins/create/create_workfile.py index 63d35e6da8..d52ace4b65 100644 --- a/client/ayon_core/hosts/blender/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/blender/plugins/create/create_workfile.py @@ -44,7 +44,12 @@ class CreateWorkfile(BaseCreator, AutoCreator): if not workfile_instance: asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - task_name, task_name, asset_doc, project_name, host_name + self.default_variant, + task_name, + task_name, + asset_doc, + project_name, + host_name ) data = { "folderPath": asset_name, @@ -74,7 +79,12 @@ class CreateWorkfile(BaseCreator, AutoCreator): # Update instance context if it's different asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - task_name, task_name, asset_doc, project_name, host_name + self.default_variant, + task_name, + task_name, + asset_doc, + project_name, + host_name ) workfile_instance["folderPath"] = asset_name diff --git a/client/ayon_core/hosts/photoshop/lib.py b/client/ayon_core/hosts/photoshop/lib.py index 9f800300b9..eb7b4acfd5 100644 --- a/client/ayon_core/hosts/photoshop/lib.py +++ b/client/ayon_core/hosts/photoshop/lib.py @@ -52,12 +52,11 @@ class PSAutoCreator(AutoCreator): if existing_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - project_name, - asset_doc, - task_name, - host_name, - self.product_type, self.default_variant, + task_name, + asset_doc, + project_name, + host_name, ) data = { "folderPath": asset_name, diff --git a/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py b/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py index ad31463e46..d0a92745bb 100644 --- a/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py @@ -44,11 +44,11 @@ class AutoImageCreator(PSAutoCreator): if existing_instance is None: product_name = self.get_product_name( - project_name, - asset_doc, - task_name, - host_name, self.default_variant, + task_name, + asset_doc, + project_name, + host_name, ) data = { diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py index b1b50e902a..e9bf60c197 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py @@ -61,8 +61,8 @@ This creator publishes color space look file (LUT). product_name = self.get_product_name( variant=instance_data["variant"], task_name=instance_data["task"] or "Not set", - project_name=self.project_name, asset_doc=asset_doc, + project_name=self.project_name, ) instance_data["creator_attributes"] = { From 999563ea521a877fd9074c52cec00d8ccb21bf77 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 26 Feb 2024 18:31:39 +0800 Subject: [PATCH 288/573] tweak comment and some syntax error --- .../max/plugins/publish/validate_no_animation.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_no_animation.py b/client/ayon_core/hosts/max/plugins/publish/validate_no_animation.py index 9f859a1b28..4f57933085 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_no_animation.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_no_animation.py @@ -46,22 +46,22 @@ class ValidateNoAnimation(pyblish.api.InstancePlugin, sel_pos_ctl = rt.getPropertyController( sel.controller, 'Position') ctl_count = (sel_pos_ctl.keys).count - if len(ctl_count) > 0: + if ctl_count > 0: invalid.append( - (sel), f"Object Position(s) has {ctl_count} keyframe(s)") + ( (sel), f"Object Position(s) has {ctl_count} keyframe(s)")) sel_rot_ctl = rt.getPropertyController( sel.controller, "Rotation" ) ctl_count = (sel_rot_ctl.keys).count - if len(ctl_count) > 0: + if ctl_count > 0: invalid.append( - (sel), f"Object Rotation(s) has {ctl_count} keyframe(s)") + ((sel), f"Object Rotation(s) has {ctl_count} keyframe(s)")) sel_scale_ctl = rt.getPropertyController( sel.controller, "Scale" ) ctl_count = (sel_scale_ctl.keys).count - if len(ctl_count) > 0: + if ctl_count > 0: invalid.append( - (sel), f"Object Rotation(s) has {ctl_count} keyframe(s)") + ((sel), f"Object Scale(s) has {ctl_count} keyframe(s)")) return invalid From ad5b8cee48d3d32379b9b0ce9450039b1d83cfb7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 11:42:21 +0100 Subject: [PATCH 289/573] removed deprecated import --- .../hosts/photoshop/plugins/publish/collect_workfile.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_workfile.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_workfile.py index 5ca03faf8f..b9080a12ff 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_workfile.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_workfile.py @@ -1,8 +1,6 @@ import os import pyblish.api -from ayon_core.pipeline.create import get_subset_name - class CollectWorkfile(pyblish.api.ContextPlugin): """Collect current script for publish.""" From c03bc9b8a1a647a5cd79d0d3d2b35e086807a2dd Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 11:43:35 +0100 Subject: [PATCH 290/573] use product naming in workfile template builder --- .../workfile/workfile_template_builder.py | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/client/ayon_core/pipeline/workfile/workfile_template_builder.py b/client/ayon_core/pipeline/workfile/workfile_template_builder.py index 8da3961a3d..fb82cbbdaa 100644 --- a/client/ayon_core/pipeline/workfile/workfile_template_builder.py +++ b/client/ayon_core/pipeline/workfile/workfile_template_builder.py @@ -1471,7 +1471,7 @@ class PlaceholderLoadMixin(object): Note: This returns all representation documents from all versions of - matching subset. To filter for last version use + matching product. To filter for last version use '_reduce_last_version_repre_docs'. Args: @@ -1562,22 +1562,22 @@ class PlaceholderLoadMixin(object): repre_context = repre_doc["context"] asset_name = repre_context["asset"] - subset_name = repre_context["subset"] + product_name = repre_context["subset"] version = repre_context.get("version", -1) if asset_name not in mapping: mapping[asset_name] = {} - subset_mapping = mapping[asset_name] - if subset_name not in subset_mapping: - subset_mapping[subset_name] = collections.defaultdict(list) + product_mapping = mapping[asset_name] + if product_name not in product_mapping: + product_mapping[product_name] = collections.defaultdict(list) - version_mapping = subset_mapping[subset_name] + version_mapping = product_mapping[product_name] version_mapping[version].append(repre_doc) output = [] - for subset_mapping in mapping.values(): - for version_mapping in subset_mapping.values(): + for product_mapping in mapping.values(): + for version_mapping in product_mapping.values(): last_version = tuple(sorted(version_mapping.keys()))[-1] output.extend(version_mapping[last_version]) return output @@ -1588,8 +1588,8 @@ class PlaceholderLoadMixin(object): Note: Ignore repre ids is to avoid loading the same representation again on load. But the representation can be loaded with different loader - and there could be published new version of matching subset for the - representation. We should maybe expect containers. + and there could be published new version of matching product for + the representation. We should maybe expect containers. Also import loaders don't have containers at all... @@ -1761,7 +1761,7 @@ class PlaceholderCreateMixin(object): tooltip=( "Creator" "\nDefines variant name which will be use for " - "\ncompiling of subset name." + "\ncompiling of product name." ) ), attribute_definitions.UISeparatorDef(), @@ -1796,7 +1796,7 @@ class PlaceholderCreateMixin(object): creator_plugin = self.builder.get_creators_by_name()[creator_name] - # create subset name + # create product name context = self._builder.get_current_context() project_name = context["project_name"] asset_name = context["asset_name"] @@ -1807,7 +1807,7 @@ class PlaceholderCreateMixin(object): project_name, asset_name, fields=["_id"] ) assert asset_doc, "No current asset found in Session" - subset_name = creator_plugin.get_subset_name( + product_name = creator_plugin.get_product_name( create_variant, task_name, asset_doc["_id"], @@ -1817,7 +1817,7 @@ class PlaceholderCreateMixin(object): else: asset_doc = get_asset_by_name(project_name, asset_name) assert asset_doc, "No current asset found in Session" - subset_name = creator_plugin.get_subset_name( + product_name = creator_plugin.get_product_name( create_variant, task_name, asset_doc, @@ -1828,17 +1828,17 @@ class PlaceholderCreateMixin(object): creator_data = { "creator_name": creator_name, "create_variant": create_variant, - "subset_name": subset_name, + "product_name": product_name, "creator_plugin": creator_plugin } self._before_instance_create(placeholder) - # compile subset name from variant + # compile product name from variant try: if legacy_create: creator_instance = creator_plugin( - subset_name, + product_name, asset_name ).process() else: From cb4cf9d8c0093d0768c667fca2f85fbd6039bcac Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 11:43:45 +0100 Subject: [PATCH 291/573] more product naming --- .../hosts/unreal/plugins/load/load_layout_existing.py | 4 ++-- client/ayon_core/plugins/load/delete_old_versions.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py b/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py index 6f390b4920..45914146a6 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py @@ -284,9 +284,9 @@ class ExistingLayoutLoader(plugin.Loader): # Create the container for the asset. asset = repr_data.get('context').get('asset') - subset = repr_data.get('context').get('subset') + product_name = repr_data.get('context').get('subset') container = self._create_container( - f"{asset}_{subset}", mesh_path, asset, + f"{asset}_{product_name}", mesh_path, asset, repr_data.get('_id'), repr_data.get('parent'), repr_data.get('context').get('family') ) diff --git a/client/ayon_core/plugins/load/delete_old_versions.py b/client/ayon_core/plugins/load/delete_old_versions.py index 956b232e2b..4fc61ebb8b 100644 --- a/client/ayon_core/plugins/load/delete_old_versions.py +++ b/client/ayon_core/plugins/load/delete_old_versions.py @@ -401,7 +401,7 @@ # import ftrack_api # # session = ftrack_api.Session() -# subset_name = data["subset"]["name"] +# product_name = data["subset"]["name"] # versions = { # '"{}"'.format(version_doc["name"]) # for version_doc in data["versions"] @@ -414,7 +414,7 @@ # " and version in ({})" # ).format( # asset_ftrack_id, -# subset_name, +# product_name, # ",".join(versions) # ) # ).all() From c90ddbcb6b387b100f99acf6b759393c96f0e3bc Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 26 Feb 2024 19:14:56 +0800 Subject: [PATCH 292/573] tweak on get_invalid function and clean up code for getting folderPath and task data --- .../publish/validate_instance_in_context.py | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py b/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py index 5d544513c0..e4d078b36c 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py @@ -39,9 +39,9 @@ class ValidateInstanceInContext(pyblish.api.InstancePlugin, if not instance_node: return asset = rt.getUserProp(instance_node, "folderPath") - context_asset = self.get_context_asset(instance) + context_asset = instance.context.data["folderPath"] task = rt.getUserProp(instance_node, "task") - context_task = self.get_context_task(instance) + context_task = instance.context.data["task"] if asset != context_asset: raise PublishValidationError( message=( @@ -81,27 +81,26 @@ class ValidateInstanceInContext(pyblish.api.InstancePlugin, @classmethod def get_invalid(cls, instance): + invalid = [] node = rt.getNodeByName(instance.data["instance_node"]) asset = rt.getUserProp(node, "folderPath") - context_asset = cls.get_context_asset(instance) + context_asset = instance.context.data["folderPath"] if asset != context_asset: - return instance.data["instance_node"] + invalid.append(node) + task = rt.getUserProp(node, "task") + context_task = instance.context.data["task"] + if task != context_task: + invalid.append(node) + + return invalid @classmethod def repair(cls, instance): - context_asset = cls.get_context_asset(instance) - context_task = cls.get_context_task(instance) + context_asset = instance.context.data["folderPath"] + context_task = instance.context.data["task"] instance_node = rt.getNodeByName(instance.data.get( "instance_node", "")) if not instance_node: return rt.SetUserProp(instance_node, "folderPath", context_asset) rt.SetUserProp(instance_node, "task", context_task) - - @staticmethod - def get_context_asset(instance): - return instance.context.data["folderPath"] - - @staticmethod - def get_context_task(instance): - return instance.context.data["task"] From ef654cd7d8d1cbe3aea4238328aced4082bd9d4a Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 26 Feb 2024 20:05:09 +0800 Subject: [PATCH 293/573] use sel.isAnimated instead of doing check on controller --- .../plugins/publish/validate_no_animation.py | 41 ++++++------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_no_animation.py b/client/ayon_core/hosts/max/plugins/publish/validate_no_animation.py index 4f57933085..0b7a296cd9 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_no_animation.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_no_animation.py @@ -27,41 +27,24 @@ class ValidateNoAnimation(pyblish.api.InstancePlugin, return invalid = self.get_invalid(instance) if invalid: - bullet_point_invalid_statement = "\n".join( - "- {}: {}".format(obj, message) - for obj, message in invalid - ) raise PublishValidationError( - "Keyframes found on:\n\n{0}".format( - bullet_point_invalid_statement) + "Keyframes found on:\n\n{0}".format(invalid) , title="Keyframes on model" ) @staticmethod def get_invalid(instance): - invalid = [] - selected_objects = instance.data["members"] - for sel in selected_objects: - sel_pos_ctl = rt.getPropertyController( - sel.controller, 'Position') - ctl_count = (sel_pos_ctl.keys).count - if ctl_count > 0: - invalid.append( - ( (sel), f"Object Position(s) has {ctl_count} keyframe(s)")) - sel_rot_ctl = rt.getPropertyController( - sel.controller, "Rotation" - ) - ctl_count = (sel_rot_ctl.keys).count - if ctl_count > 0: - invalid.append( - ((sel), f"Object Rotation(s) has {ctl_count} keyframe(s)")) - sel_scale_ctl = rt.getPropertyController( - sel.controller, "Scale" - ) - ctl_count = (sel_scale_ctl.keys).count - if ctl_count > 0: - invalid.append( - ((sel), f"Object Scale(s) has {ctl_count} keyframe(s)")) + """Get invalid object(s) which have keyframe(s) + + + Args: + instance (pyblish.api.instance): Instance + + Returns: + list: list of invalid objects + """ + invalid = [invalid for invalid in instance.data["members"] + if invalid.isAnimated] return invalid From 0c455446cfa16dbcf886d06dc3658e37048512fc Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 26 Feb 2024 12:43:29 +0000 Subject: [PATCH 294/573] Initial working version --- .../tools/launcher/models/actions.py | 47 ++++++++++++++++--- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/tools/launcher/models/actions.py b/client/ayon_core/tools/launcher/models/actions.py index 53d2b78dc3..cc323e3745 100644 --- a/client/ayon_core/tools/launcher/models/actions.py +++ b/client/ayon_core/tools/launcher/models/actions.py @@ -293,6 +293,23 @@ class ActionsModel: self._get_action_objects() self._controller.emit_event("actions.refresh.finished") + def should_start_last_workfile(self, project_name, host_name, task_id): + from ayon_core.lib.applications import should_start_last_workfile + + task_name = None + task_type = None + if task_id is not None: + task = self._controller.get_task_entity(project_name, task_id) + task_name = task["name"] + task_type = task["taskType"] + + return should_start_last_workfile( + project_name, + host_name, + task_name, + task_type + ) + def get_action_items(self, project_name, folder_id, task_id): """Get actions for project. @@ -304,7 +321,6 @@ class ActionsModel: Returns: list[ActionItem]: List of actions. """ - not_open_workfile_actions = self._get_no_last_workfile_for_context( project_name, folder_id, task_id) session = self._prepare_session(project_name, folder_id, task_id) @@ -318,8 +334,16 @@ class ActionsModel: # Handling of 'force_not_open_workfile' for applications if action_item.is_application: action_item = action_item.copy() + host_name = action_item.identifier.replace( + "application.", "" + ).split("/")[0] + start_last_workfile_default = self.should_start_last_workfile( + project_name, host_name, task_id + ) action_item.force_not_open_workfile = ( - not_open_workfile_actions.get(identifier, False) + not_open_workfile_actions.get( + identifier, not start_last_workfile_default + ) ) output.append(action_item) @@ -359,11 +383,18 @@ class ActionsModel: per_action = self._get_no_last_workfile_for_context( project_name, folder_id, task_id ) - force_not_open_workfile = per_action.get(identifier, False) + action.data["start_last_workfile"] = True + host_name = action_item.identifier.replace( + "application.", "" + ).split("/")[0] + start_last_workfile_default = self.should_start_last_workfile( + project_name, host_name, task_id + ) + force_not_open_workfile = per_action.get( + identifier, not start_last_workfile_default + ) if force_not_open_workfile: action.data["start_last_workfile"] = False - else: - action.data.pop("start_last_workfile", None) action.process(session) except Exception as exc: self.log.warning("Action trigger failed.", exc_info=True) @@ -464,6 +495,10 @@ class ActionsModel: label = action.label or identifier variant_label = getattr(action, "label_variant", None) icon = get_action_icon(action) + host_name = identifier.replace("application.", "").split("/")[0] + force_not_open_workfile = not self.should_start_last_workfile( + project_name, host_name, None + ) item = ActionItem( identifier, label, @@ -471,7 +506,7 @@ class ActionsModel: icon, action.order, is_application, - False + force_not_open_workfile ) action_items[identifier] = item self._action_items[project_name] = action_items From 0bf93a1324a4ae6e7f902aba064d0d55427e050c Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 26 Feb 2024 12:55:44 +0000 Subject: [PATCH 295/573] Code cosmetics --- .../tools/launcher/models/actions.py | 53 ++++++++++++------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/client/ayon_core/tools/launcher/models/actions.py b/client/ayon_core/tools/launcher/models/actions.py index cc323e3745..8246bfa007 100644 --- a/client/ayon_core/tools/launcher/models/actions.py +++ b/client/ayon_core/tools/launcher/models/actions.py @@ -334,17 +334,23 @@ class ActionsModel: # Handling of 'force_not_open_workfile' for applications if action_item.is_application: action_item = action_item.copy() - host_name = action_item.identifier.replace( - "application.", "" - ).split("/")[0] - start_last_workfile_default = self.should_start_last_workfile( - project_name, host_name, task_id - ) - action_item.force_not_open_workfile = ( - not_open_workfile_actions.get( - identifier, not start_last_workfile_default + + if identifier in not_open_workfile_actions: + action_item.force_not_open_workfile = ( + not_open_workfile_actions[identifier] + ) + else: + host_name = action_item.identifier.replace( + "application.", "" + ).split("/")[0] + start_last_workfile_default = ( + self.should_start_last_workfile( + project_name, host_name, task_id + ) + ) + action_item.force_not_open_workfile = ( + not start_last_workfile_default ) - ) output.append(action_item) return output @@ -384,17 +390,26 @@ class ActionsModel: project_name, folder_id, task_id ) action.data["start_last_workfile"] = True - host_name = action_item.identifier.replace( - "application.", "" - ).split("/")[0] - start_last_workfile_default = self.should_start_last_workfile( - project_name, host_name, task_id - ) - force_not_open_workfile = per_action.get( - identifier, not start_last_workfile_default - ) + + force_not_open_workfile = None + if identifier in per_action: + force_not_open_workfile = per_action[identifier] + else: + host_name = action_item.identifier.replace( + "application.", "" + ).split("/")[0] + start_last_workfile_default = ( + self.should_start_last_workfile( + project_name, host_name, task_id + ) + ) + force_not_open_workfile = per_action.get( + identifier, not start_last_workfile_default + ) + if force_not_open_workfile: action.data["start_last_workfile"] = False + action.process(session) except Exception as exc: self.log.warning("Action trigger failed.", exc_info=True) From f207c396495ae65969848291f58f82c9d83f478f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 26 Feb 2024 14:45:06 +0100 Subject: [PATCH 296/573] Refactor tag data handling for backward compatibility. - Update product_name and product_type handling - Add error logging for missing values --- client/ayon_core/hosts/resolve/api/plugin.py | 1 - .../plugins/publish/precollect_instances.py | 26 ++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/resolve/api/plugin.py b/client/ayon_core/hosts/resolve/api/plugin.py index 607bf58afd..0c63dead32 100644 --- a/client/ayon_core/hosts/resolve/api/plugin.py +++ b/client/ayon_core/hosts/resolve/api/plugin.py @@ -630,7 +630,6 @@ class PublishClip: self.track_name = str(track_name).replace(" ", "_") self.track_index = int(timeline_item_data["track"]["index"]) - # adding tag.family into tag if kwargs.get("avalon"): self.tag_data.update(kwargs["avalon"]) diff --git a/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py b/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py index 3154e0c3e5..b1374859e3 100644 --- a/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py +++ b/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py @@ -64,7 +64,28 @@ class PrecollectInstances(pyblish.api.ContextPlugin): }) asset = tag_data["folder_path"] - product_name = tag_data["productName"] + + # TODO: remove backward compatibility + product_name = tag_data.get("productName") + if product_name is None: + # backward compatibility: subset -> productName + product_name = tag_data.get("subset") + + # backward compatibility: product_name should not be missing + if not product_name: + self.log.error( + "Product name is not defined for: {}".format(asset)) + + # TODO: remove backward compatibility + product_type = tag_data.get("productType") + if product_type is None: + # backward compatibility: family -> productType + product_type = tag_data.get("family") + + # backward compatibility: product_type should not be missing + if not product_type: + self.log.error( + "Product type is not defined for: {}".format(asset)) data.update({ "name": "{}_{}".format(asset, product_name), @@ -77,6 +98,9 @@ class PrecollectInstances(pyblish.api.ContextPlugin): "handleEnd": handle_end, "newAssetPublishing": True, "families": ["clip"], + "productType": product_type, + "productName": product_name, + "family": product_type }) # otio clip data From ce87996046a92ec9893a021e8b6c0a02bdc666a9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 15:10:42 +0100 Subject: [PATCH 297/573] added 'get_project_environments' to lib functions --- client/ayon_core/settings/lib.py | 34 +++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/settings/lib.py b/client/ayon_core/settings/lib.py index 3ddba85c9b..eadf3ba544 100644 --- a/client/ayon_core/settings/lib.py +++ b/client/ayon_core/settings/lib.py @@ -171,9 +171,37 @@ def get_project_settings(project_name, *args, **kwargs): return _AyonSettingsCache.get_value_by_project(project_name) -def get_general_environments(): - settings = get_ayon_settings() - return json.loads(settings["core"]["environments"]) +def get_general_environments(studio_settings=None): + """General studio environment variables. + + Args: + studio_settings (Optional[dict]): Pre-queried studio settings. + + Returns: + dict[str, Any]: General studio environment variables. + + """ + if studio_settings is None: + studio_settings = get_ayon_settings() + return json.loads(studio_settings["core"]["environments"]) + + +def get_project_environments(project_name, project_settings=None): + """Project environment variables. + + Args: + project_name (str): Project name. + project_settings (Optional[dict]): Pre-queried project settings. + + Returns: + dict[str, Any]: Project environment variables. + + """ + if project_settings is None: + project_settings = get_project_settings(project_name) + return json.loads( + project_settings["core"]["project_environments"] + ) def get_current_project_settings(): From 5c73bd41306392ce0e78fe61e11c189c8016a3f4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 15:22:15 +0100 Subject: [PATCH 298/573] use 'get_studio_settings' instead of 'get_ayon_settings' --- client/ayon_core/addon/base.py | 4 ++-- client/ayon_core/tools/tray/tray.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/addon/base.py b/client/ayon_core/addon/base.py index f71a82b591..f0763649ca 100644 --- a/client/ayon_core/addon/base.py +++ b/client/ayon_core/addon/base.py @@ -17,7 +17,7 @@ import appdirs from ayon_core.lib import Logger, is_dev_mode_enabled from ayon_core.client import get_ayon_server_api_connection -from ayon_core.settings import get_ayon_settings +from ayon_core.settings import get_studio_settings from .interfaces import ( IPluginPaths, @@ -733,7 +733,7 @@ class AddonsManager: # Prepare settings for addons settings = self._settings if settings is None: - settings = get_ayon_settings() + settings = get_studio_settings() modules_settings = {} diff --git a/client/ayon_core/tools/tray/tray.py b/client/ayon_core/tools/tray/tray.py index d09f40b7fc..3c6c529be8 100644 --- a/client/ayon_core/tools/tray/tray.py +++ b/client/ayon_core/tools/tray/tray.py @@ -17,7 +17,7 @@ from ayon_core.lib import ( is_staging_enabled, is_running_from_build, ) -from ayon_core.settings import get_ayon_settings +from ayon_core.settings import get_studio_settings from ayon_core.addon import ( ITrayAction, ITrayService, @@ -47,7 +47,7 @@ class TrayManager: self.log = Logger.get_logger(self.__class__.__name__) - studio_settings = get_ayon_settings() + studio_settings = get_studio_settings() update_check_interval = studio_settings["core"].get( "update_check_interval" From 973aff1300ad2f4d87721e0b31f91583d995d1e3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 15:31:02 +0100 Subject: [PATCH 299/573] fix 'prouctName' to 'productName' --- .../hosts/blender/plugins/publish/extract_abc_animation.py | 2 +- client/ayon_core/hosts/blender/plugins/publish/extract_blend.py | 2 +- .../hosts/blender/plugins/publish/extract_blend_animation.py | 2 +- .../hosts/blender/plugins/publish/extract_camera_fbx.py | 2 +- client/ayon_core/hosts/blender/plugins/publish/extract_fbx.py | 2 +- .../hosts/blender/plugins/publish/extract_fbx_animation.py | 2 +- .../hosts/blender/plugins/publish/extract_playblast.py | 2 +- .../hosts/blender/plugins/publish/extract_thumbnail.py | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_abc_animation.py b/client/ayon_core/hosts/blender/plugins/publish/extract_abc_animation.py index 1aea8988e2..0086dccd67 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_abc_animation.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_abc_animation.py @@ -24,7 +24,7 @@ class ExtractAnimationABC( # Define extract output file path stagingdir = self.staging_dir(instance) folder_name = instance.data["assetEntity"]["name"] - product_name = instance.data["prouctName"] + product_name = instance.data["productName"] instance_name = f"{folder_name}_{product_name}" filename = f"{instance_name}.abc" diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_blend.py b/client/ayon_core/hosts/blender/plugins/publish/extract_blend.py index 2973df74b4..dd2e33df80 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_blend.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_blend.py @@ -21,7 +21,7 @@ class ExtractBlend(publish.Extractor, publish.OptionalPyblishPluginMixin): stagingdir = self.staging_dir(instance) folder_name = instance.data["assetEntity"]["name"] - product_name = instance.data["prouctName"] + product_name = instance.data["productName"] instance_name = f"{folder_name}_{product_name}" filename = f"{instance_name}.blend" filepath = os.path.join(stagingdir, filename) diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_blend_animation.py b/client/ayon_core/hosts/blender/plugins/publish/extract_blend_animation.py index b30990ab15..da663b46ea 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_blend_animation.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_blend_animation.py @@ -24,7 +24,7 @@ class ExtractBlendAnimation( stagingdir = self.staging_dir(instance) folder_name = instance.data["assetEntity"]["name"] - product_name = instance.data["prouctName"] + product_name = instance.data["productName"] instance_name = f"{folder_name}_{product_name}" filename = f"{instance_name}.blend" filepath = os.path.join(stagingdir, filename) diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_camera_fbx.py b/client/ayon_core/hosts/blender/plugins/publish/extract_camera_fbx.py index 21dfa457be..03059f1e13 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_camera_fbx.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_camera_fbx.py @@ -21,7 +21,7 @@ class ExtractCamera(publish.Extractor, publish.OptionalPyblishPluginMixin): # Define extract output file path stagingdir = self.staging_dir(instance) folder_name = instance.data["assetEntity"]["name"] - product_name = instance.data["prouctName"] + product_name = instance.data["productName"] instance_name = f"{folder_name}_{product_name}" filename = f"{instance_name}.fbx" filepath = os.path.join(stagingdir, filename) diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_fbx.py b/client/ayon_core/hosts/blender/plugins/publish/extract_fbx.py index 6b9dc01f98..8fea077e7c 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_fbx.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_fbx.py @@ -22,7 +22,7 @@ class ExtractFBX(publish.Extractor, publish.OptionalPyblishPluginMixin): # Define extract output file path stagingdir = self.staging_dir(instance) folder_name = instance.data["assetEntity"]["name"] - product_name = instance.data["prouctName"] + product_name = instance.data["productName"] instance_name = f"{folder_name}_{product_name}" filename = f"{instance_name}.fbx" filepath = os.path.join(stagingdir, filename) diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_fbx_animation.py b/client/ayon_core/hosts/blender/plugins/publish/extract_fbx_animation.py index 4b65c58879..b98167c741 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_fbx_animation.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_fbx_animation.py @@ -146,7 +146,7 @@ class ExtractAnimationFBX( root.select_set(True) armature.select_set(True) folder_name = instance.data["assetEntity"]["name"] - product_name = instance.data["prouctName"] + product_name = instance.data["productName"] instance_name = f"{folder_name}_{product_name}" fbx_filename = f"{instance_name}_{armature.name}.fbx" filepath = os.path.join(stagingdir, fbx_filename) diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_playblast.py b/client/ayon_core/hosts/blender/plugins/publish/extract_playblast.py index 638c9f6f80..acb09d0d77 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_playblast.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_playblast.py @@ -56,7 +56,7 @@ class ExtractPlayblast(publish.Extractor, publish.OptionalPyblishPluginMixin): # get output path stagingdir = self.staging_dir(instance) folder_name = instance.data["assetEntity"]["name"] - product_name = instance.data["prouctName"] + product_name = instance.data["productName"] filename = f"{folder_name}_{product_name}" path = os.path.join(stagingdir, filename) diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_thumbnail.py b/client/ayon_core/hosts/blender/plugins/publish/extract_thumbnail.py index 88293dde96..89168fb9c9 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_thumbnail.py @@ -33,7 +33,7 @@ class ExtractThumbnail(publish.Extractor): stagingdir = self.staging_dir(instance) folder_name = instance.data["assetEntity"]["name"] - product_name = instance.data["prouctName"] + product_name = instance.data["productName"] filename = f"{folder_name}_{product_name}" path = os.path.join(stagingdir, filename) From 7888d2e59146a8982cf87ae9049f0d6f708c2aaf Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 26 Feb 2024 15:33:20 +0100 Subject: [PATCH 300/573] Add itertools import, update data handling, and handle backward compatibility. Remove redundant code and improve data assignment. --- .../plugins/publish/collect_clip_effects.py | 21 +++++++++------- .../plugins/publish/precollect_instances.py | 24 +++++++++++++++++++ 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/hosts/hiero/plugins/publish/collect_clip_effects.py b/client/ayon_core/hosts/hiero/plugins/publish/collect_clip_effects.py index 1107bb19f6..32b4864022 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/collect_clip_effects.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/collect_clip_effects.py @@ -1,3 +1,4 @@ +from itertools import product import re import pyblish.api @@ -114,15 +115,17 @@ class CollectClipEffects(pyblish.api.InstancePlugin): continue data[key] = value - # change names - data["productName"] = product_name - data["productType"] = product_type - data["families"] = [product_type] - data["name"] = product_name + "_" + data["folderPath"] - data["label"] = "{} - {}".format( - data["folderPath"], product_name - ) - data["effects"] = effects + data.update({ + "productName": product_name, + "productType": product_type, + "family": product_type, + "families": [product_type], + "name": product_name + "_" + data["folderPath"], + "label": "{} - {}".format( + data["folderPath"], product_name + ), + "effects": effects, + }) # create new instance _instance = instance.context.create_instance(**data) diff --git a/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py b/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py index f75f2a800e..911b96c280 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py @@ -99,11 +99,35 @@ class PrecollectInstances(pyblish.api.ContextPlugin): label += " ({})".format(clip_name) label += " {}".format(product_name) + # TODO: remove backward compatibility + product_name = tag_data.get("productName") + if product_name is None: + # backward compatibility: subset -> productName + product_name = tag_data.get("subset") + + # backward compatibility: product_name should not be missing + if not product_name: + self.log.error( + "Product name is not defined for: {}".format(asset)) + + # TODO: remove backward compatibility + product_type = tag_data.get("productType") + if product_type is None: + # backward compatibility: family -> productType + product_type = tag_data.get("family") + + # backward compatibility: product_type should not be missing + if not product_type: + self.log.error( + "Product type is not defined for: {}".format(asset)) + data.update({ "name": "{}_{}".format(asset, product_name), "label": label, "folderPath": asset, "asset_name": asset_name, + "productName": product_name, + "productType": product_type, "item": track_item, "families": families, "publish": tag_data["publish"], From 8dd9d25ac44ec58e1ca0e7a0214f846d92adf0c1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 15:48:55 +0100 Subject: [PATCH 301/573] fix 'get_product_name' in blender workfile creator --- .../ayon_core/hosts/blender/plugins/create/create_workfile.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/ayon_core/hosts/blender/plugins/create/create_workfile.py b/client/ayon_core/hosts/blender/plugins/create/create_workfile.py index d52ace4b65..21b383ab97 100644 --- a/client/ayon_core/hosts/blender/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/blender/plugins/create/create_workfile.py @@ -46,7 +46,6 @@ class CreateWorkfile(BaseCreator, AutoCreator): product_name = self.get_product_name( self.default_variant, task_name, - task_name, asset_doc, project_name, host_name @@ -81,7 +80,6 @@ class CreateWorkfile(BaseCreator, AutoCreator): product_name = self.get_product_name( self.default_variant, task_name, - task_name, asset_doc, project_name, host_name From ff0c6cef650493e2122ce34274d91045be81ca1a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 16:16:23 +0100 Subject: [PATCH 302/573] don't use "modules" subkey --- client/ayon_core/hosts/maya/plugins/publish/collect_render.py | 4 +--- .../modules/timers_manager/plugins/publish/start_timer.py | 4 ++-- .../modules/timers_manager/plugins/publish/stop_timer.py | 4 ++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_render.py b/client/ayon_core/hosts/maya/plugins/publish/collect_render.py index d5392fba4a..f91cca1f38 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/collect_render.py +++ b/client/ayon_core/hosts/maya/plugins/publish/collect_render.py @@ -293,9 +293,7 @@ class CollectMayaRender(pyblish.api.InstancePlugin): "colorspaceView": colorspace_data["view"], } - rr_settings = ( - context.data["system_settings"]["modules"]["royalrender"] - ) + rr_settings = context.data["system_settings"]["royalrender"] if rr_settings["enabled"]: data["rrPathName"] = instance.data.get("rrPathName") self.log.debug(data["rrPathName"]) diff --git a/client/ayon_core/modules/timers_manager/plugins/publish/start_timer.py b/client/ayon_core/modules/timers_manager/plugins/publish/start_timer.py index a3eb49ee70..b551c01815 100644 --- a/client/ayon_core/modules/timers_manager/plugins/publish/start_timer.py +++ b/client/ayon_core/modules/timers_manager/plugins/publish/start_timer.py @@ -18,8 +18,8 @@ class StartTimer(pyblish.api.ContextPlugin): self.log.debug("TimersManager is disabled") return - modules_settings = context.data["system_settings"]["modules"] - if not modules_settings["timers_manager"]["disregard_publishing"]: + studio_settings = context.data["system_settings"] + if not studio_settings["timers_manager"]["disregard_publishing"]: self.log.debug("Publish is not affecting running timers.") return diff --git a/client/ayon_core/modules/timers_manager/plugins/publish/stop_timer.py b/client/ayon_core/modules/timers_manager/plugins/publish/stop_timer.py index 9d7cb33ba9..9c3a63e78e 100644 --- a/client/ayon_core/modules/timers_manager/plugins/publish/stop_timer.py +++ b/client/ayon_core/modules/timers_manager/plugins/publish/stop_timer.py @@ -19,8 +19,8 @@ class StopTimer(pyblish.api.ContextPlugin): self.log.debug("TimersManager is disabled") return - modules_settings = context.data["system_settings"]["modules"] - if not modules_settings["timers_manager"]["disregard_publishing"]: + studio_settings = context.data["system_settings"] + if not studio_settings["timers_manager"]["disregard_publishing"]: self.log.debug("Publish is not affecting running timers.") return From 4ddfc7e00cfeae5e290862cbca59b9655940091a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 17:51:34 +0100 Subject: [PATCH 303/573] switch and update expecte representation context --- client/ayon_core/pipeline/load/plugins.py | 6 +++--- client/ayon_core/pipeline/load/utils.py | 20 +++++++++++++++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 962417c6f2..77b78a9ae3 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -192,13 +192,13 @@ class LoaderPlugin(list): raise NotImplementedError("Loader.load() must be " "implemented by subclass") - def update(self, container, representation): + def update(self, container, context): """Update `container` to `representation` - Arguments: + Args: container (avalon-core:container-1.0): Container to update, from `host.ls()`. - representation (dict): Update the container to this representation. + context (dict): Update the container to this representation. """ raise NotImplementedError("Loader.update() must be " diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index 056836d712..e2bc1c7a26 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -1,7 +1,6 @@ import os import platform import copy -import getpass import logging import inspect import collections @@ -11,7 +10,9 @@ from ayon_core.host import ILoadHost from ayon_core.client import ( get_project, get_assets, + get_asset_by_id, get_subsets, + get_subset_by_id, get_versions, get_version_by_id, get_last_version_by_subset_id, @@ -481,6 +482,8 @@ def update_container(container, version=-1): new_version = get_version_by_name( project_name, version, current_version["parent"], fields=["_id"] ) + subset_doc = get_subset_by_id(project_name, current_version["parent"]) + asset_doc = get_asset_by_id(project_name, subset_doc["parent"]) assert new_version is not None, "This is a bug" @@ -499,8 +502,19 @@ def update_container(container, version=-1): "Can't update container because loader '{}' was not found." .format(container.get("loader")) ) + project_doc = get_project(project_name) + context = { + "project": { + "name": project_doc["name"], + "code": project_doc["data"]["code"], + }, + "asset": asset_doc, + "subset": subset_doc, + "version": new_version, + "representation": new_representation, + } - return Loader().update(container, new_representation) + return Loader().update(container, context) def switch_container(container, representation, loader_plugin=None): @@ -549,7 +563,7 @@ def switch_container(container, representation, loader_plugin=None): loader = loader_plugin(new_context) - return loader.switch(container, new_representation) + return loader.switch(container, new_context) def get_representation_path_from_context(context): From 54a71203ef34481fb8cd393de50d87770f7357cb Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 17:52:45 +0100 Subject: [PATCH 304/573] unreal load plugins are expecting representation context --- .../plugins/load/load_alembic_animation.py | 15 +++++--- .../unreal/plugins/load/load_animation.py | 13 ++++--- .../hosts/unreal/plugins/load/load_camera.py | 9 +++-- .../plugins/load/load_geometrycache_abc.py | 37 +++++++++++-------- .../hosts/unreal/plugins/load/load_layout.py | 14 ++++--- .../plugins/load/load_layout_existing.py | 12 +++--- .../plugins/load/load_skeletalmesh_abc.py | 32 +++++++++------- .../plugins/load/load_skeletalmesh_fbx.py | 30 +++++++++------ .../plugins/load/load_staticmesh_abc.py | 24 ++++++------ .../plugins/load/load_staticmesh_fbx.py | 30 +++++++++------ .../hosts/unreal/plugins/load/load_uasset.py | 17 ++++++--- .../unreal/plugins/load/load_yeticache.py | 11 +++--- 12 files changed, 142 insertions(+), 102 deletions(-) diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_alembic_animation.py b/client/ayon_core/hosts/unreal/plugins/load/load_alembic_animation.py index 4d7760e385..f51c37dee6 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_alembic_animation.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_alembic_animation.py @@ -126,12 +126,15 @@ class AnimationAlembicLoader(plugin.Loader): return asset_content - def update(self, container, representation): - name = container["asset_name"] - source_path = get_representation_path(representation) + def update(self, container, context): + folder_name = container["asset_name"] + repre_doc = context["representation"] + source_path = get_representation_path(repre_doc) destination_path = container["namespace"] - task = self.get_task(source_path, destination_path, name, True) + task = self.get_task( + source_path, destination_path, folder_name, True + ) # do import fbx and replace existing data asset_tools = unreal.AssetToolsHelpers.get_asset_tools() @@ -143,8 +146,8 @@ class AnimationAlembicLoader(plugin.Loader): unreal_pipeline.imprint( container_path, { - "representation": str(representation["_id"]), - "parent": str(representation["parent"]) + "representation": str(repre_doc["_id"]), + "parent": str(repre_doc["parent"]) }) asset_content = unreal.EditorAssetLibrary.list_assets( diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_animation.py b/client/ayon_core/hosts/unreal/plugins/load/load_animation.py index 4d44b6c0c2..1258fea198 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_animation.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_animation.py @@ -246,9 +246,10 @@ class AnimationFBXLoader(plugin.Loader): unreal.EditorLevelLibrary.save_current_level() unreal.EditorLevelLibrary.load_level(master_level) - def update(self, container, representation): - name = container["asset_name"] - source_path = get_representation_path(representation) + def update(self, container, context): + repre_doc = context["representation"] + folder_name = container["asset_name"] + source_path = get_representation_path(repre_doc) asset_doc = get_current_project_asset(fields=["data.fps"]) destination_path = container["namespace"] @@ -258,7 +259,7 @@ class AnimationFBXLoader(plugin.Loader): task.set_editor_property('filename', source_path) task.set_editor_property('destination_path', destination_path) # strip suffix - task.set_editor_property('destination_name', name) + task.set_editor_property('destination_name', folder_name) task.set_editor_property('replace_existing', True) task.set_editor_property('automated', True) task.set_editor_property('save', True) @@ -305,8 +306,8 @@ class AnimationFBXLoader(plugin.Loader): unreal_pipeline.imprint( container_path, { - "representation": str(representation["_id"]), - "parent": str(representation["parent"]) + "representation": str(repre_doc["_id"]), + "parent": str(repre_doc["parent"]) }) asset_content = EditorAssetLibrary.list_assets( diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_camera.py b/client/ayon_core/hosts/unreal/plugins/load/load_camera.py index faba561085..09441d5a32 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_camera.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_camera.py @@ -260,7 +260,7 @@ class CameraLoader(plugin.Loader): return asset_content - def update(self, container, representation): + def update(self, container, context): ar = unreal.AssetRegistryHelpers.get_asset_registry() curr_level_sequence = LevelSequenceLib.get_current_level_sequence() @@ -379,12 +379,13 @@ class CameraLoader(plugin.Loader): sub_scene.set_sequence(new_sequence) + repre_doc = context["representation"] self._import_camera( EditorLevelLibrary.get_editor_world(), new_sequence, new_sequence.get_bindings(), settings, - str(representation["data"]["path"]) + str(repre_doc["data"]["path"]) ) # Set range of all sections @@ -412,8 +413,8 @@ class CameraLoader(plugin.Loader): key.set_time(unreal.FrameNumber(value=new_time)) data = { - "representation": str(representation["_id"]), - "parent": str(representation["parent"]) + "representation": str(repre_doc["_id"]), + "parent": str(repre_doc["parent"]) } imprint(f"{asset_dir}/{container.get('container_name')}", data) diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py b/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py index 360737cbc5..bca99f202f 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py @@ -108,7 +108,7 @@ class PointCacheAlembicLoader(plugin.Loader): Args: context (dict): application context - name (str): subset name + name (str): Product name namespace (str): in Unreal this is basically path to container. This is not passed here, so namespace is set by `containerise()` because only then we know @@ -163,25 +163,30 @@ class PointCacheAlembicLoader(plugin.Loader): return asset_content - def update(self, container, representation): - context = representation.get("context", {}) - - unreal.log_warning(context) - - if not context: - raise RuntimeError("No context found in representation") + def update(self, container, context): + asset_doc = context["asset"] + subset_doc = context["subset"] + version_doc = context["version"] + repre_doc = context["representation"] # Create directory for asset and Ayon container - asset = context.get('asset') - name = context.get('subset') + folder_name = asset_doc["name"] + product_name = subset_doc["name"] + suffix = "_CON" - asset_name = f"{asset}_{name}" if asset else f"{name}" - version = context.get('version') + asset_name = product_name + if folder_name: + asset_name = f"{folder_name}_{product_name}" + # Check if version is hero version and use different name - name_version = f"{name}_v{version:03d}" if version else f"{name}_hero" + version = version_doc.get("name", -1) + if version < 0: + name_version = f"{product_name}_hero" + else: + name_version = f"{product_name}_v{version:03d}" tools = unreal.AssetToolsHelpers().get_asset_tools() asset_dir, container_name = tools.create_unique_asset_name( - f"{self.root}/{asset}/{name_version}", suffix="") + f"{self.root}/{folder_name}/{name_version}", suffix="") container_name += suffix @@ -189,14 +194,14 @@ class PointCacheAlembicLoader(plugin.Loader): frame_end = int(container.get("frame_end")) if not unreal.EditorAssetLibrary.does_directory_exist(asset_dir): - path = get_representation_path(representation) + path = get_representation_path(repre_doc) self.import_and_containerize( path, asset_dir, asset_name, container_name, frame_start, frame_end) self.imprint( - asset, asset_dir, container_name, asset_name, representation, + folder_name, asset_dir, container_name, asset_name, repre_doc, frame_start, frame_end) asset_content = unreal.EditorAssetLibrary.list_assets( diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_layout.py b/client/ayon_core/hosts/unreal/plugins/load/load_layout.py index a1cc2e785a..94454e8fc4 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_layout.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_layout.py @@ -659,7 +659,7 @@ class LayoutLoader(plugin.Loader): return asset_content - def update(self, container, representation): + def update(self, container, context): data = get_current_project_settings() create_sequences = data["unreal"]["level_sequences_for_layouts"] @@ -675,9 +675,11 @@ class LayoutLoader(plugin.Loader): root = "/Game/Ayon" asset_dir = container.get('namespace') - context = representation.get("context") - hierarchy = context.get('hierarchy').split("/") + asset_doc = context["asset"] + repre_doc = context["representation"] + + hierarchy = list(asset_doc["data"]["parents"]) sequence = None master_level = None @@ -726,13 +728,13 @@ class LayoutLoader(plugin.Loader): EditorAssetLibrary.delete_directory(f"{asset_dir}/animations/") - source_path = get_representation_path(representation) + source_path = get_representation_path(repre_doc) loaded_assets = self._process(source_path, asset_dir, sequence) data = { - "representation": str(representation["_id"]), - "parent": str(representation["parent"]), + "representation": str(repre_doc["_id"]), + "parent": str(repre_doc["parent"]), "loaded_assets": loaded_assets } imprint( diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py b/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py index 6f390b4920..c4b5246488 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py @@ -407,16 +407,18 @@ class ExistingLayoutLoader(plugin.Loader): } upipeline.imprint(f"{curr_level_path}/{container_name}", data) - def update(self, container, representation): + def update(self, container, context): asset_dir = container.get('namespace') - source_path = get_representation_path(representation) - project_name = get_current_project_name() + project_name = context["project"]["name"] + repre_doc = context["representation"] + + source_path = get_representation_path(repre_doc) containers = self._process(source_path, project_name) data = { - "representation": str(representation["_id"]), - "parent": str(representation["parent"]), + "representation": str(repre_doc["_id"]), + "parent": str(repre_doc["parent"]), "loaded_assets": containers } upipeline.imprint( diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py index 225df3b440..4de05d1171 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py @@ -144,34 +144,40 @@ class SkeletalMeshAlembicLoader(plugin.Loader): return asset_content - def update(self, container, representation): - context = representation.get("context", {}) + def update(self, container, context): + asset_doc = context["asset"] + subset_doc = context["subset"] + version_doc = context["version"] + repre_doc = context["representation"] - if not context: - raise RuntimeError("No context found in representation") + folder_name = asset_doc["name"] + product_name = subset_doc["name"] - # Create directory for asset and Ayon container - asset = context.get('asset') - name = context.get('subset') + # Create directory for folder and Ayon container suffix = "_CON" - asset_name = f"{asset}_{name}" if asset else f"{name}" - version = context.get('version') + asset_name = product_name + if folder_name: + asset_name = f"{folder_name}_{product_name}" # Check if version is hero version and use different name - name_version = f"{name}_v{version:03d}" if version else f"{name}_hero" + version = version_doc.get("name", -1) + if version < 0: + name_version = f"{product_name}_hero" + else: + name_version = f"{product_name}_v{version:03d}" tools = unreal.AssetToolsHelpers().get_asset_tools() asset_dir, container_name = tools.create_unique_asset_name( - f"{self.root}/{asset}/{name_version}", suffix="") + f"{self.root}/{folder_name}/{name_version}", suffix="") container_name += suffix if not unreal.EditorAssetLibrary.does_directory_exist(asset_dir): - path = get_representation_path(representation) + path = get_representation_path(repre_doc) self.import_and_containerize(path, asset_dir, asset_name, container_name) self.imprint( - asset, asset_dir, container_name, asset_name, representation) + folder_name, asset_dir, container_name, asset_name, repre_doc) asset_content = unreal.EditorAssetLibrary.list_assets( asset_dir, recursive=True, include_folder=False diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py index 1c45c58d02..865d609a89 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py @@ -146,34 +146,40 @@ class SkeletalMeshFBXLoader(plugin.Loader): return asset_content - def update(self, container, representation): - context = representation.get("context", {}) + def update(self, container, context): + asset_doc = context["asse"] + subset_doc = context["subset"] + version_doc = context["version"] + repre_doc = context["representation"] - if not context: - raise RuntimeError("No context found in representation") + folder_name = asset_doc["name"] + product_name = subset_doc["name"] # Create directory for asset and Ayon container - asset = context.get('asset') - name = context.get('subset') suffix = "_CON" - asset_name = f"{asset}_{name}" if asset else f"{name}" - version = context.get('version') + asset_name = product_name + if folder_name: + asset_name = f"{folder_name}_{product_name}" # Check if version is hero version and use different name - name_version = f"{name}_v{version:03d}" if version else f"{name}_hero" + version = version_doc.get("name", -1) + if version < 0: + name_version = f"{product_name}_hero" + else: + name_version = f"{product_name}_v{version:03d}" tools = unreal.AssetToolsHelpers().get_asset_tools() asset_dir, container_name = tools.create_unique_asset_name( - f"{self.root}/{asset}/{name_version}", suffix="") + f"{self.root}/{folder_name}/{name_version}", suffix="") container_name += suffix if not unreal.EditorAssetLibrary.does_directory_exist(asset_dir): - path = get_representation_path(representation) + path = get_representation_path(repre_doc) self.import_and_containerize( path, asset_dir, asset_name, container_name) self.imprint( - asset, asset_dir, container_name, asset_name, representation) + folder_name, asset_dir, container_name, asset_name, repre_doc) asset_content = unreal.EditorAssetLibrary.list_assets( asset_dir, recursive=True, include_folder=False diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_abc.py b/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_abc.py index a0814b5b07..1db7cbcfaf 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_abc.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_abc.py @@ -145,34 +145,36 @@ class StaticMeshAlembicLoader(plugin.Loader): return asset_content - def update(self, container, representation): - context = representation.get("context", {}) + def update(self, container, context): + asset_doc = context["asset"] + subset_doc = context["subset"] + repre_doc = context["representation"] - if not context: - raise RuntimeError("No context found in representation") + folder_name = asset_doc["name"] + product_name = subset_doc["name"] # Create directory for asset and Ayon container - asset = context.get('asset') - name = context.get('subset') suffix = "_CON" - asset_name = f"{asset}_{name}" if asset else f"{name}" + asset_name = product_name + if folder_name: + asset_name = f"{folder_name}_{product_name}" version = context.get('version') # Check if version is hero version and use different name - name_version = f"{name}_v{version:03d}" if version else f"{name}_hero" + name_version = f"{product_name}_v{version:03d}" if version else f"{product_name}_hero" tools = unreal.AssetToolsHelpers().get_asset_tools() asset_dir, container_name = tools.create_unique_asset_name( - f"{self.root}/{asset}/{name_version}", suffix="") + f"{self.root}/{folder_name}/{name_version}", suffix="") container_name += suffix if not unreal.EditorAssetLibrary.does_directory_exist(asset_dir): - path = get_representation_path(representation) + path = get_representation_path(repre_doc) self.import_and_containerize(path, asset_dir, asset_name, container_name) self.imprint( - asset, asset_dir, container_name, asset_name, representation) + folder_name, asset_dir, container_name, asset_name, repre_doc) asset_content = unreal.EditorAssetLibrary.list_assets( asset_dir, recursive=True, include_folder=False diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py b/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py index a78b1bc959..680b9dfe29 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py @@ -134,34 +134,40 @@ class StaticMeshFBXLoader(plugin.Loader): return asset_content - def update(self, container, representation): - context = representation.get("context", {}) + def update(self, container, context): + asset_doc = context["asset"] + subset_doc = context["subset"] + version_doc = context["version"] + repre_doc = context["representation"] - if not context: - raise RuntimeError("No context found in representation") + folder_name = asset_doc["name"] + product_name = subset_doc["name"] # Create directory for asset and Ayon container - asset = context.get('asset') - name = context.get('subset') suffix = "_CON" - asset_name = f"{asset}_{name}" if asset else f"{name}" - version = context.get('version') + asset_name = product_name + if folder_name: + asset_name = f"{folder_name}_{product_name}" # Check if version is hero version and use different name - name_version = f"{name}_v{version:03d}" if version else f"{name}_hero" + version = version_doc.get("name", -1) + if version < 0: + name_version = f"{product_name}_hero" + else: + name_version = f"{product_name}_v{version:03d}" tools = unreal.AssetToolsHelpers().get_asset_tools() asset_dir, container_name = tools.create_unique_asset_name( - f"{self.root}/{asset}/{name_version}", suffix="") + f"{self.root}/{folder_name}/{name_version}", suffix="") container_name += suffix if not unreal.EditorAssetLibrary.does_directory_exist(asset_dir): - path = get_representation_path(representation) + path = get_representation_path(repre_doc) self.import_and_containerize( path, asset_dir, asset_name, container_name) self.imprint( - asset, asset_dir, container_name, asset_name, representation) + folder_name, asset_dir, container_name, asset_name, repre_doc) asset_content = unreal.EditorAssetLibrary.list_assets( asset_dir, recursive=True, include_folder=False diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_uasset.py b/client/ayon_core/hosts/unreal/plugins/load/load_uasset.py index 048ec8eaba..46e685d1bc 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_uasset.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_uasset.py @@ -96,11 +96,15 @@ class UAssetLoader(plugin.Loader): return asset_content - def update(self, container, representation): + def update(self, container, context): ar = unreal.AssetRegistryHelpers.get_asset_registry() asset_dir = container["namespace"] - name = representation["context"]["subset"] + + subset_doc = context["subset"] + repre_doc = context["representation"] + + product_name = subset_doc["name"] unique_number = container["container_name"].split("_")[-2] @@ -116,19 +120,20 @@ class UAssetLoader(plugin.Loader): if obj.get_class().get_name() != "AyonAssetContainer": unreal.EditorAssetLibrary.delete_asset(asset) - update_filepath = get_representation_path(representation) + update_filepath = get_representation_path(repre_doc) shutil.copy( update_filepath, - f"{destination_path}/{name}_{unique_number}.{self.extension}") + f"{destination_path}/{product_name}_{unique_number}.{self.extension}" + ) container_path = f'{container["namespace"]}/{container["objectName"]}' # update metadata unreal_pipeline.imprint( container_path, { - "representation": str(representation["_id"]), - "parent": str(representation["parent"]), + "representation": str(repre_doc["_id"]), + "parent": str(repre_doc["parent"]), } ) diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_yeticache.py b/client/ayon_core/hosts/unreal/plugins/load/load_yeticache.py index b643f352b7..c6e275c844 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_yeticache.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_yeticache.py @@ -69,7 +69,7 @@ class YetiLoader(plugin.Loader): Args: context (dict): application context - name (str): subset name + name (str): Product name namespace (str): in Unreal this is basically path to container. This is not passed here, so namespace is set by `containerise()` because only then we know @@ -139,9 +139,10 @@ class YetiLoader(plugin.Loader): return asset_content - def update(self, container, representation): + def update(self, container, context): + repre_doc = context["representation"] name = container["asset_name"] - source_path = get_representation_path(representation) + source_path = get_representation_path(repre_doc) destination_path = container["namespace"] task = self.get_task(source_path, destination_path, name, True) @@ -154,8 +155,8 @@ class YetiLoader(plugin.Loader): unreal_pipeline.imprint( container_path, { - "representation": str(representation["_id"]), - "parent": str(representation["parent"]) + "representation": str(repre_doc["_id"]), + "parent": str(repre_doc["parent"]) }) asset_content = unreal.EditorAssetLibrary.list_assets( From 6d71830e32cb759114efcf2ad8397b07232d4ec2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 17:53:00 +0100 Subject: [PATCH 305/573] tvpaint load plugins are expecting representation context --- .../hosts/tvpaint/plugins/load/load_reference_image.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py b/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py index 0a12e93f44..ea670996a4 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py +++ b/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py @@ -210,15 +210,17 @@ class LoadImage(plugin.Loader): def switch(self, container, representation): self.update(container, representation) - def update(self, container, representation): + def update(self, container, context): """Replace container with different version. New layers are loaded as first step. Then is tried to change data in new layers with data from old layers. When that is done old layers are removed. """ + + repre_doc = context["representation"] # Create new containers first - context = get_representation_context(representation) + context = get_representation_context(repre_doc) # Get layer ids from previous container old_layer_names = self.get_members_from_container(container) From d790d2e41162e2f7fe037f6f7ee908393b33b699 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 17:53:16 +0100 Subject: [PATCH 306/573] substance load plugins are expecting representation context --- .../hosts/substancepainter/plugins/load/load_mesh.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/hosts/substancepainter/plugins/load/load_mesh.py b/client/ayon_core/hosts/substancepainter/plugins/load/load_mesh.py index 48aa99d357..810fecb8e5 100644 --- a/client/ayon_core/hosts/substancepainter/plugins/load/load_mesh.py +++ b/client/ayon_core/hosts/substancepainter/plugins/load/load_mesh.py @@ -97,12 +97,13 @@ class SubstanceLoadProjectMesh(load.LoaderPlugin): set_container_metadata(project_mesh_object_name, container) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) - def update(self, container, representation): + def update(self, container, context): + repre_doc = context["representation"] - path = get_representation_path(representation) + path = get_representation_path(repre_doc) # Reload the mesh container_options = container.get("options", {}) @@ -121,7 +122,7 @@ class SubstanceLoadProjectMesh(load.LoaderPlugin): # Update container representation object_name = container["objectName"] - update_data = {"representation": str(representation["_id"])} + update_data = {"representation": str(repre_doc["_id"])} set_container_metadata(object_name, update_data, update=True) def remove(self, container): From 3c67c4fdd09ed7b1813f751c2b6a2544c8b7cd5c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 17:53:47 +0100 Subject: [PATCH 307/573] photoshop load plugins are expecting representation context --- .../ayon_core/hosts/photoshop/api/README.md | 12 +++++------ .../photoshop/plugins/load/load_image.py | 12 +++++------ .../plugins/load/load_image_from_sequence.py | 2 +- .../photoshop/plugins/load/load_reference.py | 21 ++++++++++++------- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/client/ayon_core/hosts/photoshop/api/README.md b/client/ayon_core/hosts/photoshop/api/README.md index 02868753d1..42feeb4142 100644 --- a/client/ayon_core/hosts/photoshop/api/README.md +++ b/client/ayon_core/hosts/photoshop/api/README.md @@ -224,23 +224,23 @@ class ImageLoader(load.LoaderPlugin): self.__class__.__name__ ) - def update(self, container, representation): + def update(self, container, context): layer = container.pop("layer") - + repre_doc = context["representation"] with photoshop.maintained_selection(): stub.replace_smart_object( - layer, get_representation_path(representation) + layer, get_representation_path(repre_doc) ) stub.imprint( - layer, {"representation": str(representation["_id"])} + layer, {"representation": str(repre_doc["_id"])} ) def remove(self, container): container["layer"].Delete() - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) ``` For easier debugging of Javascript: https://community.adobe.com/t5/download-install/adobe-extension-debuger-problem/td-p/10911704?page=1 diff --git a/client/ayon_core/hosts/photoshop/plugins/load/load_image.py b/client/ayon_core/hosts/photoshop/plugins/load/load_image.py index 0fa6bca901..ec6392bade 100644 --- a/client/ayon_core/hosts/photoshop/plugins/load/load_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/load/load_image.py @@ -36,13 +36,13 @@ class ImageLoader(photoshop.PhotoshopLoader): self.__class__.__name__ ) - def update(self, container, representation): + def update(self, container, context): """ Switch asset or change version """ stub = self.get_stub() layer = container.pop("layer") - context = representation.get("context", {}) + repre_doc = context["representation"] namespace_from_container = re.sub(r'_\d{3}$', '', container["namespace"]) @@ -55,14 +55,14 @@ class ImageLoader(photoshop.PhotoshopLoader): else: # switching version - keep same name layer_name = container["namespace"] - path = get_representation_path(representation) + path = get_representation_path(repre_doc) with photoshop.maintained_selection(): stub.replace_smart_object( layer, path, layer_name ) stub.imprint( - layer.id, {"representation": str(representation["_id"])} + layer.id, {"representation": str(repre_doc["_id"])} ) def remove(self, container): @@ -77,8 +77,8 @@ class ImageLoader(photoshop.PhotoshopLoader): stub.imprint(layer.id, {}) stub.delete_layer(layer.id) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def import_layer(self, file_name, layer_name, stub): return stub.import_smart_object(file_name, layer_name) diff --git a/client/ayon_core/hosts/photoshop/plugins/load/load_image_from_sequence.py b/client/ayon_core/hosts/photoshop/plugins/load/load_image_from_sequence.py index 06ac70041e..49ca513bd2 100644 --- a/client/ayon_core/hosts/photoshop/plugins/load/load_image_from_sequence.py +++ b/client/ayon_core/hosts/photoshop/plugins/load/load_image_from_sequence.py @@ -86,7 +86,7 @@ class ImageFromSequenceLoader(photoshop.PhotoshopLoader): ) ] - def update(self, container, representation): + def update(self, container, context): """No update possible, not containerized.""" pass diff --git a/client/ayon_core/hosts/photoshop/plugins/load/load_reference.py b/client/ayon_core/hosts/photoshop/plugins/load/load_reference.py index e2fec039d0..f83272f97d 100644 --- a/client/ayon_core/hosts/photoshop/plugins/load/load_reference.py +++ b/client/ayon_core/hosts/photoshop/plugins/load/load_reference.py @@ -37,32 +37,37 @@ class ReferenceLoader(photoshop.PhotoshopLoader): self.__class__.__name__ ) - def update(self, container, representation): + def update(self, container, context): """ Switch asset or change version """ stub = self.get_stub() layer = container.pop("layer") - context = representation.get("context", {}) + asset_doc = context["asset"] + subset_doc = context["subset"] + repre_doc = context["representation"] + + folder_name = asset_doc["name"] + product_name = subset_doc["name"] namespace_from_container = re.sub(r'_\d{3}$', '', container["namespace"]) - layer_name = "{}_{}".format(context["asset"], context["subset"]) + layer_name = "{}_{}".format(folder_name, product_name) # switching assets if namespace_from_container != layer_name: layer_name = get_unique_layer_name( - stub.get_layers(), context["asset"], context["subset"] + stub.get_layers(), folder_name, product_name ) else: # switching version - keep same name layer_name = container["namespace"] - path = get_representation_path(representation) + path = get_representation_path(repre_doc) with photoshop.maintained_selection(): stub.replace_smart_object( layer, path, layer_name ) stub.imprint( - layer.id, {"representation": str(representation["_id"])} + layer.id, {"representation": str(repre_doc["_id"])} ) def remove(self, container): @@ -76,8 +81,8 @@ class ReferenceLoader(photoshop.PhotoshopLoader): stub.imprint(layer.id, {}) stub.delete_layer(layer.id) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def import_layer(self, file_name, layer_name, stub): return stub.import_smart_object( From c575d081e48e1737af00494cb2a1da0c091de249 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 17:54:30 +0100 Subject: [PATCH 308/573] resolve load plugins are expecting representation context --- client/ayon_core/hosts/resolve/api/plugin.py | 2 +- .../hosts/resolve/plugins/load/load_clip.py | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/client/ayon_core/hosts/resolve/api/plugin.py b/client/ayon_core/hosts/resolve/api/plugin.py index ccb20f712f..42c88000f0 100644 --- a/client/ayon_core/hosts/resolve/api/plugin.py +++ b/client/ayon_core/hosts/resolve/api/plugin.py @@ -538,7 +538,7 @@ class TimelineItemLoader(LoaderPlugin): ): pass - def update(self, container, representation): + def update(self, container, context): """Update an existing `container` """ pass diff --git a/client/ayon_core/hosts/resolve/plugins/load/load_clip.py b/client/ayon_core/hosts/resolve/plugins/load/load_clip.py index 47aeac213b..2579b6a735 100644 --- a/client/ayon_core/hosts/resolve/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/resolve/plugins/load/load_clip.py @@ -59,21 +59,21 @@ class LoadClip(plugin.TimelineItemLoader): self.__class__.__name__, data_imprint) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) - def update(self, container, representation): + def update(self, container, context): """ Updating previously loaded clips """ - context = get_representation_context(representation) + repre_doc = context["representation"] name = container['name'] namespace = container['namespace'] timeline_item = container["_timeline_item"] media_pool_item = timeline_item.GetMediaPoolItem() - files = plugin.get_representation_files(representation) + files = plugin.get_representation_files(repre_doc) loader = plugin.ClipLoader(self, context) timeline_item = loader.update(timeline_item, files) @@ -92,10 +92,10 @@ class LoadClip(plugin.TimelineItemLoader): def get_tag_data(self, context, name, namespace): """Return data to be imprinted on the timeline item marker""" - representation = context["representation"] - version = context['version'] - version_data = version.get("data", {}) - version_name = version.get("name", None) + repre_doc = context["representation"] + version_doc = context["version"] + version_data = version_doc.get("data", {}) + version_name = version_doc.get("name", None) colorspace = version_data.get("colorspace", None) object_name = "{}_{}".format(name, namespace) @@ -111,7 +111,7 @@ class LoadClip(plugin.TimelineItemLoader): # add variables related to version context data.update({ - "representation": str(representation["_id"]), + "representation": str(repre_doc["_id"]), "version": version_name, "colorspace": colorspace, "objectName": object_name From 05e8ebabe62acc1cf16bcc4850045ec17aac00d8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 17:54:35 +0100 Subject: [PATCH 309/573] nuke load plugins are expecting representation context --- .../hosts/nuke/plugins/load/load_backdrop.py | 15 ++++++----- .../nuke/plugins/load/load_camera_abc.py | 14 +++++----- .../hosts/nuke/plugins/load/load_clip.py | 26 ++++++++++--------- .../hosts/nuke/plugins/load/load_effects.py | 15 ++++++----- .../nuke/plugins/load/load_effects_ip.py | 15 ++++++----- .../hosts/nuke/plugins/load/load_gizmo.py | 15 ++++++----- .../hosts/nuke/plugins/load/load_gizmo_ip.py | 15 ++++++----- .../hosts/nuke/plugins/load/load_image.py | 20 +++++++------- .../hosts/nuke/plugins/load/load_model.py | 16 ++++++------ .../hosts/nuke/plugins/load/load_ociolook.py | 15 +++++------ .../nuke/plugins/load/load_script_precomp.py | 16 +++++++----- 11 files changed, 96 insertions(+), 86 deletions(-) diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py b/client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py index ed512c86ab..642e20c979 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py @@ -178,7 +178,7 @@ class LoadBackdropNodes(load.LoaderPlugin): loader=self.__class__.__name__, data=data_imprint) - def update(self, container, representation): + def update(self, container, context): """Update the Loader's path Nuke automatically tries to reset some variables when changing @@ -189,13 +189,14 @@ class LoadBackdropNodes(load.LoaderPlugin): # get main variables # Get version from io - project_name = get_current_project_name() - version_doc = get_version_by_id(project_name, representation["parent"]) + project_name = context["project"]["name"] + version_doc = context["version"] + repre_doc = context["representation"] # get corresponding node GN = container["node"] - file = get_representation_path(representation).replace("\\", "/") + file = get_representation_path(repre_doc).replace("\\", "/") name = container['name'] version_data = version_doc.get("data", {}) @@ -207,7 +208,7 @@ class LoadBackdropNodes(load.LoaderPlugin): add_keys = ["source", "author", "fps"] data_imprint = { - "representation": str(representation["_id"]), + "representation": str(repre_doc["_id"]), "version": vname, "colorspaceInput": colorspace, } @@ -248,8 +249,8 @@ class LoadBackdropNodes(load.LoaderPlugin): return update_container(GN, data_imprint) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): node = container["node"] diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py b/client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py index 919a3beb06..e3511a4e8b 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py @@ -92,7 +92,7 @@ class AlembicCameraLoader(load.LoaderPlugin): loader=self.__class__.__name__, data=data_imprint) - def update(self, container, representation): + def update(self, container, context): """ Called by Scene Inventory when look should be updated to current version. @@ -109,8 +109,8 @@ class AlembicCameraLoader(load.LoaderPlugin): None """ # Get version from io - project_name = get_current_project_name() - version_doc = get_version_by_id(project_name, representation["parent"]) + version_doc = context["version"] + repre_doc = context["representation"] # get main variables version_data = version_doc.get("data", {}) @@ -124,7 +124,7 @@ class AlembicCameraLoader(load.LoaderPlugin): add_keys = ["source", "author", "fps"] data_imprint = { - "representation": str(representation["_id"]), + "representation": str(repre_doc["_id"]), "frameStart": first, "frameEnd": last, "version": vname @@ -134,7 +134,7 @@ class AlembicCameraLoader(load.LoaderPlugin): data_imprint.update({k: version_data[k]}) # getting file path - file = get_representation_path(representation).replace("\\", "/") + file = get_representation_path(repre_doc).replace("\\", "/") with maintained_selection(): camera_node = container["node"] @@ -191,8 +191,8 @@ class AlembicCameraLoader(load.LoaderPlugin): color_value = "0xd88467ff" node["tile_color"].setValue(int(color_value, 16)) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): node = container["node"] diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py index 31b523fbc8..e9e71baa76 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py @@ -209,8 +209,8 @@ class LoadClip(plugin.NukeLoader): return container - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def _representation_with_hash_in_frame(self, representation): """Convert frame key value to padded hash @@ -241,7 +241,7 @@ class LoadClip(plugin.NukeLoader): representation["context"]["frame"] = hashed_frame return representation - def update(self, container, representation): + def update(self, container, context): """Update the Loader's path Nuke automatically tries to reset some variables when changing @@ -250,16 +250,18 @@ class LoadClip(plugin.NukeLoader): """ - is_sequence = len(representation["files"]) > 1 + repre_doc = context["representation"] + + is_sequence = len(repre_doc["files"]) > 1 read_node = container["node"] if is_sequence: - representation = self._representation_with_hash_in_frame( - representation + repre_doc = self._representation_with_hash_in_frame( + repre_doc ) - filepath = get_representation_path(representation).replace("\\", "/") + filepath = get_representation_path(repre_doc).replace("\\", "/") self.log.debug("_ filepath: {}".format(filepath)) start_at_workfile = "start at" in read_node['frame_mode'].value() @@ -270,13 +272,13 @@ class LoadClip(plugin.NukeLoader): ] project_name = get_current_project_name() - version_doc = get_version_by_id(project_name, representation["parent"]) + version_doc = get_version_by_id(project_name, repre_doc["parent"]) version_data = version_doc.get("data", {}) - repre_id = representation["_id"] + repre_id = repre_doc["_id"] # colorspace profile - colorspace = representation["data"].get("colorspace") + colorspace = repre_doc["data"].get("colorspace") colorspace = colorspace or version_data.get("colorspace") self.handle_start = version_data.get("handleStart", 0) @@ -303,12 +305,12 @@ class LoadClip(plugin.NukeLoader): # we will switch off undo-ing with viewer_update_and_undo_stop(): used_colorspace = self._set_colorspace( - read_node, version_data, representation["data"], filepath) + read_node, version_data, repre_doc["data"], filepath) self._set_range_to_node(read_node, first, last, start_at_workfile) updated_dict = { - "representation": str(representation["_id"]), + "representation": str(repre_doc["_id"]), "frameStart": str(first), "frameEnd": str(last), "version": str(version_doc.get("name")), diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_effects.py b/client/ayon_core/hosts/nuke/plugins/load/load_effects.py index 0b5f31033e..3e87c9cf60 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_effects.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_effects.py @@ -146,7 +146,7 @@ class LoadEffects(load.LoaderPlugin): loader=self.__class__.__name__, data=data_imprint) - def update(self, container, representation): + def update(self, container, context): """Update the Loader's path Nuke automatically tries to reset some variables when changing @@ -156,13 +156,14 @@ class LoadEffects(load.LoaderPlugin): """ # get main variables # Get version from io - project_name = get_current_project_name() - version_doc = get_version_by_id(project_name, representation["parent"]) + project_name = context["project"]["name"] + version_doc = context["version"] + repre_doc = context["representation"] # get corresponding node GN = container["node"] - file = get_representation_path(representation).replace("\\", "/") + file = get_representation_path(repre_doc).replace("\\", "/") name = container['name'] version_data = version_doc.get("data", {}) vname = version_doc.get("name", None) @@ -177,7 +178,7 @@ class LoadEffects(load.LoaderPlugin): "source", "author", "fps"] data_imprint = { - "representation": str(representation["_id"]), + "representation": str(repre_doc["_id"]), "frameStart": first, "frameEnd": last, "version": vname, @@ -344,8 +345,8 @@ class LoadEffects(load.LoaderPlugin): else: return input - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): node = container["node"] diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py b/client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py index 4d8a8518f2..5c363cddc4 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py @@ -150,7 +150,7 @@ class LoadEffectsInputProcess(load.LoaderPlugin): loader=self.__class__.__name__, data=data_imprint) - def update(self, container, representation): + def update(self, container, context): """Update the Loader's path Nuke automatically tries to reset some variables when changing @@ -161,13 +161,14 @@ class LoadEffectsInputProcess(load.LoaderPlugin): # get main variables # Get version from io - project_name = get_current_project_name() - version_doc = get_version_by_id(project_name, representation["parent"]) + project_name = context["project"]["name"] + version_doc = context["version"] + repre_doc = context["representation"] # get corresponding node GN = container["node"] - file = get_representation_path(representation).replace("\\", "/") + file = get_representation_path(repre_doc).replace("\\", "/") version_data = version_doc.get("data", {}) vname = version_doc.get("name", None) first = version_data.get("frameStart", None) @@ -179,7 +180,7 @@ class LoadEffectsInputProcess(load.LoaderPlugin): "source", "author", "fps"] data_imprint = { - "representation": str(representation["_id"]), + "representation": str(repre_doc["_id"]), "frameStart": first, "frameEnd": last, "version": vname, @@ -355,8 +356,8 @@ class LoadEffectsInputProcess(load.LoaderPlugin): else: return input - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): node = container["node"] diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py b/client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py index 54daa74405..058228a145 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py @@ -97,7 +97,7 @@ class LoadGizmo(load.LoaderPlugin): loader=self.__class__.__name__, data=data_imprint) - def update(self, container, representation): + def update(self, container, context): """Update the Loader's path Nuke automatically tries to reset some variables when changing @@ -108,13 +108,14 @@ class LoadGizmo(load.LoaderPlugin): # get main variables # Get version from io - project_name = get_current_project_name() - version_doc = get_version_by_id(project_name, representation["parent"]) + project_name = context["project"]["name"] + version_doc = context["version"] + repre_doc = context["representation"] # get corresponding node group_node = container["node"] - file = get_representation_path(representation).replace("\\", "/") + file = get_representation_path(repre_doc).replace("\\", "/") name = container['name'] version_data = version_doc.get("data", {}) vname = version_doc.get("name", None) @@ -128,7 +129,7 @@ class LoadGizmo(load.LoaderPlugin): "source", "author", "fps"] data_imprint = { - "representation": str(representation["_id"]), + "representation": str(repre_doc["_id"]), "frameStart": first, "frameEnd": last, "version": vname, @@ -173,8 +174,8 @@ class LoadGizmo(load.LoaderPlugin): return update_container(new_group_node, data_imprint) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): node = container["node"] diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py b/client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py index 677d9868f1..61e1c34028 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py @@ -104,7 +104,7 @@ class LoadGizmoInputProcess(load.LoaderPlugin): loader=self.__class__.__name__, data=data_imprint) - def update(self, container, representation): + def update(self, container, context): """Update the Loader's path Nuke automatically tries to reset some variables when changing @@ -115,13 +115,14 @@ class LoadGizmoInputProcess(load.LoaderPlugin): # get main variables # Get version from io - project_name = get_current_project_name() - version_doc = get_version_by_id(project_name, representation["parent"]) + project_name = context["project"]["name"] + version_doc = context["version"] + repre_doc = context["representation"] # get corresponding node group_node = container["node"] - file = get_representation_path(representation).replace("\\", "/") + file = get_representation_path(repre_doc).replace("\\", "/") name = container['name'] version_data = version_doc.get("data", {}) vname = version_doc.get("name", None) @@ -135,7 +136,7 @@ class LoadGizmoInputProcess(load.LoaderPlugin): "source", "author", "fps"] data_imprint = { - "representation": str(representation["_id"]), + "representation": str(repre_doc["_id"]), "frameStart": first, "frameEnd": last, "version": vname, @@ -254,8 +255,8 @@ class LoadGizmoInputProcess(load.LoaderPlugin): else: return input - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): node = container["node"] diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_image.py b/client/ayon_core/hosts/nuke/plugins/load/load_image.py index e9435ec10a..4f7a5ccc27 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_image.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_image.py @@ -155,10 +155,10 @@ class LoadImage(load.LoaderPlugin): loader=self.__class__.__name__, data=data_imprint) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) - def update(self, container, representation): + def update(self, container, context): """Update the Loader's path Nuke automatically tries to reset some variables when changing @@ -171,12 +171,16 @@ class LoadImage(load.LoaderPlugin): assert node.Class() == "Read", "Must be Read" - repr_cont = representation["context"] + project_name = context["project"]["name"] + version_doc = context["version"] + repre_doc = context["representation"] - file = get_representation_path(representation) + repr_cont = repre_doc["context"] + + file = get_representation_path(repre_doc) if not file: - repr_id = representation["_id"] + repr_id = repre_doc["_id"] self.log.warning( "Representation id `{}` is failing to load".format(repr_id)) return @@ -191,8 +195,6 @@ class LoadImage(load.LoaderPlugin): format(frame_number, "0{}".format(padding))) # Get start frame from version data - project_name = get_current_project_name() - version_doc = get_version_by_id(project_name, representation["parent"]) last_version_doc = get_last_version_by_subset_id( project_name, version_doc["parent"], fields=["_id"] ) @@ -210,7 +212,7 @@ class LoadImage(load.LoaderPlugin): updated_dict = {} updated_dict.update({ - "representation": str(representation["_id"]), + "representation": str(repre_doc["_id"]), "frameStart": str(first), "frameEnd": str(last), "version": str(version_doc.get("name")), diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_model.py b/client/ayon_core/hosts/nuke/plugins/load/load_model.py index 125cb28e27..cd4b72df91 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_model.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_model.py @@ -96,7 +96,7 @@ class AlembicModelLoader(load.LoaderPlugin): loader=self.__class__.__name__, data=data_imprint) - def update(self, container, representation): + def update(self, container, context): """ Called by Scene Inventory when look should be updated to current version. @@ -106,15 +106,15 @@ class AlembicModelLoader(load.LoaderPlugin): Args: container: object that has look to be updated - representation: (dict): relationship data to get proper + context: (dict): relationship data to get proper representation from DB and persisted data in .json Returns: None """ # Get version from io - project_name = get_current_project_name() - version_doc = get_version_by_id(project_name, representation["parent"]) + version_doc = context["version"] + repre_doc = context["representation"] # get corresponding node model_node = container["node"] @@ -131,7 +131,7 @@ class AlembicModelLoader(load.LoaderPlugin): add_keys = ["source", "author", "fps"] data_imprint = { - "representation": str(representation["_id"]), + "representation": str(repre_doc["_id"]), "frameStart": first, "frameEnd": last, "version": vname @@ -141,7 +141,7 @@ class AlembicModelLoader(load.LoaderPlugin): data_imprint.update({k: version_data[k]}) # getting file path - file = get_representation_path(representation).replace("\\", "/") + file = get_representation_path(repre_doc).replace("\\", "/") with maintained_selection(): model_node['selected'].setValue(True) @@ -202,8 +202,8 @@ class AlembicModelLoader(load.LoaderPlugin): color_value = "0xd88467ff" node["tile_color"].setValue(int(color_value, 16)) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): node = nuke.toNode(container['objectName']) diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_ociolook.py b/client/ayon_core/hosts/nuke/plugins/load/load_ociolook.py index e168c2bac1..e2e7cd3262 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_ociolook.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_ociolook.py @@ -219,14 +219,13 @@ class LoadOcioLookNodes(load.LoaderPlugin): return group_node - def update(self, container, representation): - - project_name = get_current_project_name() - version_doc = get_version_by_id(project_name, representation["parent"]) + def update(self, container, context): + version_doc = context["version"] + repre_doc = context["representation"] group_node = container["node"] - filepath = get_representation_path(representation) + filepath = get_representation_path(repre_doc) json_f = self._load_json_data(filepath) @@ -242,7 +241,7 @@ class LoadOcioLookNodes(load.LoaderPlugin): group_node["name"].value())) return update_container( - group_node, {"representation": str(representation["_id"])}) + group_node, {"representation": str(repre_doc["_id"])}) def _load_json_data(self, filepath): # getting data from json file with unicode conversion @@ -280,8 +279,8 @@ class LoadOcioLookNodes(load.LoaderPlugin): else: return input - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): node = nuke.toNode(container['objectName']) diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_script_precomp.py b/client/ayon_core/hosts/nuke/plugins/load/load_script_precomp.py index 1c91e51a09..5d62a7ca0f 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_script_precomp.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_script_precomp.py @@ -104,10 +104,10 @@ class LinkAsGroup(load.LoaderPlugin): loader=self.__class__.__name__, data=data_imprint) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) - def update(self, container, representation): + def update(self, container, context): """Update the Loader's path Nuke automatically tries to reset some variables when changing @@ -117,11 +117,13 @@ class LinkAsGroup(load.LoaderPlugin): """ node = container["node"] - root = get_representation_path(representation).replace("\\", "/") + project_name = context["project"]["name"] + version_doc = context["version"] + repre_doc = context["representation"] + + root = get_representation_path(repre_doc).replace("\\", "/") # Get start frame from version data - project_name = get_current_project_name() - version_doc = get_version_by_id(project_name, representation["parent"]) last_version_doc = get_last_version_by_subset_id( project_name, version_doc["parent"], fields=["_id"] ) @@ -129,7 +131,7 @@ class LinkAsGroup(load.LoaderPlugin): updated_dict = {} version_data = version_doc["data"] updated_dict.update({ - "representation": str(representation["_id"]), + "representation": str(repre_doc["_id"]), "frameEnd": version_data.get("frameEnd"), "version": version_doc.get("name"), "colorspace": version_data.get("colorspace"), From 1db78bafd31afcc818616ee718c071c8aa4c876c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 17:55:44 +0100 Subject: [PATCH 310/573] aftereffects load plugins are expecting representation context --- .../plugins/load/load_background.py | 25 +++++++++++-------- .../aftereffects/plugins/load/load_file.py | 23 ++++++++++------- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/client/ayon_core/hosts/aftereffects/plugins/load/load_background.py b/client/ayon_core/hosts/aftereffects/plugins/load/load_background.py index f23d7ec0bd..b834875e89 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/load/load_background.py +++ b/client/ayon_core/hosts/aftereffects/plugins/load/load_background.py @@ -11,7 +11,7 @@ from ayon_core.hosts.aftereffects.api.lib import ( class BackgroundLoader(api.AfterEffectsLoader): """ - Load images from Background family + Load images from Background product type Creates for each background separate folder with all imported images from background json AND automatically created composition with layers, each layer for separate image. @@ -56,16 +56,21 @@ class BackgroundLoader(api.AfterEffectsLoader): self.__class__.__name__ ) - def update(self, container, representation): + def update(self, container, context): """ Switch asset or change version """ stub = self.get_stub() - context = representation.get("context", {}) + asset_doc = context["asset"] + subset_doc = context["subset"] + repre_doc = context["representation"] + + folder_name = asset_doc["name"] + product_name = subset_doc["name"] _ = container.pop("layer") # without iterator number (_001, 002...) namespace_from_container = re.sub(r'_\d{3}$', '', container["namespace"]) - comp_name = "{}_{}".format(context["asset"], context["subset"]) + comp_name = "{}_{}".format(folder_name, product_name) # switching assets if namespace_from_container != comp_name: @@ -73,11 +78,11 @@ class BackgroundLoader(api.AfterEffectsLoader): existing_items = [layer.name for layer in items] comp_name = get_unique_layer_name( existing_items, - "{}_{}".format(context["asset"], context["subset"])) + "{}_{}".format(folder_name, product_name)) else: # switching version - keep same name comp_name = container["namespace"] - path = get_representation_path(representation) + path = get_representation_path(repre_doc) layers = get_background_layers(path) comp = stub.reload_background(container["members"][1], @@ -85,8 +90,8 @@ class BackgroundLoader(api.AfterEffectsLoader): layers) # update container - container["representation"] = str(representation["_id"]) - container["name"] = context["subset"] + container["representation"] = str(repre_doc["_id"]) + container["name"] = product_name container["namespace"] = comp_name container["members"] = comp.members @@ -104,5 +109,5 @@ class BackgroundLoader(api.AfterEffectsLoader): stub.imprint(layer.id, {}) stub.delete_item(layer.id) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) diff --git a/client/ayon_core/hosts/aftereffects/plugins/load/load_file.py b/client/ayon_core/hosts/aftereffects/plugins/load/load_file.py index a8e67e9f88..bceea66e8e 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/load/load_file.py +++ b/client/ayon_core/hosts/aftereffects/plugins/load/load_file.py @@ -64,31 +64,36 @@ class FileLoader(api.AfterEffectsLoader): self.__class__.__name__ ) - def update(self, container, representation): + def update(self, container, context): """ Switch asset or change version """ stub = self.get_stub() layer = container.pop("layer") - context = representation.get("context", {}) + asset_doc = context["asset"] + subset_doc = context["subset"] + repre_doc = context["representation"] + + folder_name = asset_doc["name"] + product_name = subset_doc["name"] namespace_from_container = re.sub(r'_\d{3}$', '', container["namespace"]) - layer_name = "{}_{}".format(context["asset"], context["subset"]) + layer_name = "{}_{}".format(folder_name, product_name) # switching assets if namespace_from_container != layer_name: layers = stub.get_items(comps=True) existing_layers = [layer.name for layer in layers] layer_name = get_unique_layer_name( existing_layers, - "{}_{}".format(context["asset"], context["subset"])) + "{}_{}".format(folder_name, product_name)) else: # switching version - keep same name layer_name = container["namespace"] - path = get_representation_path(representation) + path = get_representation_path(repre_doc) # with aftereffects.maintained_selection(): # TODO stub.replace_item(layer.id, path, stub.LOADED_ICON + layer_name) stub.imprint( - layer.id, {"representation": str(representation["_id"]), - "name": context["subset"], + layer.id, {"representation": str(repre_doc["_id"]), + "name": product_name, "namespace": layer_name} ) @@ -103,5 +108,5 @@ class FileLoader(api.AfterEffectsLoader): stub.imprint(layer.id, {}) stub.delete_item(layer.id) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) From cd74ae7d88b85584e700d5a9ead35ae746ee6bcf Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 17:56:25 +0100 Subject: [PATCH 311/573] blender load plugins are expecting representation context --- client/ayon_core/hosts/blender/api/plugin.py | 6 +++--- .../ayon_core/hosts/blender/plugins/load/load_abc.py | 9 +++++---- .../hosts/blender/plugins/load/load_action.py | 10 +++++----- .../hosts/blender/plugins/load/load_audio.py | 11 ++++++----- .../hosts/blender/plugins/load/load_blend.py | 9 +++++---- .../hosts/blender/plugins/load/load_blendscene.py | 9 +++++---- .../hosts/blender/plugins/load/load_camera_abc.py | 9 +++++---- .../hosts/blender/plugins/load/load_camera_fbx.py | 9 +++++---- .../ayon_core/hosts/blender/plugins/load/load_fbx.py | 9 +++++---- .../hosts/blender/plugins/load/load_layout_json.py | 9 +++++---- .../ayon_core/hosts/blender/plugins/load/load_look.py | 9 +++++---- 11 files changed, 54 insertions(+), 45 deletions(-) diff --git a/client/ayon_core/hosts/blender/api/plugin.py b/client/ayon_core/hosts/blender/api/plugin.py index d72754f148..8d72c488e5 100644 --- a/client/ayon_core/hosts/blender/api/plugin.py +++ b/client/ayon_core/hosts/blender/api/plugin.py @@ -507,13 +507,13 @@ class AssetLoader(LoaderPlugin): # return self._get_instance_collection(instance_name, nodes) - def exec_update(self, container: Dict, representation: Dict): + def exec_update(self, container: Dict, context: Dict): """Must be implemented by a sub-class""" raise NotImplementedError("Must be implemented by a sub-class") - def update(self, container: Dict, representation: Dict): + def update(self, container: Dict, context: Dict): """ Run the update on Blender main thread""" - mti = MainThreadItem(self.exec_update, container, representation) + mti = MainThreadItem(self.exec_update, container, context) execute_in_main_thread(mti) def exec_remove(self, container: Dict) -> bool: diff --git a/client/ayon_core/hosts/blender/plugins/load/load_abc.py b/client/ayon_core/hosts/blender/plugins/load/load_abc.py index b25f4eb277..23c919ac59 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_abc.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_abc.py @@ -176,7 +176,7 @@ class CacheModelLoader(plugin.AssetLoader): self[:] = objects return objects - def exec_update(self, container: Dict, representation: Dict): + def exec_update(self, container: Dict, context: Dict): """Update the loaded asset. This will remove all objects of the current collection, load the new @@ -188,15 +188,16 @@ class CacheModelLoader(plugin.AssetLoader): Warning: No nested collections are supported at the moment! """ + repre_doc = context["representation"] object_name = container["objectName"] asset_group = bpy.data.objects.get(object_name) - libpath = Path(get_representation_path(representation)) + libpath = Path(get_representation_path(repre_doc)) extension = libpath.suffix.lower() self.log.info( "Container: %s\nRepresentation: %s", pformat(container, indent=2), - pformat(representation, indent=2), + pformat(repre_doc, indent=2), ) assert asset_group, ( @@ -241,7 +242,7 @@ class CacheModelLoader(plugin.AssetLoader): asset_group.matrix_basis = mat metadata["libpath"] = str(libpath) - metadata["representation"] = str(representation["_id"]) + metadata["representation"] = str(repre_doc["_id"]) def exec_remove(self, container: Dict) -> bool: """Remove an existing container from a Blender scene. diff --git a/client/ayon_core/hosts/blender/plugins/load/load_action.py b/client/ayon_core/hosts/blender/plugins/load/load_action.py index 5c8ba0df44..9afcefbab5 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_action.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_action.py @@ -114,7 +114,7 @@ class BlendActionLoader(plugin.AssetLoader): self[:] = nodes return nodes - def update(self, container: Dict, representation: Dict): + def update(self, container: Dict, context: Dict): """Update the loaded asset. This will remove all objects of the current collection, load the new @@ -126,18 +126,18 @@ class BlendActionLoader(plugin.AssetLoader): Warning: No nested collections are supported at the moment! """ - + repre_doc = context["representation"] collection = bpy.data.collections.get( container["objectName"] ) - libpath = Path(get_representation_path(representation)) + libpath = Path(get_representation_path(repre_doc)) extension = libpath.suffix.lower() logger.info( "Container: %s\nRepresentation: %s", pformat(container, indent=2), - pformat(representation, indent=2), + pformat(repre_doc, indent=2), ) assert collection, ( @@ -241,7 +241,7 @@ class BlendActionLoader(plugin.AssetLoader): # Save the list of objects in the metadata container collection_metadata["objects"] = objects_list collection_metadata["libpath"] = str(libpath) - collection_metadata["representation"] = str(representation["_id"]) + collection_metadata["representation"] = str(repre_doc["_id"]) bpy.ops.object.select_all(action='DESELECT') diff --git a/client/ayon_core/hosts/blender/plugins/load/load_audio.py b/client/ayon_core/hosts/blender/plugins/load/load_audio.py index 007889f6f6..62b0586eb2 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_audio.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_audio.py @@ -94,7 +94,7 @@ class AudioLoader(plugin.AssetLoader): self[:] = objects return [objects] - def exec_update(self, container: Dict, representation: Dict): + def exec_update(self, container: Dict, context: Dict): """Update an audio strip in the sequence editor. Arguments: @@ -103,14 +103,15 @@ class AudioLoader(plugin.AssetLoader): representation (openpype:representation-1.0): Representation to update, from `host.ls()`. """ + repre_doc = context["representation"] object_name = container["objectName"] asset_group = bpy.data.objects.get(object_name) - libpath = Path(get_representation_path(representation)) + libpath = Path(get_representation_path(repre_doc)) self.log.info( "Container: %s\nRepresentation: %s", pformat(container, indent=2), - pformat(representation, indent=2), + pformat(repre_doc, indent=2), ) assert asset_group, ( @@ -173,8 +174,8 @@ class AudioLoader(plugin.AssetLoader): window_manager.windows[-1].screen.areas[0].type = old_type metadata["libpath"] = str(libpath) - metadata["representation"] = str(representation["_id"]) - metadata["parent"] = str(representation["parent"]) + metadata["representation"] = str(repre_doc["_id"]) + metadata["parent"] = str(repre_doc["parent"]) metadata["audio"] = new_audio def exec_remove(self, container: Dict) -> bool: diff --git a/client/ayon_core/hosts/blender/plugins/load/load_blend.py b/client/ayon_core/hosts/blender/plugins/load/load_blend.py index c9862f9841..cec800a557 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_blend.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_blend.py @@ -179,13 +179,14 @@ class BlendLoader(plugin.AssetLoader): self[:] = objects return objects - def exec_update(self, container: Dict, representation: Dict): + def exec_update(self, container: Dict, context: Dict): """ Update the loaded asset. """ + repre_doc = context["representation"] group_name = container["objectName"] asset_group = bpy.data.objects.get(group_name) - libpath = Path(get_representation_path(representation)).as_posix() + libpath = Path(get_representation_path(repre_doc)).as_posix() assert asset_group, ( f"The asset is not loaded: {container['objectName']}" @@ -232,8 +233,8 @@ class BlendLoader(plugin.AssetLoader): new_data = { "libpath": libpath, - "representation": str(representation["_id"]), - "parent": str(representation["parent"]), + "representation": str(repre_doc["_id"]), + "parent": str(repre_doc["parent"]), "members": members, } diff --git a/client/ayon_core/hosts/blender/plugins/load/load_blendscene.py b/client/ayon_core/hosts/blender/plugins/load/load_blendscene.py index 248bf5a901..d9491c0749 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_blendscene.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_blendscene.py @@ -129,13 +129,14 @@ class BlendSceneLoader(plugin.AssetLoader): self[:] = objects return objects - def exec_update(self, container: Dict, representation: Dict): + def exec_update(self, container: Dict, context: Dict): """ Update the loaded asset. """ + repre_doc = context["representation"] group_name = container["objectName"] asset_group = bpy.data.collections.get(group_name) - libpath = Path(get_representation_path(representation)).as_posix() + libpath = Path(get_representation_path(repre_doc)).as_posix() assert asset_group, ( f"The asset is not loaded: {container['objectName']}" @@ -193,8 +194,8 @@ class BlendSceneLoader(plugin.AssetLoader): new_data = { "libpath": libpath, - "representation": str(representation["_id"]), - "parent": str(representation["parent"]), + "representation": str(repre_doc["_id"]), + "parent": str(repre_doc["parent"]), "members": members, } diff --git a/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py b/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py index 8f0bd6741d..0a111b54d0 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py @@ -128,7 +128,7 @@ class AbcCameraLoader(plugin.AssetLoader): self[:] = objects return objects - def exec_update(self, container: Dict, representation: Dict): + def exec_update(self, container: Dict, context: Dict): """Update the loaded asset. This will remove all objects of the current collection, load the new @@ -140,15 +140,16 @@ class AbcCameraLoader(plugin.AssetLoader): Warning: No nested collections are supported at the moment! """ + repre_doc = context["representation"] object_name = container["objectName"] asset_group = bpy.data.objects.get(object_name) - libpath = Path(get_representation_path(representation)) + libpath = Path(get_representation_path(repre_doc)) extension = libpath.suffix.lower() self.log.info( "Container: %s\nRepresentation: %s", pformat(container, indent=2), - pformat(representation, indent=2), + pformat(repre_doc, indent=2), ) assert asset_group, ( @@ -183,7 +184,7 @@ class AbcCameraLoader(plugin.AssetLoader): asset_group.matrix_basis = mat metadata["libpath"] = str(libpath) - metadata["representation"] = str(representation["_id"]) + metadata["representation"] = str(repre_doc["_id"]) def exec_remove(self, container: Dict) -> bool: """Remove an existing container from a Blender scene. diff --git a/client/ayon_core/hosts/blender/plugins/load/load_camera_fbx.py b/client/ayon_core/hosts/blender/plugins/load/load_camera_fbx.py index 7642871dc7..4c512cbeaa 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_camera_fbx.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_camera_fbx.py @@ -131,7 +131,7 @@ class FbxCameraLoader(plugin.AssetLoader): self[:] = objects return objects - def exec_update(self, container: Dict, representation: Dict): + def exec_update(self, container: Dict, context: Dict): """Update the loaded asset. This will remove all objects of the current collection, load the new @@ -143,15 +143,16 @@ class FbxCameraLoader(plugin.AssetLoader): Warning: No nested collections are supported at the moment! """ + repre_doc = context["representation"] object_name = container["objectName"] asset_group = bpy.data.objects.get(object_name) - libpath = Path(get_representation_path(representation)) + libpath = Path(get_representation_path(repre_doc)) extension = libpath.suffix.lower() self.log.info( "Container: %s\nRepresentation: %s", pformat(container, indent=2), - pformat(representation, indent=2), + pformat(repre_doc, indent=2), ) assert asset_group, ( @@ -193,7 +194,7 @@ class FbxCameraLoader(plugin.AssetLoader): asset_group.matrix_basis = mat metadata["libpath"] = str(libpath) - metadata["representation"] = str(representation["_id"]) + metadata["representation"] = str(repre_doc["_id"]) def exec_remove(self, container: Dict) -> bool: """Remove an existing container from a Blender scene. diff --git a/client/ayon_core/hosts/blender/plugins/load/load_fbx.py b/client/ayon_core/hosts/blender/plugins/load/load_fbx.py index 03993c9f5e..977e3db970 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_fbx.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_fbx.py @@ -175,7 +175,7 @@ class FbxModelLoader(plugin.AssetLoader): self[:] = objects return objects - def exec_update(self, container: Dict, representation: Dict): + def exec_update(self, container: Dict, context: Dict): """Update the loaded asset. This will remove all objects of the current collection, load the new @@ -187,15 +187,16 @@ class FbxModelLoader(plugin.AssetLoader): Warning: No nested collections are supported at the moment! """ + repre_doc = context["representation"] object_name = container["objectName"] asset_group = bpy.data.objects.get(object_name) - libpath = Path(get_representation_path(representation)) + libpath = Path(get_representation_path(repre_doc)) extension = libpath.suffix.lower() self.log.info( "Container: %s\nRepresentation: %s", pformat(container, indent=2), - pformat(representation, indent=2), + pformat(repre_doc, indent=2), ) assert asset_group, ( @@ -248,7 +249,7 @@ class FbxModelLoader(plugin.AssetLoader): asset_group.matrix_basis = mat metadata["libpath"] = str(libpath) - metadata["representation"] = str(representation["_id"]) + metadata["representation"] = str(repre_doc["_id"]) def exec_remove(self, container: Dict) -> bool: """Remove an existing container from a Blender scene. diff --git a/client/ayon_core/hosts/blender/plugins/load/load_layout_json.py b/client/ayon_core/hosts/blender/plugins/load/load_layout_json.py index f48862a803..740dd9dd53 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_layout_json.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_layout_json.py @@ -184,7 +184,7 @@ class JsonLayoutLoader(plugin.AssetLoader): self[:] = asset_group.children return asset_group.children - def exec_update(self, container: Dict, representation: Dict): + def exec_update(self, container: Dict, context: Dict): """Update the loaded asset. This will remove all objects of the current collection, load the new @@ -193,15 +193,16 @@ class JsonLayoutLoader(plugin.AssetLoader): will not be removed, only unlinked. Normally this should not be the case though. """ + repre_doc = context["representation"] object_name = container["objectName"] asset_group = bpy.data.objects.get(object_name) - libpath = Path(get_representation_path(representation)) + libpath = Path(get_representation_path(repre_doc)) extension = libpath.suffix.lower() self.log.info( "Container: %s\nRepresentation: %s", pformat(container, indent=2), - pformat(representation, indent=2), + pformat(repre_doc, indent=2), ) assert asset_group, ( @@ -262,7 +263,7 @@ class JsonLayoutLoader(plugin.AssetLoader): asset_group.matrix_basis = mat metadata["libpath"] = str(libpath) - metadata["representation"] = str(representation["_id"]) + metadata["representation"] = str(repre_doc["_id"]) def exec_remove(self, container: Dict) -> bool: """Remove an existing container from a Blender scene. diff --git a/client/ayon_core/hosts/blender/plugins/load/load_look.py b/client/ayon_core/hosts/blender/plugins/load/load_look.py index f9ebf98912..507c940846 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_look.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_look.py @@ -138,15 +138,16 @@ class BlendLookLoader(plugin.AssetLoader): self[:] = nodes return nodes - def update(self, container: Dict, representation: Dict): + def update(self, container: Dict, context: Dict): collection = bpy.data.collections.get(container["objectName"]) - libpath = Path(get_representation_path(representation)) + repre_doc = context["representation"] + libpath = Path(get_representation_path(repre_doc)) extension = libpath.suffix.lower() self.log.info( "Container: %s\nRepresentation: %s", pformat(container, indent=2), - pformat(representation, indent=2), + pformat(repre_doc, indent=2), ) assert collection, ( @@ -201,7 +202,7 @@ class BlendLookLoader(plugin.AssetLoader): collection_metadata["objects"] = objects collection_metadata["materials"] = materials collection_metadata["libpath"] = str(libpath) - collection_metadata["representation"] = str(representation["_id"]) + collection_metadata["representation"] = str(repre_doc["_id"]) def remove(self, container: Dict) -> bool: collection = bpy.data.collections.get(container["objectName"]) From 00bfee8289a36f414a4f4897f4d53bbfc70ce876 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 17:56:31 +0100 Subject: [PATCH 312/573] flame load plugins are expecting representation context --- .../hosts/flame/plugins/load/load_clip.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/client/ayon_core/hosts/flame/plugins/load/load_clip.py b/client/ayon_core/hosts/flame/plugins/load/load_clip.py index 47d0331255..72a6f2a585 100644 --- a/client/ayon_core/hosts/flame/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/flame/plugins/load/load_clip.py @@ -11,7 +11,7 @@ from ayon_core.lib.transcoding import ( class LoadClip(opfapi.ClipLoader): - """Load a subset to timeline as clip + """Load a product to timeline as clip Place clip to timeline on its asset origin timings collected during conforming to project @@ -31,14 +31,14 @@ class LoadClip(opfapi.ClipLoader): # settings reel_group_name = "OpenPype_Reels" reel_name = "Loaded" - clip_name_template = "{asset}_{subset}<_{output}>" + clip_name_template = "{folder[name]}_{product[name]}<_{output}>" """ Anatomy keys from version context data and dynamically added: - {layerName} - original layer name token - {layerUID} - original layer UID token - {originalBasename} - original clip name taken from file """ - layer_rename_template = "{asset}_{subset}<_{output}>" + layer_rename_template = "{folder[name]}_{product[name]}<_{output}>" layer_rename_patterns = [] def load(self, context, name, namespace, options): @@ -180,27 +180,27 @@ class LoadClip(opfapi.ClipLoader): # unwrapping segment from input clip pass - # def switch(self, container, representation): - # self.update(container, representation) + # def switch(self, container, context): + # self.update(container, context) - # def update(self, container, representation): + # def update(self, container, context): # """ Updating previously loaded clips # """ - # # load clip to timeline and get main variables + # repre_doc = context['representation'] # name = container['name'] # namespace = container['namespace'] # track_item = phiero.get_track_items( # track_item_name=namespace) # version = io.find_one({ # "type": "version", - # "_id": representation["parent"] + # "_id": repre_doc["parent"] # }) # version_data = version.get("data", {}) # version_name = version.get("name", None) # colorspace = version_data.get("colorspace", None) # object_name = "{}_{}".format(name, namespace) - # file = get_representation_path(representation).replace("\\", "/") + # file = get_representation_path(repre_doc).replace("\\", "/") # clip = track_item.source() # # reconnect media to new path @@ -225,7 +225,7 @@ class LoadClip(opfapi.ClipLoader): # # add variables related to version context # data_imprint.update({ - # "representation": str(representation["_id"]), + # "representation": str(repre_doc["_id"]), # "version": version_name, # "colorspace": colorspace, # "objectName": object_name From 63acc96fd0579762ab1bb4635a19e6ad39d34117 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 17:56:43 +0100 Subject: [PATCH 313/573] fusion load plugins are expecting representation context --- .../hosts/fusion/plugins/load/load_alembic.py | 11 ++++++----- .../ayon_core/hosts/fusion/plugins/load/load_fbx.py | 11 ++++++----- .../hosts/fusion/plugins/load/load_sequence.py | 11 ++++++----- .../ayon_core/hosts/fusion/plugins/load/load_usd.py | 11 ++++++----- 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/client/ayon_core/hosts/fusion/plugins/load/load_alembic.py b/client/ayon_core/hosts/fusion/plugins/load/load_alembic.py index 0bc7ffd180..17f043bb34 100644 --- a/client/ayon_core/hosts/fusion/plugins/load/load_alembic.py +++ b/client/ayon_core/hosts/fusion/plugins/load/load_alembic.py @@ -44,23 +44,24 @@ class FusionLoadAlembicMesh(load.LoaderPlugin): context=context, loader=self.__class__.__name__) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) - def update(self, container, representation): + def update(self, container, context): """Update Alembic path""" tool = container["_tool"] assert tool.ID == self.tool_type, f"Must be {self.tool_type}" comp = tool.Comp() - path = get_representation_path(representation) + repre_doc = context["representation"] + path = get_representation_path(repre_doc) with comp_lock_and_undo_chunk(comp, "Update tool"): tool["Filename"] = path # Update the imprinted representation - tool.SetData("avalon.representation", str(representation["_id"])) + tool.SetData("avalon.representation", str(repre_doc["_id"])) def remove(self, container): tool = container["_tool"] diff --git a/client/ayon_core/hosts/fusion/plugins/load/load_fbx.py b/client/ayon_core/hosts/fusion/plugins/load/load_fbx.py index 3751d7cc39..75320431a8 100644 --- a/client/ayon_core/hosts/fusion/plugins/load/load_fbx.py +++ b/client/ayon_core/hosts/fusion/plugins/load/load_fbx.py @@ -59,23 +59,24 @@ class FusionLoadFBXMesh(load.LoaderPlugin): loader=self.__class__.__name__, ) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) - def update(self, container, representation): + def update(self, container, context): """Update path""" tool = container["_tool"] assert tool.ID == self.tool_type, f"Must be {self.tool_type}" comp = tool.Comp() - path = get_representation_path(representation) + repre_doc = context["representation"] + path = get_representation_path(repre_doc) with comp_lock_and_undo_chunk(comp, "Update tool"): tool["ImportFile"] = path # Update the imprinted representation - tool.SetData("avalon.representation", str(representation["_id"])) + tool.SetData("avalon.representation", str(repre_doc["_id"])) def remove(self, container): tool = container["_tool"] diff --git a/client/ayon_core/hosts/fusion/plugins/load/load_sequence.py b/client/ayon_core/hosts/fusion/plugins/load/load_sequence.py index 5c183f5159..753d50ee34 100644 --- a/client/ayon_core/hosts/fusion/plugins/load/load_sequence.py +++ b/client/ayon_core/hosts/fusion/plugins/load/load_sequence.py @@ -175,10 +175,10 @@ class FusionLoadSequence(load.LoaderPlugin): loader=self.__class__.__name__, ) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) - def update(self, container, representation): + def update(self, container, context): """Update the Loader's path Fusion automatically tries to reset some variables when changing @@ -224,7 +224,8 @@ class FusionLoadSequence(load.LoaderPlugin): assert tool.ID == "Loader", "Must be Loader" comp = tool.Comp() - context = get_representation_context(representation) + repre_doc = context["representation"] + context = get_representation_context(repre_doc) path = self.filepath_from_context(context) # Get start frame from version data @@ -255,7 +256,7 @@ class FusionLoadSequence(load.LoaderPlugin): ) # Update the imprinted representation - tool.SetData("avalon.representation", str(representation["_id"])) + tool.SetData("avalon.representation", str(repre_doc["_id"])) def remove(self, container): tool = container["_tool"] diff --git a/client/ayon_core/hosts/fusion/plugins/load/load_usd.py b/client/ayon_core/hosts/fusion/plugins/load/load_usd.py index 9c61894d66..a5630fac1b 100644 --- a/client/ayon_core/hosts/fusion/plugins/load/load_usd.py +++ b/client/ayon_core/hosts/fusion/plugins/load/load_usd.py @@ -61,22 +61,23 @@ class FusionLoadUSD(load.LoaderPlugin): context=context, loader=self.__class__.__name__) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) - def update(self, container, representation): + def update(self, container, context): tool = container["_tool"] assert tool.ID == self.tool_type, f"Must be {self.tool_type}" comp = tool.Comp() - path = get_representation_path(representation) + repre_doc = context["representation"] + path = get_representation_path(repre_doc) with comp_lock_and_undo_chunk(comp, "Update tool"): tool["Filename"] = path # Update the imprinted representation - tool.SetData("avalon.representation", str(representation["_id"])) + tool.SetData("avalon.representation", str(repre_doc["_id"])) def remove(self, container): tool = container["_tool"] From 1bd67ab083702a5a0c4051abc11057e2a74b2763 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 17:56:50 +0100 Subject: [PATCH 314/573] harmony load plugins are expecting representation context --- client/ayon_core/hosts/harmony/api/README.md | 15 ++++++------ .../hosts/harmony/plugins/load/load_audio.py | 8 +++---- .../harmony/plugins/load/load_background.py | 24 +++++++++++-------- .../plugins/load/load_imagesequence.py | 21 ++++++++-------- .../harmony/plugins/load/load_palette.py | 23 ++++++++++-------- .../harmony/plugins/load/load_template.py | 13 +++++----- .../plugins/load/load_template_workfile.py | 8 +++---- 7 files changed, 61 insertions(+), 51 deletions(-) diff --git a/client/ayon_core/hosts/harmony/api/README.md b/client/ayon_core/hosts/harmony/api/README.md index 457e22fb2e..151b2bce9e 100644 --- a/client/ayon_core/hosts/harmony/api/README.md +++ b/client/ayon_core/hosts/harmony/api/README.md @@ -204,7 +204,7 @@ class CreateComposite(harmony.Creator): name = "compositeDefault" label = "Composite" - family = "mindbender.template" + product_type = "mindbender.template" def __init__(self, *args, **kwargs): super(CreateComposite, self).__init__(*args, **kwargs) @@ -221,7 +221,7 @@ class CreateRender(harmony.Creator): name = "writeDefault" label = "Write" - family = "mindbender.imagesequence" + product_type = "mindbender.imagesequence" node_type = "WRITE" def __init__(self, *args, **kwargs): @@ -611,11 +611,12 @@ class ImageSequenceLoader(load.LoaderPlugin): self.__class__.__name__ ) - def update(self, container, representation): + def update(self, container, context): node = container.pop("node") + repre_doc = context["representation"] project_name = get_current_project_name() - version = get_version_by_id(project_name, representation["parent"]) + version = get_version_by_id(project_name, repre_doc["parent"]) files = [] for f in version["data"]["files"]: files.append( @@ -632,7 +633,7 @@ class ImageSequenceLoader(load.LoaderPlugin): ) harmony.imprint( - node, {"representation": str(representation["_id"])} + node, {"representation": str(repre_doc["_id"])} ) def remove(self, container): @@ -648,8 +649,8 @@ class ImageSequenceLoader(load.LoaderPlugin): {"function": func, "args": [node]} ) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) ``` ## Resources diff --git a/client/ayon_core/hosts/harmony/plugins/load/load_audio.py b/client/ayon_core/hosts/harmony/plugins/load/load_audio.py index 14389166d7..b73c82197a 100644 --- a/client/ayon_core/hosts/harmony/plugins/load/load_audio.py +++ b/client/ayon_core/hosts/harmony/plugins/load/load_audio.py @@ -45,17 +45,17 @@ class ImportAudioLoader(load.LoaderPlugin): {"function": func, "args": [context["subset"]["name"], wav_file]} ) - subset_name = context["subset"]["name"] + product_name = context["subset"]["name"] return harmony.containerise( - subset_name, + product_name, namespace, - subset_name, + product_name, context, self.__class__.__name__ ) - def update(self, container, representation): + def update(self, container, context): pass def remove(self, container): diff --git a/client/ayon_core/hosts/harmony/plugins/load/load_background.py b/client/ayon_core/hosts/harmony/plugins/load/load_background.py index 1c61cfa7a4..bf454a9ec7 100644 --- a/client/ayon_core/hosts/harmony/plugins/load/load_background.py +++ b/client/ayon_core/hosts/harmony/plugins/load/load_background.py @@ -254,7 +254,7 @@ class BackgroundLoader(load.LoaderPlugin): bg_folder = os.path.dirname(path) - subset_name = context["subset"]["name"] + product_name = context["subset"]["name"] # read_node_name += "_{}".format(uuid.uuid4()) container_nodes = [] @@ -272,16 +272,17 @@ class BackgroundLoader(load.LoaderPlugin): container_nodes.append(read_node) return harmony.containerise( - subset_name, + product_name, namespace, - subset_name, + product_name, context, self.__class__.__name__, nodes=container_nodes ) - def update(self, container, representation): - path = get_representation_path(representation) + def update(self, container, context): + repre_doc = context["representation"] + path = get_representation_path(repre_doc) with open(path) as json_file: data = json.load(json_file) @@ -301,7 +302,7 @@ class BackgroundLoader(load.LoaderPlugin): print(container) - is_latest = is_representation_from_latest(representation) + is_latest = is_representation_from_latest(repre_doc) for layer in sorted(layers): file_to_import = [ os.path.join(bg_folder, layer).replace("\\", "/") @@ -351,8 +352,11 @@ class BackgroundLoader(load.LoaderPlugin): harmony.send({"function": func, "args": [node, "red"]}) harmony.imprint( - container['name'], {"representation": str(representation["_id"]), - "nodes": container['nodes']} + container['name'], + { + "representation": str(repre_doc["_id"]), + "nodes": container["nodes"] + } ) def remove(self, container): @@ -369,5 +373,5 @@ class BackgroundLoader(load.LoaderPlugin): ) harmony.imprint(container['name'], {}, remove=True) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) diff --git a/client/ayon_core/hosts/harmony/plugins/load/load_imagesequence.py b/client/ayon_core/hosts/harmony/plugins/load/load_imagesequence.py index 4d87272de8..60b90fe42d 100644 --- a/client/ayon_core/hosts/harmony/plugins/load/load_imagesequence.py +++ b/client/ayon_core/hosts/harmony/plugins/load/load_imagesequence.py @@ -47,7 +47,7 @@ class ImageSequenceLoader(load.LoaderPlugin): files.append(fname.parent.joinpath(remainder[0]).as_posix()) asset = context["asset"]["name"] - subset = context["subset"]["name"] + product_name = context["subset"]["name"] group_id = str(uuid.uuid4()) read_node = harmony.send( @@ -56,7 +56,7 @@ class ImageSequenceLoader(load.LoaderPlugin): "args": [ files, asset, - subset, + product_name, 1, group_id ] @@ -64,7 +64,7 @@ class ImageSequenceLoader(load.LoaderPlugin): )["result"] return harmony.containerise( - f"{asset}_{subset}", + f"{asset}_{product_name}", namespace, read_node, context, @@ -72,18 +72,19 @@ class ImageSequenceLoader(load.LoaderPlugin): nodes=[read_node] ) - def update(self, container, representation): + def update(self, container, context): """Update loaded containers. Args: container (dict): Container data. - representation (dict): Representation data. + context (dict): Representation context data. """ self_name = self.__class__.__name__ node = container.get("nodes").pop() - path = get_representation_path(representation) + repre_doc = context["representation"] + path = get_representation_path(repre_doc) collections, remainder = clique.assemble( os.listdir(os.path.dirname(path)) ) @@ -110,7 +111,7 @@ class ImageSequenceLoader(load.LoaderPlugin): ) # Colour node. - if is_representation_from_latest(representation): + if is_representation_from_latest(repre_doc): harmony.send( { "function": "PypeHarmony.setColor", @@ -124,7 +125,7 @@ class ImageSequenceLoader(load.LoaderPlugin): }) harmony.imprint( - node, {"representation": str(representation["_id"])} + node, {"representation": str(repre_doc["_id"])} ) def remove(self, container): @@ -140,6 +141,6 @@ class ImageSequenceLoader(load.LoaderPlugin): ) harmony.imprint(node, {}, remove=True) - def switch(self, container, representation): + def switch(self, container, context): """Switch loaded representations.""" - self.update(container, representation) + self.update(container, context) diff --git a/client/ayon_core/hosts/harmony/plugins/load/load_palette.py b/client/ayon_core/hosts/harmony/plugins/load/load_palette.py index aa5894e026..f9ce888f93 100644 --- a/client/ayon_core/hosts/harmony/plugins/load/load_palette.py +++ b/client/ayon_core/hosts/harmony/plugins/load/load_palette.py @@ -26,15 +26,17 @@ class ImportPaletteLoader(load.LoaderPlugin): self.__class__.__name__ ) - def load_palette(self, representation): - subset_name = representation["context"]["subset"] - name = subset_name.replace("palette", "") + def load_palette(self, context): + subset_doc = context["subset"] + repre_doc = context["representation"] + product_name = subset_doc["name"] + name = product_name.replace("palette", "") # Overwrite palette on disk. scene_path = harmony.send( {"function": "scene.currentProjectPath"} )["result"] - src = get_representation_path(representation) + src = get_representation_path(repre_doc) dst = os.path.join( scene_path, "palette-library", @@ -44,7 +46,7 @@ class ImportPaletteLoader(load.LoaderPlugin): harmony.save_scene() - msg = "Updated {}.".format(subset_name) + msg = "Updated {}.".format(product_name) msg += " You need to reload the scene to see the changes.\n" msg += "Please save workfile when ready and use Workfiles " msg += "to reopen it." @@ -59,13 +61,14 @@ class ImportPaletteLoader(load.LoaderPlugin): def remove(self, container): harmony.remove(container["name"]) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) - def update(self, container, representation): + def update(self, container, context): self.remove(container) - name = self.load_palette(representation) + name = self.load_palette(context) - container["representation"] = str(representation["_id"]) + repre_doc = context["representation"] + container["representation"] = str(repre_doc["_id"]) container["name"] = name harmony.imprint(name, container) diff --git a/client/ayon_core/hosts/harmony/plugins/load/load_template.py b/client/ayon_core/hosts/harmony/plugins/load/load_template.py index d26f148c09..e981340c68 100644 --- a/client/ayon_core/hosts/harmony/plugins/load/load_template.py +++ b/client/ayon_core/hosts/harmony/plugins/load/load_template.py @@ -70,19 +70,20 @@ class TemplateLoader(load.LoaderPlugin): self_name ) - def update(self, container, representation): + def update(self, container, context): """Update loaded containers. Args: container (dict): Container data. - representation (dict): Representation data. + context (dict): Representation context data. """ node_name = container["name"] node = harmony.find_node_by_name(node_name, "GROUP") self_name = self.__class__.__name__ - if is_representation_from_latest(representation): + repre_doc = context["representation"] + if is_representation_from_latest(repre_doc): self._set_green(node) else: self._set_red(node) @@ -110,7 +111,7 @@ class TemplateLoader(load.LoaderPlugin): None, container["data"]) harmony.imprint( - node, {"representation": str(representation["_id"])} + node, {"representation": str(repre_doc["_id"])} ) def remove(self, container): @@ -125,9 +126,9 @@ class TemplateLoader(load.LoaderPlugin): {"function": "PypeHarmony.deleteNode", "args": [node]} ) - def switch(self, container, representation): + def switch(self, container, context): """Switch representation containers.""" - self.update(container, representation) + self.update(container, context) def _set_green(self, node): """Set node color to green `rgba(0, 255, 0, 255)`.""" diff --git a/client/ayon_core/hosts/harmony/plugins/load/load_template_workfile.py b/client/ayon_core/hosts/harmony/plugins/load/load_template_workfile.py index 0ea46f8f67..1b127c5bc4 100644 --- a/client/ayon_core/hosts/harmony/plugins/load/load_template_workfile.py +++ b/client/ayon_core/hosts/harmony/plugins/load/load_template_workfile.py @@ -40,17 +40,17 @@ class ImportTemplateLoader(load.LoaderPlugin): shutil.rmtree(temp_dir) - subset_name = context["subset"]["name"] + product_name = context["subset"]["name"] return harmony.containerise( - subset_name, + product_name, namespace, - subset_name, + product_name, context, self.__class__.__name__ ) - def update(self, container, representation): + def update(self, container, context): pass def remove(self, container): From 548b7f27375493ed7629145bb5fde619b7ca9a99 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 17:56:56 +0100 Subject: [PATCH 315/573] maya load plugins are expecting representation context --- client/ayon_core/hosts/maya/api/plugin.py | 125 ++++++++++-------- client/ayon_core/hosts/maya/api/setdress.py | 26 +++- .../maya/plugins/load/load_arnold_standin.py | 11 +- .../hosts/maya/plugins/load/load_assembly.py | 4 +- .../hosts/maya/plugins/load/load_audio.py | 11 +- .../hosts/maya/plugins/load/load_gpucache.py | 11 +- .../hosts/maya/plugins/load/load_image.py | 10 +- .../maya/plugins/load/load_image_plane.py | 26 ++-- .../hosts/maya/plugins/load/load_look.py | 13 +- .../hosts/maya/plugins/load/load_maya_usd.py | 11 +- .../maya/plugins/load/load_multiverse_usd.py | 13 +- .../plugins/load/load_multiverse_usd_over.py | 13 +- .../maya/plugins/load/load_redshift_proxy.py | 12 +- .../hosts/maya/plugins/load/load_reference.py | 8 +- .../maya/plugins/load/load_rendersetup.py | 11 +- .../maya/plugins/load/load_vdb_to_arnold.py | 14 +- .../maya/plugins/load/load_vdb_to_redshift.py | 13 +- .../maya/plugins/load/load_vdb_to_vray.py | 11 +- .../hosts/maya/plugins/load/load_vrayproxy.py | 13 +- .../hosts/maya/plugins/load/load_vrayscene.py | 11 +- .../hosts/maya/plugins/load/load_xgen.py | 7 +- .../maya/plugins/load/load_yeti_cache.py | 12 +- 22 files changed, 207 insertions(+), 179 deletions(-) diff --git a/client/ayon_core/hosts/maya/api/plugin.py b/client/ayon_core/hosts/maya/api/plugin.py index 7a01f1a174..2772081f3c 100644 --- a/client/ayon_core/hosts/maya/api/plugin.py +++ b/client/ayon_core/hosts/maya/api/plugin.py @@ -29,7 +29,7 @@ from ayon_core.pipeline import ( ) from ayon_core.pipeline.load import LoadError from ayon_core.client import get_asset_by_name -from ayon_core.pipeline.create import get_subset_name +from ayon_core.pipeline.create import get_product_name from . import lib from .lib import imprint, read @@ -89,16 +89,16 @@ class Creator(LegacyCreator): class MayaCreatorBase(object): @staticmethod - def cache_subsets(shared_data): + def cache_instance_data(shared_data): """Cache instances for Creators to shared data. - Create `maya_cached_subsets` key when needed in shared data and + Create `maya_cached_instance_data` key when needed in shared data and fill it with all collected instances from the scene under its respective creator identifiers. If legacy instances are detected in the scene, create - `maya_cached_legacy_subsets` there and fill it with - all legacy subsets under family as a key. + `maya_cached_legacy_instances` there and fill it with + all legacy products under product type as a key. Args: Dict[str, Any]: Shared data. @@ -107,7 +107,7 @@ class MayaCreatorBase(object): Dict[str, Any]: Shared data dictionary. """ - if shared_data.get("maya_cached_subsets") is None: + if shared_data.get("maya_cached_instance_data") is None: cache = dict() cache_legacy = dict() @@ -131,8 +131,8 @@ class MayaCreatorBase(object): cache_legacy.setdefault(family, []).append(node) - shared_data["maya_cached_subsets"] = cache - shared_data["maya_cached_legacy_subsets"] = cache_legacy + shared_data["maya_cached_instance_data"] = cache + shared_data["maya_cached_legacy_instances"] = cache_legacy return shared_data def get_publish_families(self): @@ -143,8 +143,7 @@ class MayaCreatorBase(object): specify `usd` but apply different extractors like `usdMultiverse`. There is no need to override this method if you only have the - primary family defined by the `family` property as that will always - be set. + 'product_type' required for publish filtering. Returns: list: families for instances of this creator @@ -165,7 +164,7 @@ class MayaCreatorBase(object): data.pop("families", None) # We store creator attributes at the root level and assume they - # will not clash in names with `subset`, `task`, etc. and other + # will not clash in names with `product`, `task`, etc. and other # default names. This is just so these attributes in many cases # are still editable in the maya UI by artists. # note: pop to move to end of dict to sort attributes last on the node @@ -244,9 +243,11 @@ class MayaCreatorBase(object): return node_data def _default_collect_instances(self): - self.cache_subsets(self.collection_shared_data) - cached_subsets = self.collection_shared_data["maya_cached_subsets"] - for node in cached_subsets.get(self.identifier, []): + self.cache_instance_data(self.collection_shared_data) + cached_instances = ( + self.collection_shared_data["maya_cached_instance_data"] + ) + for node in cached_instances.get(self.identifier, []): node_data = self.read_instance_node(node) created_instance = CreatedInstance.from_existing(node_data, self) @@ -279,7 +280,7 @@ class MayaCreator(NewCreator, MayaCreatorBase): settings_category = "maya" - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): members = list() if pre_create_data.get("use_selection"): @@ -294,11 +295,11 @@ class MayaCreator(NewCreator, MayaCreatorBase): families.append(family) with lib.undo_chunk(): - instance_node = cmds.sets(members, name=subset_name) + instance_node = cmds.sets(members, name=product_name) instance_data["instance_node"] = instance_node instance = CreatedInstance( - self.family, - subset_name, + self.product_type, + product_name, instance_data, self) self._add_instance_to_context(instance) @@ -385,7 +386,7 @@ def ensure_namespace(namespace): class RenderlayerCreator(NewCreator, MayaCreatorBase): """Creator which creates an instance per renderlayer in the workfile. - Create and manages renderlayer subset per renderLayer in workfile. + Create and manages renderlayer product per renderLayer in workfile. This generates a singleton node in the scene which, if it exists, tells the Creator to collect Maya rendersetup renderlayers as individual instances. As such, triggering create doesn't actually create the instance node per @@ -405,7 +406,7 @@ class RenderlayerCreator(NewCreator, MayaCreatorBase): if nodes: return nodes if return_all else nodes[0] - def create(self, subset_name, instance_data, pre_create_data): + def create(self, product_name, instance_data, pre_create_data): # A Renderlayer is never explicitly created using the create method. # Instead, renderlayers from the scene are collected. Thus "create" # would only ever be called to say, 'hey, please refresh collect' @@ -462,15 +463,15 @@ class RenderlayerCreator(NewCreator, MayaCreatorBase): "variant": layer.name(), } asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( layer.name(), instance_data["task"], asset_doc, project_name) instance = CreatedInstance( - family=self.family, - subset_name=subset_name, + product_type=self.product_type, + product_name=product_name, data=instance_data, creator=self ) @@ -574,7 +575,7 @@ class RenderlayerCreator(NewCreator, MayaCreatorBase): if node and cmds.objExists(node): cmds.delete(node) - def get_subset_name( + def get_product_name( self, variant, task_name, @@ -583,19 +584,27 @@ class RenderlayerCreator(NewCreator, MayaCreatorBase): host_name=None, instance=None ): - # creator.family != 'render' as expected - return get_subset_name(self.layer_instance_prefix, - variant, - task_name, - asset_doc, - project_name) + dynamic_data = self.get_dynamic_data( + variant, task_name, asset_doc, project_name, host_name, instance + ) + # creator.product_type != 'render' as expected + return get_product_name( + project_name, + asset_doc, + task_name, + host_name, + self.layer_instance_prefix or self.product_type, + variant, + dynamic_data=dynamic_data, + project_settings=self.project_settings + ) -def get_load_color_for_family(family, settings=None): - """Get color for family from settings. +def get_load_color_for_product_type(product_type, settings=None): + """Get color for product type from settings. Args: - family (str): Family name. + product_type (str): Family name. settings (Optional[dict]): Settings dictionary. Returns: @@ -606,7 +615,7 @@ def get_load_color_for_family(family, settings=None): settings = get_project_settings(get_current_project_name()) colors = settings["maya"]["load"]["colors"] - color = colors.get(family) + color = colors.get(product_type) if not color: return None @@ -657,24 +666,24 @@ class Loader(LoaderPlugin): self.log.debug("No custom group_name, no group will be created.") options["attach_to_root"] = False - asset = context["asset"] - subset = context["subset"] - family = ( - subset["data"].get("family") - or subset["data"]["families"][0] + asset_doc = context["asset"] + subset_doc = context["subset"] + product_type = ( + subset_doc["data"].get("family") + or subset_doc["data"]["families"][0] ) formatting_data = { - "asset_name": asset["name"], - "asset_type": asset["type"], + "asset_name": asset_doc["name"], + "asset_type": asset_doc["type"], "folder": { - "name": asset["name"], + "name": asset_doc["name"], }, - "subset": subset["name"], + "subset": subset_doc["name"], "product": { - "name": subset["name"], - "type": family, + "name": subset_doc["name"], + "type": product_type, }, - "family": family + "family": product_type } custom_namespace = custom_naming["namespace"].format( @@ -745,7 +754,7 @@ class ReferenceLoader(Loader): options['group_name'] = group_name - # Offset loaded subset + # Offset loaded product if "offset" in options: offset = [i * c for i in options["offset"]] options["translate"] = offset @@ -782,14 +791,17 @@ class ReferenceLoader(Loader): """To be implemented by subclass""" raise NotImplementedError("Must be implemented by subclass") - def update(self, container, representation): + def update(self, container, context): from maya import cmds from ayon_core.hosts.maya.api.lib import get_container_members node = container["objectName"] - path = get_representation_path(representation) + project_name = context["project"]["name"] + repre_doc = context["representation"] + + path = get_representation_path(repre_doc) # Get reference node from container members members = get_container_members(node) @@ -802,9 +814,9 @@ class ReferenceLoader(Loader): "abc": "Alembic", "fbx": "FBX", "usd": "USD Import" - }.get(representation["name"]) + }.get(repre_doc["name"]) - assert file_type, "Unsupported representation: %s" % representation + assert file_type, "Unsupported representation: %s" % repre_doc assert os.path.exists(path), "%s does not exist." % path @@ -812,7 +824,7 @@ class ReferenceLoader(Loader): # them to incoming data. alembic_attrs = ["speed", "offset", "cycleType", "time"] alembic_data = {} - if representation["name"] == "abc": + if repre_doc["name"] == "abc": alembic_nodes = cmds.ls( "{}:*".format(namespace), type="AlembicNode" ) @@ -829,10 +841,7 @@ class ReferenceLoader(Loader): self.log.debug("No alembic nodes found in {}".format(members)) try: - path = self.prepare_root_value(path, - representation["context"] - ["project"] - ["name"]) + path = self.prepare_root_value(path, project_name) content = cmds.file(path, loadReference=reference_node, type=file_type, @@ -856,7 +865,7 @@ class ReferenceLoader(Loader): self._organize_containers(content, container["objectName"]) # Reapply alembic settings. - if representation["name"] == "abc" and alembic_data: + if repre_doc["name"] == "abc" and alembic_data: alembic_nodes = cmds.ls( "{}:*".format(namespace), type="AlembicNode" ) @@ -890,7 +899,7 @@ class ReferenceLoader(Loader): # Update metadata cmds.setAttr("{}.representation".format(node), - str(representation["_id"]), + str(repre_doc["_id"]), type="string") # When an animation or pointcache gets connected to an Xgen container, diff --git a/client/ayon_core/hosts/maya/api/setdress.py b/client/ayon_core/hosts/maya/api/setdress.py index 7a1054cc49..03b18927ac 100644 --- a/client/ayon_core/hosts/maya/api/setdress.py +++ b/client/ayon_core/hosts/maya/api/setdress.py @@ -315,16 +315,27 @@ def update_package_version(container, version): new_representation = get_representation_by_name( project_name, current_representation["name"], new_version["_id"] ) - - update_package(container, new_representation) + # TODO there is 'get_representation_context' to get the context which + # could be possible to use here + new_context = { + "project": { + "name": project_doc["name"], + "code": project_doc["data"].get("code", "") + }, + "asset": asset_doc, + "subset": subset_doc, + "version": version_doc, + "representation": new_representation, + } + update_package(container, new_context) -def update_package(set_container, representation): +def update_package(set_container, context): """Update any matrix changes in the scene based on the new data Args: set_container (dict): container data from `ls()` - representation (dict): the representation document from the database + context (dict): the representation document from the database Returns: None @@ -332,7 +343,8 @@ def update_package(set_container, representation): """ # Load the original package data - project_name = get_current_project_name() + project_name = context["project"]["name"] + repre_doc = context["representation"] current_representation = get_representation_by_id( project_name, set_container["representation"] ) @@ -343,7 +355,7 @@ def update_package(set_container, representation): current_data = json.load(fp) # Load the new package data - new_file = get_representation_path(representation) + new_file = get_representation_path(repre_doc) assert new_file.endswith(".json") with open(new_file, "r") as fp: new_data = json.load(fp) @@ -354,7 +366,7 @@ def update_package(set_container, representation): # TODO: This should be handled by the pipeline itself cmds.setAttr(set_container['objectName'] + ".representation", - str(representation['_id']), type="string") + str(repre_doc['_id']), type="string") def update_scene(set_container, containers, current_data, new_data, new_file): diff --git a/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py b/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py index 16ac460cf7..04d9084175 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py @@ -177,7 +177,7 @@ class ArnoldStandinLoader(load.LoaderPlugin): return proxy_path, string_replace_operator - def update(self, container, representation): + def update(self, container, context): # Update the standin members = cmds.sets(container['objectName'], query=True) for member in members: @@ -190,7 +190,8 @@ class ArnoldStandinLoader(load.LoaderPlugin): if cmds.nodeType(shapes[0]) == "aiStandIn": standin = shapes[0] - path = get_representation_path(representation) + repre_doc = context["representation"] + path = get_representation_path(repre_doc) proxy_basename, proxy_path = self._get_proxy_path(path) # Whether there is proxy or so, we still update the string operator. @@ -216,12 +217,12 @@ class ArnoldStandinLoader(load.LoaderPlugin): cmds.setAttr( container["objectName"] + ".representation", - str(representation["_id"]), + str(repre_doc["_id"]), type="string" ) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): members = cmds.sets(container['objectName'], query=True) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_assembly.py b/client/ayon_core/hosts/maya/plugins/load/load_assembly.py index e119dfe1c3..1f06655dad 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_assembly.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_assembly.py @@ -49,9 +49,9 @@ class AssemblyLoader(load.LoaderPlugin): context=context, loader=self.__class__.__name__) - def update(self, container, representation): + def update(self, container, context): - return setdress.update_package(container, representation) + return setdress.update_package(container, context) def remove(self, container): """Remove all sub containers""" diff --git a/client/ayon_core/hosts/maya/plugins/load/load_audio.py b/client/ayon_core/hosts/maya/plugins/load/load_audio.py index deeeac66f2..df811a585c 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_audio.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_audio.py @@ -45,7 +45,8 @@ class AudioLoader(load.LoaderPlugin): loader=self.__class__.__name__ ) - def update(self, container, representation): + def update(self, container, context): + repre_doc = context["representation"] members = get_container_members(container) audio_nodes = cmds.ls(members, type="audio") @@ -60,7 +61,7 @@ class AudioLoader(load.LoaderPlugin): ) activate_sound = current_sound == audio_node - path = get_representation_path(representation) + path = get_representation_path(repre_doc) cmds.sound( audio_node, @@ -93,12 +94,12 @@ class AudioLoader(load.LoaderPlugin): cmds.setAttr( container["objectName"] + ".representation", - str(representation["_id"]), + str(repre_doc["_id"]), type="string" ) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): members = cmds.sets(container['objectName'], query=True) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_gpucache.py b/client/ayon_core/hosts/maya/plugins/load/load_gpucache.py index cdaaeeae6a..6b632f2343 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_gpucache.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_gpucache.py @@ -74,8 +74,9 @@ class GpuCacheLoader(load.LoaderPlugin): context=context, loader=self.__class__.__name__) - def update(self, container, representation): - path = get_representation_path(representation) + def update(self, container, context): + repre_doc = context["representation"] + path = get_representation_path(repre_doc) # Update the cache members = cmds.sets(container['objectName'], query=True) @@ -87,11 +88,11 @@ class GpuCacheLoader(load.LoaderPlugin): cmds.setAttr(cache + ".cacheFileName", path, type="string") cmds.setAttr(container["objectName"] + ".representation", - str(representation["_id"]), + str(repre_doc["_id"]), type="string") - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): members = cmds.sets(container['objectName'], query=True) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_image.py b/client/ayon_core/hosts/maya/plugins/load/load_image.py index aedeb63e3d..7b324986f0 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_image.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_image.py @@ -146,23 +146,23 @@ class FileNodeLoader(load.LoaderPlugin): loader=self.__class__.__name__ ) - def update(self, container, representation): + def update(self, container, context): + repre_doc = context["representation"] members = cmds.sets(container['objectName'], query=True) file_node = cmds.ls(members, type="file")[0] - context = get_representation_context(representation) self._apply_representation_context(context, file_node) # Update representation cmds.setAttr( container["objectName"] + ".representation", - str(representation["_id"]), + str(repre_doc["_id"]), type="string" ) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): members = cmds.sets(container['objectName'], query=True) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_image_plane.py b/client/ayon_core/hosts/maya/plugins/load/load_image_plane.py index fb27e6597a..2366f6edd7 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_image_plane.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_image_plane.py @@ -205,34 +205,26 @@ class ImagePlaneLoader(load.LoaderPlugin): loader=self.__class__.__name__ ) - def update(self, container, representation): + def update(self, container, context): + asset_doc = context["asset"] + repre_doc = context["representation"] members = get_container_members(container) image_planes = cmds.ls(members, type="imagePlane") assert image_planes, "Image plane not found." image_plane_shape = image_planes[0] - path = get_representation_path(representation) + path = get_representation_path(repre_doc) cmds.setAttr("{}.imageName".format(image_plane_shape), path, type="string") cmds.setAttr("{}.representation".format(container["objectName"]), - str(representation["_id"]), + str(repre_doc["_id"]), type="string") # Set frame range. - project_name = get_current_project_name() - version = get_version_by_id( - project_name, representation["parent"], fields=["parent"] - ) - subset = get_subset_by_id( - project_name, version["parent"], fields=["parent"] - ) - asset = get_asset_by_id( - project_name, subset["parent"], fields=["parent"] - ) - start_frame = asset["data"]["frameStart"] - end_frame = asset["data"]["frameEnd"] + start_frame = asset_doc["data"]["frameStart"] + end_frame = asset_doc["data"]["frameEnd"] for attr, value in { "frameOffset": 0, @@ -243,8 +235,8 @@ class ImagePlaneLoader(load.LoaderPlugin): plug = "{}.{}".format(image_plane_shape, attr) cmds.setAttr(plug, value) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): members = cmds.sets(container['objectName'], query=True) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_look.py b/client/ayon_core/hosts/maya/plugins/load/load_look.py index ba5891469d..fb5be14aa1 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_look.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_look.py @@ -43,10 +43,10 @@ class LookLoader(ayon_core.hosts.maya.api.plugin.ReferenceLoader): self[:] = nodes - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) - def update(self, container, representation): + def update(self, container, context): """ Called by Scene Inventory when look should be updated to current version. @@ -56,7 +56,7 @@ class LookLoader(ayon_core.hosts.maya.api.plugin.ReferenceLoader): Args: container: object that has look to be updated - representation: (dict): relationship data to get proper + context: (dict): relationship data to get proper representation from DB and persisted data in .json Returns: @@ -72,15 +72,16 @@ class LookLoader(ayon_core.hosts.maya.api.plugin.ReferenceLoader): orig_nodes = set(self._get_nodes_with_shader(shader_nodes)) # Trigger the regular reference update on the ReferenceLoader - super(LookLoader, self).update(container, representation) + super(LookLoader, self).update(container, context) # get new applied shaders and nodes from new version shader_nodes = cmds.ls(members, type='shadingEngine') nodes = set(self._get_nodes_with_shader(shader_nodes)) + version_doc = context["version"] project_name = get_current_project_name() json_representation = get_representation_by_name( - project_name, "json", representation["parent"] + project_name, "json", version_doc["_id"] ) # Load relationships diff --git a/client/ayon_core/hosts/maya/plugins/load/load_maya_usd.py b/client/ayon_core/hosts/maya/plugins/load/load_maya_usd.py index c2bea1501c..cb9fde7b33 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_maya_usd.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_maya_usd.py @@ -69,7 +69,7 @@ class MayaUsdLoader(load.LoaderPlugin): context=context, loader=self.__class__.__name__) - def update(self, container, representation): + def update(self, container, context): # type: (dict, dict) -> None """Update container with specified representation.""" node = container['objectName'] @@ -78,16 +78,17 @@ class MayaUsdLoader(load.LoaderPlugin): members = cmds.sets(node, query=True) or [] shapes = cmds.ls(members, type="mayaUsdProxyShape") - path = get_representation_path(representation) + repre_doc = context["representation"] + path = get_representation_path(repre_doc) for shape in shapes: cmds.setAttr("{}.filePath".format(shape), path, type="string") cmds.setAttr("{}.representation".format(node), - str(representation["_id"]), + str(repre_doc["_id"]), type="string") - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): # type: (dict) -> None diff --git a/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd.py b/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd.py index a9ba2b8773..64e6048c31 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd.py @@ -60,7 +60,7 @@ class MultiverseUsdLoader(load.LoaderPlugin): context=context, loader=self.__class__.__name__) - def update(self, container, representation): + def update(self, container, context): # type: (dict, dict) -> None """Update container with specified representation.""" node = container['objectName'] @@ -70,7 +70,9 @@ class MultiverseUsdLoader(load.LoaderPlugin): shapes = cmds.ls(members, type="mvUsdCompoundShape") assert shapes, "Cannot find mvUsdCompoundShape in container" - project_name = representation["context"]["project"]["name"] + project_name = context["project"]["name"] + repre_doc = context["representation"] + path = get_representation_path(repre_doc) prev_representation_id = cmds.getAttr("{}.representation".format(node)) prev_representation = get_representation_by_id(project_name, prev_representation_id) @@ -89,18 +91,17 @@ class MultiverseUsdLoader(load.LoaderPlugin): "Couldn't find matching path (or too many)" prev_path_idx = asset_paths.index(prev_path) - path = get_representation_path(representation) asset_paths[prev_path_idx] = path multiverse.SetUsdCompoundAssetPaths(shape, asset_paths) cmds.setAttr("{}.representation".format(node), - str(representation["_id"]), + str(repre_doc["_id"]), type="string") mel.eval('refreshEditorTemplates;') - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): # type: (dict) -> None diff --git a/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd_over.py b/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd_over.py index d448dc74a8..6de03fe306 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd_over.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd_over.py @@ -71,7 +71,7 @@ class MultiverseUsdOverLoader(load.LoaderPlugin): return container - def update(self, container, representation): + def update(self, container, context): # type: (dict, dict) -> None """Update container with specified representation.""" @@ -88,13 +88,14 @@ class MultiverseUsdOverLoader(load.LoaderPlugin): mvShape = container['mvUsdCompoundShape'] assert mvShape, "Missing mv source" - project_name = representation["context"]["project"]["name"] + project_name = context["project"]["name"] + repre_doc = context["representation"] prev_representation_id = cmds.getAttr("{}.representation".format(node)) prev_representation = get_representation_by_id(project_name, prev_representation_id) prev_path = os.path.normpath(prev_representation["data"]["path"]) - path = get_representation_path(representation) + path = get_representation_path(repre_doc) for shape in shapes: asset_paths = multiverse.GetUsdCompoundAssetPaths(shape) @@ -107,12 +108,12 @@ class MultiverseUsdOverLoader(load.LoaderPlugin): multiverse.SetUsdCompoundAssetPaths(shape, asset_paths) cmds.setAttr("{}.representation".format(node), - str(representation["_id"]), + str(repre_doc["_id"]), type="string") mel.eval('refreshEditorTemplates;') - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): # type: (dict) -> None diff --git a/client/ayon_core/hosts/maya/plugins/load/load_redshift_proxy.py b/client/ayon_core/hosts/maya/plugins/load/load_redshift_proxy.py index 8910d0fcd0..4c6b93ad45 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_redshift_proxy.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_redshift_proxy.py @@ -75,7 +75,7 @@ class RedshiftProxyLoader(load.LoaderPlugin): context=context, loader=self.__class__.__name__) - def update(self, container, representation): + def update(self, container, context): node = container['objectName'] assert cmds.objExists(node), "Missing container" @@ -83,8 +83,8 @@ class RedshiftProxyLoader(load.LoaderPlugin): members = cmds.sets(node, query=True) or [] rs_meshes = cmds.ls(members, type="RedshiftProxyMesh") assert rs_meshes, "Cannot find RedshiftProxyMesh in container" - - filename = get_representation_path(representation) + repre_doc = context["representation"] + filename = get_representation_path(repre_doc) for rs_mesh in rs_meshes: cmds.setAttr("{}.fileName".format(rs_mesh), @@ -93,7 +93,7 @@ class RedshiftProxyLoader(load.LoaderPlugin): # Update metadata cmds.setAttr("{}.representation".format(node), - str(representation["_id"]), + str(repre_doc["_id"]), type="string") def remove(self, container): @@ -113,8 +113,8 @@ class RedshiftProxyLoader(load.LoaderPlugin): self.log.warning("Namespace not deleted because it " "still has members: %s", namespace) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def create_rs_proxy(self, name, path): """Creates Redshift Proxies showing a proxy object. diff --git a/client/ayon_core/hosts/maya/plugins/load/load_reference.py b/client/ayon_core/hosts/maya/plugins/load/load_reference.py index 75f42a9fe6..32b456ed86 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_reference.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_reference.py @@ -228,12 +228,12 @@ class ReferenceLoader(plugin.ReferenceLoader): *options["translate"]) return new_nodes - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) - def update(self, container, representation): + def update(self, container, context): with preserve_modelpanel_cameras(container, log=self.log): - super(ReferenceLoader, self).update(container, representation) + super(ReferenceLoader, self).update(container, context) # We also want to lock camera transforms on any new cameras in the # reference or for a camera which might have changed names. diff --git a/client/ayon_core/hosts/maya/plugins/load/load_rendersetup.py b/client/ayon_core/hosts/maya/plugins/load/load_rendersetup.py index e77e270663..58f161afc1 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_rendersetup.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_rendersetup.py @@ -84,14 +84,15 @@ class RenderSetupLoader(load.LoaderPlugin): # Already implicitly deleted by Maya upon removing reference pass - def update(self, container, representation): + def update(self, container, context): """Update RenderSetup setting by overwriting existing settings.""" lib.show_message( "Render setup update", "Render setup setting will be overwritten by new version. All " "setting specified by user not included in loaded version " "will be lost.") - path = get_representation_path(representation) + repre_doc = context["representation"] + path = get_representation_path(repre_doc) with open(path, "r") as file: try: renderSetup.instance().decode( @@ -103,10 +104,10 @@ class RenderSetupLoader(load.LoaderPlugin): # Update metadata node = container["objectName"] cmds.setAttr("{}.representation".format(node), - str(representation["_id"]), + str(repre_doc["_id"]), type="string") self.log.info("... updated") - def switch(self, container, representation): + def switch(self, container, context): """Switch representations.""" - self.update(container, representation) + self.update(container, context) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py index c68fddc60a..e2f3fe57e9 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py @@ -81,11 +81,13 @@ class LoadVDBtoArnold(load.LoaderPlugin): context=context, loader=self.__class__.__name__) - def update(self, container, representation): + def update(self, container, context): from maya import cmds - path = get_representation_path(representation) + repre_doc = context["representation"] + + path = get_representation_path(repre_doc) # Find VRayVolumeGrid members = cmds.sets(container['objectName'], query=True) @@ -93,15 +95,15 @@ class LoadVDBtoArnold(load.LoaderPlugin): assert len(grid_nodes) == 1, "This is a bug" # Update the VRayVolumeGrid - self._set_path(grid_nodes[0], path=path, representation=representation) + self._set_path(grid_nodes[0], path=path, representation=repre_doc) # Update container representation cmds.setAttr(container["objectName"] + ".representation", - str(representation["_id"]), + str(repre_doc["_id"]), type="string") - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_redshift.py b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_redshift.py index 1bc75ae4c6..60a044b20d 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_redshift.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_redshift.py @@ -95,10 +95,11 @@ class LoadVDBtoRedShift(load.LoaderPlugin): context=context, loader=self.__class__.__name__) - def update(self, container, representation): + def update(self, container, context): from maya import cmds - path = get_representation_path(representation) + repre_doc = context["representation"] + path = get_representation_path(repre_doc) # Find VRayVolumeGrid members = cmds.sets(container['objectName'], query=True) @@ -106,11 +107,11 @@ class LoadVDBtoRedShift(load.LoaderPlugin): assert len(grid_nodes) == 1, "This is a bug" # Update the VRayVolumeGrid - self._set_path(grid_nodes[0], path=path, representation=representation) + self._set_path(grid_nodes[0], path=path, representation=repre_doc) # Update container representation cmds.setAttr(container["objectName"] + ".representation", - str(representation["_id"]), + str(repre_doc["_id"]), type="string") def remove(self, container): @@ -129,8 +130,8 @@ class LoadVDBtoRedShift(load.LoaderPlugin): except RuntimeError: pass - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) @staticmethod def _set_path(grid_node, diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py index 0c87162629..c1cfa8de3b 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py @@ -254,9 +254,10 @@ class LoadVDBtoVRay(load.LoaderPlugin): restored_mapping, type="string") - def update(self, container, representation): + def update(self, container, context): + repre_doc = context["representation"] - path = get_representation_path(representation) + path = get_representation_path(repre_doc) # Find VRayVolumeGrid members = cmds.sets(container['objectName'], query=True) @@ -269,11 +270,11 @@ class LoadVDBtoVRay(load.LoaderPlugin): # Update container representation cmds.setAttr(container["objectName"] + ".representation", - str(representation["_id"]), + str(repre_doc["_id"]), type="string") - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py b/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py index 50b63f4f11..7838556219 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py @@ -96,7 +96,7 @@ class VRayProxyLoader(load.LoaderPlugin): context=context, loader=self.__class__.__name__) - def update(self, container, representation): + def update(self, container, context): # type: (dict, dict) -> None """Update container with specified representation.""" node = container['objectName'] @@ -107,9 +107,10 @@ class VRayProxyLoader(load.LoaderPlugin): assert vraymeshes, "Cannot find VRayMesh in container" # get all representations for this version + repre_doc = context["representation"] filename = ( - self._get_abc(representation["parent"]) - or get_representation_path(representation) + self._get_abc(repre_doc["parent"]) + or get_representation_path(repre_doc) ) for vray_mesh in vraymeshes: @@ -119,7 +120,7 @@ class VRayProxyLoader(load.LoaderPlugin): # Update metadata cmds.setAttr("{}.representation".format(node), - str(representation["_id"]), + str(repre_doc["_id"]), type="string") def remove(self, container): @@ -140,10 +141,10 @@ class VRayProxyLoader(load.LoaderPlugin): self.log.warning("Namespace not deleted because it " "still has members: %s", namespace) - def switch(self, container, representation): + def switch(self, container, context): # type: (dict, dict) -> None """Switch loaded representation.""" - self.update(container, representation) + self.update(container, context) def create_vray_proxy(self, name, filename): # type: (str, str) -> (list, str) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py b/client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py index 7b4edb0567..f8b938a082 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py @@ -73,7 +73,7 @@ class VRaySceneLoader(load.LoaderPlugin): context=context, loader=self.__class__.__name__) - def update(self, container, representation): + def update(self, container, context): node = container['objectName'] assert cmds.objExists(node), "Missing container" @@ -82,7 +82,8 @@ class VRaySceneLoader(load.LoaderPlugin): vraymeshes = cmds.ls(members, type="VRayScene") assert vraymeshes, "Cannot find VRayScene in container" - filename = get_representation_path(representation) + repre_doc = context["representation"] + filename = get_representation_path(repre_doc) for vray_mesh in vraymeshes: cmds.setAttr("{}.FilePath".format(vray_mesh), @@ -91,7 +92,7 @@ class VRaySceneLoader(load.LoaderPlugin): # Update metadata cmds.setAttr("{}.representation".format(node), - str(representation["_id"]), + str(repre_doc["_id"]), type="string") def remove(self, container): @@ -111,8 +112,8 @@ class VRaySceneLoader(load.LoaderPlugin): self.log.warning("Namespace not deleted because it " "still has members: %s", namespace) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def create_vray_scene(self, name, filename): """Re-create the structure created by VRay to support vrscenes diff --git a/client/ayon_core/hosts/maya/plugins/load/load_xgen.py b/client/ayon_core/hosts/maya/plugins/load/load_xgen.py index 4c38835350..fdac62a250 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_xgen.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_xgen.py @@ -113,7 +113,7 @@ class XgenLoader(ayon_core.hosts.maya.api.plugin.ReferenceLoader): ) cmds.setAttr("{}.xgExportAsDelta".format(xgen_palette), True) - def update(self, container, representation): + def update(self, container, context): """Workflow for updating Xgen. - Export changes to delta file. @@ -147,7 +147,8 @@ class XgenLoader(ayon_core.hosts.maya.api.plugin.ReferenceLoader): self.set_palette_attributes(xgen_palette, xgen_file, xgd_file) - maya_file = get_representation_path(representation) + repre_doc = context["representation"] + maya_file = get_representation_path(repre_doc) _, extension = os.path.splitext(maya_file) new_xgen_file = maya_file.replace(extension, ".xgen") data_path = "" @@ -173,7 +174,7 @@ class XgenLoader(ayon_core.hosts.maya.api.plugin.ReferenceLoader): "{}.xgExportAsDelta".format(xgen_palette): False } with attribute_values(attribute_data): - super().update(container, representation) + super().update(container, context) xgenm.applyDelta(xgen_palette.replace("|", ""), xgd_file) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py b/client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py index afbb632d87..9dc169e6e4 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py @@ -122,12 +122,12 @@ class YetiCacheLoader(load.LoaderPlugin): cmds.namespace(removeNamespace=namespace, deleteNamespaceContent=True) - def update(self, container, representation): - + def update(self, container, context): + repre_doc = context["representation"] namespace = container["namespace"] container_node = container["objectName"] - path = get_representation_path(representation) + path = get_representation_path(repre_doc) settings = self.read_settings(path) # Collect scene information of asset @@ -216,11 +216,11 @@ class YetiCacheLoader(load.LoaderPlugin): set_attribute(attr, value, yeti_node) cmds.setAttr("{}.representation".format(container_node), - str(representation["_id"]), + str(repre_doc["_id"]), typ="string") - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) # helper functions def create_namespace(self, asset): From 24e9f924257fd1caa823008c5b4e9aadae1f5d07 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 17:57:03 +0100 Subject: [PATCH 316/573] 3dsmax load plugins are expecting representation context --- .../hosts/max/plugins/load/load_camera_fbx.py | 11 ++++++----- .../hosts/max/plugins/load/load_max_scene.py | 11 ++++++----- client/ayon_core/hosts/max/plugins/load/load_model.py | 11 ++++++----- .../hosts/max/plugins/load/load_model_fbx.py | 11 ++++++----- .../hosts/max/plugins/load/load_model_obj.py | 11 ++++++----- .../hosts/max/plugins/load/load_model_usd.py | 11 ++++++----- .../hosts/max/plugins/load/load_pointcache.py | 11 ++++++----- .../max/plugins/load/load_pointcache_ornatrix.py | 11 ++++++----- .../hosts/max/plugins/load/load_pointcloud.py | 11 ++++++----- .../hosts/max/plugins/load/load_redshift_proxy.py | 11 ++++++----- .../ayon_core/hosts/max/plugins/load/load_tycache.py | 11 ++++++----- 11 files changed, 66 insertions(+), 55 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py b/client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py index 8387d7a837..d56445c695 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py +++ b/client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py @@ -51,10 +51,11 @@ class FbxLoader(load.LoaderPlugin): name, selections, context, namespace, loader=self.__class__.__name__) - def update(self, container, representation): + def update(self, container, context): from pymxs import runtime as rt - path = get_representation_path(representation) + repre_doc = context["representation"] + path = get_representation_path(repre_doc) node_name = container["instance_node"] node = rt.getNodeByName(node_name) namespace, _ = get_namespace(node_name) @@ -87,11 +88,11 @@ class FbxLoader(load.LoaderPlugin): update_custom_attribute_data(node, fbx_objects) lib.imprint(container["instance_node"], { - "representation": str(representation["_id"]) + "representation": str(repre_doc["_id"]) }) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): from pymxs import runtime as rt diff --git a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py index ead77cd2f2..39bb3b568d 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py +++ b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py @@ -48,10 +48,11 @@ class MaxSceneLoader(load.LoaderPlugin): name, max_container, context, namespace, loader=self.__class__.__name__) - def update(self, container, representation): + def update(self, container, context): from pymxs import runtime as rt - path = get_representation_path(representation) + repre_doc = context["representation"] + path = get_representation_path(repre_doc) node_name = container["instance_node"] node = rt.getNodeByName(node_name) namespace, _ = get_namespace(node_name) @@ -86,11 +87,11 @@ class MaxSceneLoader(load.LoaderPlugin): update_custom_attribute_data(node, max_objects) lib.imprint(container["instance_node"], { - "representation": str(representation["_id"]) + "representation": str(repre_doc["_id"]) }) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): from pymxs import runtime as rt diff --git a/client/ayon_core/hosts/max/plugins/load/load_model.py b/client/ayon_core/hosts/max/plugins/load/load_model.py index cf35e107c2..e0241bdb73 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_model.py +++ b/client/ayon_core/hosts/max/plugins/load/load_model.py @@ -70,10 +70,11 @@ class ModelAbcLoader(load.LoaderPlugin): namespace, loader=self.__class__.__name__ ) - def update(self, container, representation): + def update(self, container, context): from pymxs import runtime as rt - path = get_representation_path(representation) + repre_doc = context["representation"] + path = get_representation_path(repre_doc) node = rt.GetNodeByName(container["instance_node"]) node_list = [n for n in get_previous_loaded_object(node) if rt.ClassOf(n) == rt.AlembicContainer] @@ -90,11 +91,11 @@ class ModelAbcLoader(load.LoaderPlugin): abc_obj.source = path lib.imprint( container["instance_node"], - {"representation": str(representation["_id"])}, + {"representation": str(repre_doc["_id"])}, ) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): from pymxs import runtime as rt diff --git a/client/ayon_core/hosts/max/plugins/load/load_model_fbx.py b/client/ayon_core/hosts/max/plugins/load/load_model_fbx.py index c0bacca33a..03ba901b32 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_model_fbx.py +++ b/client/ayon_core/hosts/max/plugins/load/load_model_fbx.py @@ -47,10 +47,11 @@ class FbxModelLoader(load.LoaderPlugin): name, selections, context, namespace, loader=self.__class__.__name__) - def update(self, container, representation): + def update(self, container, context): from pymxs import runtime as rt - path = get_representation_path(representation) + repre_doc = context["representation"] + path = get_representation_path(repre_doc) node_name = container["instance_node"] node = rt.getNodeByName(node_name) if not node: @@ -85,11 +86,11 @@ class FbxModelLoader(load.LoaderPlugin): rt.Select(node) update_custom_attribute_data(node, fbx_objects) lib.imprint(container["instance_node"], { - "representation": str(representation["_id"]) + "representation": str(repre_doc["_id"]) }) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): from pymxs import runtime as rt diff --git a/client/ayon_core/hosts/max/plugins/load/load_model_obj.py b/client/ayon_core/hosts/max/plugins/load/load_model_obj.py index 1023b67f0c..a6c3d2a2fe 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_model_obj.py +++ b/client/ayon_core/hosts/max/plugins/load/load_model_obj.py @@ -47,10 +47,11 @@ class ObjLoader(load.LoaderPlugin): name, selections, context, namespace, loader=self.__class__.__name__) - def update(self, container, representation): + def update(self, container, context): from pymxs import runtime as rt - path = get_representation_path(representation) + repre_doc = context["representation"] + path = get_representation_path(repre_doc) node_name = container["instance_node"] node = rt.getNodeByName(node_name) namespace, _ = get_namespace(node_name) @@ -77,11 +78,11 @@ class ObjLoader(load.LoaderPlugin): rt.Select(node) lib.imprint(node_name, { - "representation": str(representation["_id"]) + "representation": str(repre_doc["_id"]) }) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): from pymxs import runtime as rt diff --git a/client/ayon_core/hosts/max/plugins/load/load_model_usd.py b/client/ayon_core/hosts/max/plugins/load/load_model_usd.py index 0ec6e5e8e7..6673a2e48b 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_model_usd.py +++ b/client/ayon_core/hosts/max/plugins/load/load_model_usd.py @@ -65,8 +65,9 @@ class ModelUSDLoader(load.LoaderPlugin): name, usd_objects, context, namespace, loader=self.__class__.__name__) - def update(self, container, representation): - path = get_representation_path(representation) + def update(self, container, context): + repre_doc = context["representation"] + path = get_representation_path(repre_doc) node_name = container["instance_node"] node = rt.GetNodeByName(node_name) namespace, name = get_namespace(node_name) @@ -107,11 +108,11 @@ class ModelUSDLoader(load.LoaderPlugin): rt.Select(node) lib.imprint(node_name, { - "representation": str(representation["_id"]) + "representation": str(repre_doc["_id"]) }) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): from pymxs import runtime as rt diff --git a/client/ayon_core/hosts/max/plugins/load/load_pointcache.py b/client/ayon_core/hosts/max/plugins/load/load_pointcache.py index e9cde4c654..6f79caea42 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_pointcache.py +++ b/client/ayon_core/hosts/max/plugins/load/load_pointcache.py @@ -76,10 +76,11 @@ class AbcLoader(load.LoaderPlugin): namespace, loader=self.__class__.__name__ ) - def update(self, container, representation): + def update(self, container, context): from pymxs import runtime as rt - path = get_representation_path(representation) + repre_doc = context["representation"] + path = get_representation_path(repre_doc) node = rt.GetNodeByName(container["instance_node"]) abc_container = [n for n in get_previous_loaded_object(node) if rt.ClassOf(n) == rt.AlembicContainer] @@ -96,11 +97,11 @@ class AbcLoader(load.LoaderPlugin): abc_obj.source = path lib.imprint( container["instance_node"], - {"representation": str(representation["_id"])}, + {"representation": str(repre_doc["_id"])}, ) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): from pymxs import runtime as rt diff --git a/client/ayon_core/hosts/max/plugins/load/load_pointcache_ornatrix.py b/client/ayon_core/hosts/max/plugins/load/load_pointcache_ornatrix.py index 338cbfafb9..67d1374266 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_pointcache_ornatrix.py +++ b/client/ayon_core/hosts/max/plugins/load/load_pointcache_ornatrix.py @@ -62,8 +62,9 @@ class OxAbcLoader(load.LoaderPlugin): namespace, loader=self.__class__.__name__ ) - def update(self, container, representation): - path = get_representation_path(representation) + def update(self, container, context): + repre_doc = context["representation"] + path = get_representation_path(repre_doc) node_name = container["instance_node"] namespace, name = get_namespace(node_name) node = rt.getNodeByName(node_name) @@ -98,11 +99,11 @@ class OxAbcLoader(load.LoaderPlugin): update_custom_attribute_data(node, ox_abc_objects) lib.imprint( container["instance_node"], - {"representation": str(representation["_id"])}, + {"representation": str(repre_doc["_id"])}, ) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): from pymxs import runtime as rt diff --git a/client/ayon_core/hosts/max/plugins/load/load_pointcloud.py b/client/ayon_core/hosts/max/plugins/load/load_pointcloud.py index 7f4fba50b3..894648ff23 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_pointcloud.py +++ b/client/ayon_core/hosts/max/plugins/load/load_pointcloud.py @@ -41,11 +41,12 @@ class PointCloudLoader(load.LoaderPlugin): name, [obj], context, namespace, loader=self.__class__.__name__) - def update(self, container, representation): + def update(self, container, context): """update the container""" from pymxs import runtime as rt - path = get_representation_path(representation) + repre_doc = context["representation"] + path = get_representation_path(repre_doc) node = rt.GetNodeByName(container["instance_node"]) node_list = get_previous_loaded_object(node) update_custom_attribute_data( @@ -55,11 +56,11 @@ class PointCloudLoader(load.LoaderPlugin): for prt in rt.Selection: prt.filename = path lib.imprint(container["instance_node"], { - "representation": str(representation["_id"]) + "representation": str(repre_doc["_id"]) }) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): """remove the container""" diff --git a/client/ayon_core/hosts/max/plugins/load/load_redshift_proxy.py b/client/ayon_core/hosts/max/plugins/load/load_redshift_proxy.py index 5f2f5ec1ad..7395a6eca5 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_redshift_proxy.py +++ b/client/ayon_core/hosts/max/plugins/load/load_redshift_proxy.py @@ -52,10 +52,11 @@ class RedshiftProxyLoader(load.LoaderPlugin): name, [rs_proxy], context, namespace, loader=self.__class__.__name__) - def update(self, container, representation): + def update(self, container, context): from pymxs import runtime as rt - path = get_representation_path(representation) + repre_doc = context["representation"] + path = get_representation_path(repre_doc) node = rt.getNodeByName(container["instance_node"]) node_list = get_previous_loaded_object(node) rt.Select(node_list) @@ -65,11 +66,11 @@ class RedshiftProxyLoader(load.LoaderPlugin): proxy.file = path lib.imprint(container["instance_node"], { - "representation": str(representation["_id"]) + "representation": str(repre_doc["_id"]) }) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): from pymxs import runtime as rt diff --git a/client/ayon_core/hosts/max/plugins/load/load_tycache.py b/client/ayon_core/hosts/max/plugins/load/load_tycache.py index 7ae1aea72c..5acc759b4a 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_tycache.py +++ b/client/ayon_core/hosts/max/plugins/load/load_tycache.py @@ -39,11 +39,12 @@ class TyCacheLoader(load.LoaderPlugin): name, [obj], context, namespace, loader=self.__class__.__name__) - def update(self, container, representation): + def update(self, container, context): """update the container""" from pymxs import runtime as rt - path = get_representation_path(representation) + repre_doc = context["representation"] + path = get_representation_path(repre_doc) node = rt.GetNodeByName(container["instance_node"]) node_list = get_previous_loaded_object(node) update_custom_attribute_data(node, node_list) @@ -51,11 +52,11 @@ class TyCacheLoader(load.LoaderPlugin): for tyc in node_list: tyc.filename = path lib.imprint(container["instance_node"], { - "representation": str(representation["_id"]) + "representation": str(repre_doc["_id"]) }) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): """remove the container""" From 8bdf0552beb56c14f2c1921e62ee877aaf660773 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 17:57:09 +0100 Subject: [PATCH 317/573] houdini load plugins are expecting representation context --- .../hosts/houdini/plugins/load/load_alembic.py | 12 ++++++------ .../houdini/plugins/load/load_alembic_archive.py | 12 ++++++------ .../hosts/houdini/plugins/load/load_ass.py | 11 ++++++----- .../hosts/houdini/plugins/load/load_bgeo.py | 14 +++++++------- .../hosts/houdini/plugins/load/load_camera.py | 8 ++++---- .../hosts/houdini/plugins/load/load_fbx.py | 12 ++++++------ .../hosts/houdini/plugins/load/load_hda.py | 7 ++++--- .../hosts/houdini/plugins/load/load_image.py | 12 ++++++------ .../houdini/plugins/load/load_redshift_proxy.py | 10 +++++----- .../hosts/houdini/plugins/load/load_usd_layer.py | 12 ++++++------ .../houdini/plugins/load/load_usd_reference.py | 12 ++++++------ .../hosts/houdini/plugins/load/load_vdb.py | 14 +++++++------- 12 files changed, 69 insertions(+), 67 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_alembic.py b/client/ayon_core/hosts/houdini/plugins/load/load_alembic.py index 6996b0d117..5e138cde83 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_alembic.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_alembic.py @@ -81,8 +81,8 @@ class AbcLoader(load.LoaderPlugin): suffix="", ) - def update(self, container, representation): - + def update(self, container, context): + repre_doc = context["representation"] node = container["node"] try: alembic_node = next( @@ -93,18 +93,18 @@ class AbcLoader(load.LoaderPlugin): return # Update the file path - file_path = get_representation_path(representation) + file_path = get_representation_path(repre_doc) file_path = file_path.replace("\\", "/") alembic_node.setParms({"fileName": file_path}) # Update attribute - node.setParms({"representation": str(representation["_id"])}) + node.setParms({"representation": str(repre_doc["_id"])}) def remove(self, container): node = container["node"] node.destroy() - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_alembic_archive.py b/client/ayon_core/hosts/houdini/plugins/load/load_alembic_archive.py index cfe3b16ebb..0d505806ff 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_alembic_archive.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_alembic_archive.py @@ -55,17 +55,17 @@ class AbcArchiveLoader(load.LoaderPlugin): self.__class__.__name__, suffix="") - def update(self, container, representation): - + def update(self, container, context): + repre_doc = context["representation"] node = container["node"] # Update the file path - file_path = get_representation_path(representation) + file_path = get_representation_path(repre_doc) file_path = file_path.replace("\\", "/") # Update attributes node.setParms({"fileName": file_path, - "representation": str(representation["_id"])}) + "representation": str(repre_doc["_id"])}) # Rebuild node.parm("buildHierarchy").pressButton() @@ -75,5 +75,5 @@ class AbcArchiveLoader(load.LoaderPlugin): node = container["node"] node.destroy() - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_ass.py b/client/ayon_core/hosts/houdini/plugins/load/load_ass.py index 6fbe315adb..396eb3a9f7 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_ass.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_ass.py @@ -48,13 +48,14 @@ class AssLoader(load.LoaderPlugin): suffix="", ) - def update(self, container, representation): + def update(self, container, context): # Update the file path + repre_doc = context["representation"] procedural = container["node"] - procedural.setParms({"ar_filename": self.format_path(representation)}) + procedural.setParms({"ar_filename": self.format_path(repre_doc)}) # Update attribute - procedural.setParms({"representation": str(representation["_id"])}) + procedural.setParms({"representation": str(repre_doc["_id"])}) def remove(self, container): node = container["node"] @@ -86,5 +87,5 @@ class AssLoader(load.LoaderPlugin): return os.path.normpath(path).replace("\\", "/") - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_bgeo.py b/client/ayon_core/hosts/houdini/plugins/load/load_bgeo.py index afcf82562c..4817e40961 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_bgeo.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_bgeo.py @@ -82,8 +82,8 @@ class BgeoLoader(load.LoaderPlugin): return filename - def update(self, container, representation): - + def update(self, container, context): + repre_doc = context["representation"] node = container["node"] try: file_node = next( @@ -94,18 +94,18 @@ class BgeoLoader(load.LoaderPlugin): return # Update the file path - file_path = get_representation_path(representation) - file_path = self.format_path(file_path, representation) + file_path = get_representation_path(repre_doc) + file_path = self.format_path(file_path, repre_doc) file_node.setParms({"file": file_path}) # Update attribute - node.setParms({"representation": str(representation["_id"])}) + node.setParms({"representation": str(repre_doc["_id"])}) def remove(self, container): node = container["node"] node.destroy() - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_camera.py b/client/ayon_core/hosts/houdini/plugins/load/load_camera.py index 11826fb30d..6f6560facc 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_camera.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_camera.py @@ -132,17 +132,17 @@ class CameraLoader(load.LoaderPlugin): self.__class__.__name__, suffix="") - def update(self, container, representation): - + def update(self, container, context): + repre_doc = context["representation"] node = container["node"] # Update the file path - file_path = get_representation_path(representation) + file_path = get_representation_path(repre_doc) file_path = file_path.replace("\\", "/") # Update attributes node.setParms({"fileName": file_path, - "representation": str(representation["_id"])}) + "representation": str(repre_doc["_id"])}) # Store the cam temporarily next to the Alembic Archive # so that we can preserve parm values the user set on it diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_fbx.py b/client/ayon_core/hosts/houdini/plugins/load/load_fbx.py index cc1a746d93..c26b12303d 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_fbx.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_fbx.py @@ -47,8 +47,8 @@ class FbxLoader(load.LoaderPlugin): return containerised_nodes - def update(self, container, representation): - + def update(self, container, context): + repre_doc = context["representation"] node = container["node"] try: file_node = next( @@ -59,21 +59,21 @@ class FbxLoader(load.LoaderPlugin): return # Update the file path from representation - file_path = get_representation_path(representation) + file_path = get_representation_path(repre_doc) file_path = file_path.replace("\\", "/") file_node.setParms({"file": file_path}) # Update attribute - node.setParms({"representation": str(representation["_id"])}) + node.setParms({"representation": str(repre_doc["_id"])}) def remove(self, container): node = container["node"] node.destroy() - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def get_node_name(self, context, name=None, namespace=None): """Define node name.""" diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_hda.py b/client/ayon_core/hosts/houdini/plugins/load/load_hda.py index 288152f2bd..ffe9e55036 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_hda.py @@ -48,11 +48,12 @@ class HdaLoader(load.LoaderPlugin): suffix="", ) - def update(self, container, representation): + def update(self, container, context): import hou + repre_doc = context["representation"] hda_node = container["node"] - file_path = get_representation_path(representation) + file_path = get_representation_path(repre_doc) file_path = file_path.replace("\\", "/") hou.hda.installFile(file_path) defs = hda_node.type().allInstalledDefinitions() @@ -60,7 +61,7 @@ class HdaLoader(load.LoaderPlugin): new = def_paths.index(file_path) defs[new].setIsPreferred(True) hda_node.setParms({ - "representation": str(representation["_id"]) + "representation": str(repre_doc["_id"]) }) def remove(self, container): diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_image.py b/client/ayon_core/hosts/houdini/plugins/load/load_image.py index 20fe2f87ca..c89cc3b173 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_image.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_image.py @@ -87,12 +87,12 @@ class ImageLoader(load.LoaderPlugin): return node - def update(self, container, representation): - + def update(self, container, context): + repre_doc = context["representation"] node = container["node"] # Update the file path - file_path = get_representation_path(representation) + file_path = get_representation_path(repre_doc) file_path = file_path.replace("\\", "/") file_path = self._get_file_sequence(file_path) @@ -100,7 +100,7 @@ class ImageLoader(load.LoaderPlugin): node.setParms( { "filename1": file_path, - "representation": str(representation["_id"]), + "representation": str(repre_doc["_id"]), } ) @@ -128,5 +128,5 @@ class ImageLoader(load.LoaderPlugin): fname = ".".join([prefix, "$F{}".format(len(padding)), suffix]) return os.path.join(root, fname).replace("\\", "/") - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_redshift_proxy.py b/client/ayon_core/hosts/houdini/plugins/load/load_redshift_proxy.py index dd6e78b3bc..3e9ce1ff2e 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_redshift_proxy.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_redshift_proxy.py @@ -72,19 +72,19 @@ class RedshiftProxyLoader(load.LoaderPlugin): suffix="", ) - def update(self, container, representation): - + def update(self, container, context): + repre_doc = context["representation"] # Update the file path - file_path = get_representation_path(representation) + file_path = get_representation_path(repre_doc) node = container["node"] node.setParms({ "RS_objprop_proxy_file": self.format_path( - file_path, representation) + file_path, repre_doc) }) # Update attribute - node.setParms({"representation": str(representation["_id"])}) + node.setParms({"representation": str(repre_doc["_id"])}) def remove(self, container): diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_usd_layer.py b/client/ayon_core/hosts/houdini/plugins/load/load_usd_layer.py index 2c37c24884..f4f8a718ad 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_usd_layer.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_usd_layer.py @@ -57,19 +57,19 @@ class USDSublayerLoader(load.LoaderPlugin): return container - def update(self, container, representation): - + def update(self, container, context): + repre_doc = context["representation"] node = container["node"] # Update the file path - file_path = get_representation_path(representation) + file_path = get_representation_path(repre_doc) file_path = file_path.replace("\\", "/") # Update attributes node.setParms( { "filepath1": file_path, - "representation": str(representation["_id"]), + "representation": str(repre_doc["_id"]), } ) @@ -81,5 +81,5 @@ class USDSublayerLoader(load.LoaderPlugin): node = container["node"] node.destroy() - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_usd_reference.py b/client/ayon_core/hosts/houdini/plugins/load/load_usd_reference.py index 9396f00cce..cb83a9a22e 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_usd_reference.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_usd_reference.py @@ -57,19 +57,19 @@ class USDReferenceLoader(load.LoaderPlugin): return container - def update(self, container, representation): - + def update(self, container, context): + repre_doc = context["representation"] node = container["node"] # Update the file path - file_path = get_representation_path(representation) + file_path = get_representation_path(repre_doc) file_path = file_path.replace("\\", "/") # Update attributes node.setParms( { "filepath1": file_path, - "representation": str(representation["_id"]), + "representation": str(repre_doc["_id"]), } ) @@ -81,5 +81,5 @@ class USDReferenceLoader(load.LoaderPlugin): node = container["node"] node.destroy() - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_vdb.py b/client/ayon_core/hosts/houdini/plugins/load/load_vdb.py index c3e374ee8d..ed38e5a5d9 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_vdb.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_vdb.py @@ -79,8 +79,8 @@ class VdbLoader(load.LoaderPlugin): return filename - def update(self, container, representation): - + def update(self, container, context): + repre_doc = context["representation"] node = container["node"] try: file_node = next( @@ -91,18 +91,18 @@ class VdbLoader(load.LoaderPlugin): return # Update the file path - file_path = get_representation_path(representation) - file_path = self.format_path(file_path, representation) + file_path = get_representation_path(repre_doc) + file_path = self.format_path(file_path, repre_doc) file_node.setParms({"file": file_path}) # Update attribute - node.setParms({"representation": str(representation["_id"])}) + node.setParms({"representation": str(repre_doc["_id"])}) def remove(self, container): node = container["node"] node.destroy() - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) From bd58e9fa67431df3034ce3e1b6a52d17a9d282da Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 17:57:18 +0100 Subject: [PATCH 318/573] hiero load plugins are expecting representation context --- client/ayon_core/hosts/hiero/api/plugin.py | 2 +- .../hosts/hiero/plugins/load/load_clip.py | 16 +++++++--------- .../hosts/hiero/plugins/load/load_effects.py | 14 +++++++------- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/client/ayon_core/hosts/hiero/api/plugin.py b/client/ayon_core/hosts/hiero/api/plugin.py index 574865488f..769b9f72c3 100644 --- a/client/ayon_core/hosts/hiero/api/plugin.py +++ b/client/ayon_core/hosts/hiero/api/plugin.py @@ -363,7 +363,7 @@ class SequenceLoader(LoaderPlugin): ): pass - def update(self, container, representation): + def update(self, container, context): """Update an existing `container` """ pass diff --git a/client/ayon_core/hosts/hiero/plugins/load/load_clip.py b/client/ayon_core/hosts/hiero/plugins/load/load_clip.py index b8c51e7536..b2a8a2253d 100644 --- a/client/ayon_core/hosts/hiero/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/hiero/plugins/load/load_clip.py @@ -146,27 +146,25 @@ class LoadClip(phiero.SequenceLoader): self.__class__.__name__, data_imprint) - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) - def update(self, container, representation): + def update(self, container, context): """ Updating previously loaded clips """ - + version_doc = context["version"] + repre_doc = context["representation"] # load clip to timeline and get main variables name = container['name'] namespace = container['namespace'] track_item = phiero.get_track_items( track_item_name=namespace).pop() - project_name = get_current_project_name() - version_doc = get_version_by_id(project_name, representation["parent"]) - version_data = version_doc.get("data", {}) version_name = version_doc.get("name", None) colorspace = version_data.get("colorspace", None) object_name = "{}_{}".format(name, namespace) - file = get_representation_path(representation).replace("\\", "/") + file = get_representation_path(repre_doc).replace("\\", "/") clip = track_item.source() # reconnect media to new path @@ -191,7 +189,7 @@ class LoadClip(phiero.SequenceLoader): # add variables related to version context data_imprint.update({ - "representation": str(representation["_id"]), + "representation": str(repre_doc["_id"]), "version": version_name, "colorspace": colorspace, "objectName": object_name diff --git a/client/ayon_core/hosts/hiero/plugins/load/load_effects.py b/client/ayon_core/hosts/hiero/plugins/load/load_effects.py index 809080e87e..9a5e659451 100644 --- a/client/ayon_core/hosts/hiero/plugins/load/load_effects.py +++ b/client/ayon_core/hosts/hiero/plugins/load/load_effects.py @@ -157,19 +157,19 @@ class LoadEffects(load.LoaderPlugin): return loaded - def update(self, container, representation): + def update(self, container, context): """ Updating previously loaded effects """ + version_doc = context["version"] + repre_doc = context["representation"] active_track = container["_item"] - file = get_representation_path(representation).replace("\\", "/") + file = get_representation_path(repre_doc).replace("\\", "/") # get main variables name = container['name'] namespace = container['namespace'] # get timeline in out data - project_name = get_current_project_name() - version_doc = get_version_by_id(project_name, representation["parent"]) version_data = version_doc["data"] clip_in = version_data["clipIn"] clip_out = version_data["clipOut"] @@ -197,7 +197,7 @@ class LoadEffects(load.LoaderPlugin): data_imprint = { "objectName": object_name, "name": name, - "representation": str(representation["_id"]), + "representation": str(repre_doc["_id"]), "children_names": [] } @@ -256,8 +256,8 @@ class LoadEffects(load.LoaderPlugin): else: return input - def switch(self, container, representation): - self.update(container, representation) + def switch(self, container, context): + self.update(container, context) def remove(self, container): pass From 5d31cdfa363ffaf92571537a96b699022953f17d Mon Sep 17 00:00:00 2001 From: Jose Caraballo Date: Mon, 26 Feb 2024 17:39:55 +0100 Subject: [PATCH 319/573] Pass correct arguments to function. --- client/ayon_core/tools/publisher/control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/publisher/control.py b/client/ayon_core/tools/publisher/control.py index 5ccd428551..eb129ed8f7 100644 --- a/client/ayon_core/tools/publisher/control.py +++ b/client/ayon_core/tools/publisher/control.py @@ -2335,7 +2335,7 @@ class PublisherController(BasePublisherController): "title": "Action failed", "message": "Action failed.", "traceback": "".join( - traceback.format_exception(exception) + traceback.format_exception(type(exception), exception, exception.__traceback__) ), "label": action.__name__, "identifier": action.id From 24f0511859a61f87cae27968c55c938dd8518e95 Mon Sep 17 00:00:00 2001 From: Jose Caraballo Date: Mon, 26 Feb 2024 17:51:48 +0100 Subject: [PATCH 320/573] Fix line length. --- client/ayon_core/tools/publisher/control.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/publisher/control.py b/client/ayon_core/tools/publisher/control.py index eb129ed8f7..487748afd9 100644 --- a/client/ayon_core/tools/publisher/control.py +++ b/client/ayon_core/tools/publisher/control.py @@ -2335,7 +2335,11 @@ class PublisherController(BasePublisherController): "title": "Action failed", "message": "Action failed.", "traceback": "".join( - traceback.format_exception(type(exception), exception, exception.__traceback__) + traceback.format_exception( + type(exception), + exception, + exception.__traceback__ + ) ), "label": action.__name__, "identifier": action.id From 23858a8cab8155e26336c301f8a5f258c1ad5d36 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 26 Feb 2024 18:54:18 +0100 Subject: [PATCH 321/573] remove "family" from json and modify layout loader to expect product type --- .../hosts/blender/plugins/publish/extract_layout.py | 2 +- .../hosts/unreal/plugins/load/load_layout.py | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_layout.py b/client/ayon_core/hosts/blender/plugins/publish/extract_layout.py index 8409014533..16c0392070 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_layout.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_layout.py @@ -181,7 +181,7 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin): json_element["reference_fbx"] = str(fbx_id) if abc_id: json_element["reference_abc"] = str(abc_id) - json_element["family"] = product_type + json_element["product_type"] = product_type json_element["instance_name"] = asset.name json_element["asset_name"] = metadata["asset_name"] diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_layout.py b/client/ayon_core/hosts/unreal/plugins/load/load_layout.py index 2a05720d7a..1a17268d1c 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_layout.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_layout.py @@ -369,16 +369,18 @@ class LayoutLoader(plugin.Loader): if representation not in repr_loaded: repr_loaded.append(representation) - family = element.get('family') + product_type = element.get("product_type") + if product_type is None: + product_type = element.get("family") loaders = loaders_from_representation( all_loaders, representation) loader = None if repr_format == 'fbx': - loader = self._get_fbx_loader(loaders, family) + loader = self._get_fbx_loader(loaders, product_type) elif repr_format == 'abc': - loader = self._get_abc_loader(loaders, family) + loader = self._get_abc_loader(loaders, product_type) if not loader: self.log.error( @@ -422,12 +424,12 @@ class LayoutLoader(plugin.Loader): actors = [] - if family == 'model': + if product_type == 'model': actors, _ = self._process_family( assets, 'StaticMesh', transform, basis, sequence, inst ) - elif family == 'rig': + elif product_type == 'rig': actors, bindings = self._process_family( assets, 'SkeletalMesh', transform, basis, sequence, inst From 4fc80a22a47b549aee2a17607c512006e5ac291c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 27 Feb 2024 09:43:06 +0100 Subject: [PATCH 322/573] remo unused '_id' from json --- .../hosts/blender/plugins/publish/integrate_animation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/hosts/blender/plugins/publish/integrate_animation.py b/client/ayon_core/hosts/blender/plugins/publish/integrate_animation.py index 35f7fb4496..a10144ebf5 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/integrate_animation.py +++ b/client/ayon_core/hosts/blender/plugins/publish/integrate_animation.py @@ -47,7 +47,6 @@ class IntegrateAnimation( obj_id = rep["representation"]["_id"] if obj_id: - json_dict["_id"] = str(obj_id) json_dict["representation_id"] = str(obj_id) with open(json_path, "w") as file: From 2c0cb76f35fb3530cc5762bbb61d1edbdba2b6e0 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 27 Feb 2024 17:39:30 +0800 Subject: [PATCH 323/573] validate the mesh has uv --- .../plugins/publish/validate_mesh_has_uv.py | 45 +++++++++++++++++++ .../max/server/settings/publishers.py | 9 ++++ server_addon/max/server/version.py | 2 +- 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py b/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py new file mode 100644 index 0000000000..3e966acc87 --- /dev/null +++ b/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py @@ -0,0 +1,45 @@ + +import pyblish.api +from ayon_core.hosts.max.api.action import SelectInvalidAction +from ayon_core.pipeline.publish import ( + ValidateMeshOrder, + OptionalPyblishPluginMixin, + PublishValidationError +) +from pymxs import runtime as rt + + +class ValidateMeshHasUVs(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): + + """Validate the current mesh has UVs. + + It validates whether the current UV set has non-zero UVs and + at least more than the vertex count. It's not really bulletproof, + but a simple quick validation to check if there are likely + UVs for every face. + """ + + order = ValidateMeshOrder + hosts = ['max'] + families = ['model'] + label = 'Validate Mesh Has UVs' + actions = [SelectInvalidAction] + optional = True + + + @classmethod + def get_invalid(cls, instance): + invalid = [member for member in instance.data["members"] + if not member.mesh.numTVerts > 0] + return invalid + + def process(self, instance): + invalid = self.get_invalid(instance) + if invalid: + raise PublishValidationError( + title="Mesh has missing UVs", + message="Model meshes are required to have UVs.

" + "Meshes detected with invalid or missing UVs:
" + "{0}".format(invalid) + ) diff --git a/server_addon/max/server/settings/publishers.py b/server_addon/max/server/settings/publishers.py index 5e28c1b467..6ea6a887d1 100644 --- a/server_addon/max/server/settings/publishers.py +++ b/server_addon/max/server/settings/publishers.py @@ -86,6 +86,10 @@ class PublishersModel(BaseSettingsModel): default_factory=ValidateLoadedPluginModel, title="Validate Loaded Plugin" ) + ValidateMeshHasUVs: BasicValidateModel = SettingsField( + default_factory=BasicValidateModel, + title="Validate Mesh Has UVs" + ) ExtractModelObj: BasicValidateModel = SettingsField( default_factory=BasicValidateModel, title="Extract OBJ", @@ -134,6 +138,11 @@ DEFAULT_PUBLISH_SETTINGS = { "optional": True, "family_plugins_mapping": [] }, + "ValidateMeshHasUVs": { + "enabled": True, + "optional": True, + "active": False + }, "ExtractModelObj": { "enabled": True, "optional": True, diff --git a/server_addon/max/server/version.py b/server_addon/max/server/version.py index 1276d0254f..0a8da88258 100644 --- a/server_addon/max/server/version.py +++ b/server_addon/max/server/version.py @@ -1 +1 @@ -__version__ = "0.1.5" +__version__ = "0.1.6" From c80e8fcb3e427044d1e28b5d56ded292acc10939 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 27 Feb 2024 11:08:38 +0100 Subject: [PATCH 324/573] change order of arguments in 'get_product_name' --- .../plugins/create/workfile_creator.py | 14 +++++++--- .../blender/plugins/create/create_workfile.py | 16 ++++++------ .../fusion/plugins/create/create_workfile.py | 14 +++++++--- .../hosts/houdini/api/creator_node_shelves.py | 8 +++--- .../houdini/plugins/create/create_workfile.py | 12 +++++++-- .../max/plugins/create/create_workfile.py | 12 +++++++-- client/ayon_core/hosts/maya/api/plugin.py | 17 +++++++----- .../maya/plugins/create/convert_legacy.py | 7 ++--- .../plugins/create/create_multishot_layout.py | 7 ++--- .../maya/plugins/create/create_workfile.py | 12 +++++++-- .../nuke/plugins/create/workfile_creator.py | 7 +++-- client/ayon_core/hosts/photoshop/lib.py | 13 ++++++---- .../plugins/create/create_flatten_image.py | 23 +++++++++------- .../plugins/create/create_workfile.py | 12 +++++++-- .../plugins/create/create_colorspace_look.py | 6 ++--- .../plugins/create/create_movie_batch.py | 5 ++-- .../plugins/create/create_online.py | 6 ++--- client/ayon_core/hosts/tvpaint/api/plugin.py | 6 ++--- .../tvpaint/plugins/create/create_render.py | 26 +++++++++---------- .../tvpaint/plugins/create/create_review.py | 12 ++++----- .../tvpaint/plugins/create/create_workfile.py | 12 ++++----- client/ayon_core/pipeline/create/context.py | 2 +- .../pipeline/create/creator_plugins.py | 23 +++++++++------- .../pipeline/create/legacy_create.py | 8 +++--- .../workfile/workfile_template_builder.py | 12 ++++----- client/ayon_core/tools/creator/window.py | 2 +- client/ayon_core/tools/publisher/control.py | 2 +- 27 files changed, 181 insertions(+), 115 deletions(-) diff --git a/client/ayon_core/hosts/aftereffects/plugins/create/workfile_creator.py b/client/ayon_core/hosts/aftereffects/plugins/create/workfile_creator.py index 5645881975..41eeca8006 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/create/workfile_creator.py +++ b/client/ayon_core/hosts/aftereffects/plugins/create/workfile_creator.py @@ -50,8 +50,11 @@ class AEWorkfileCreator(AutoCreator): if existing_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - self.default_variant, task_name, asset_doc, - project_name, host_name + project_name, + asset_doc, + task_name, + self.default_variant, + host_name, ) data = { "folderPath": asset_name, @@ -77,8 +80,11 @@ class AEWorkfileCreator(AutoCreator): ): asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - self.default_variant, task_name, asset_doc, - project_name, host_name + project_name, + asset_doc, + task_name, + self.default_variant, + host_name, ) existing_instance["folderPath"] = asset_name existing_instance["task"] = task_name diff --git a/client/ayon_core/hosts/blender/plugins/create/create_workfile.py b/client/ayon_core/hosts/blender/plugins/create/create_workfile.py index 21b383ab97..696f317305 100644 --- a/client/ayon_core/hosts/blender/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/blender/plugins/create/create_workfile.py @@ -44,11 +44,11 @@ class CreateWorkfile(BaseCreator, AutoCreator): if not workfile_instance: asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - self.default_variant, - task_name, - asset_doc, project_name, - host_name + asset_doc, + task_name, + self.default_variant, + host_name, ) data = { "folderPath": asset_name, @@ -78,11 +78,11 @@ class CreateWorkfile(BaseCreator, AutoCreator): # Update instance context if it's different asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - self.default_variant, - task_name, - asset_doc, project_name, - host_name + asset_doc, + task_name, + self.default_variant, + host_name, ) workfile_instance["folderPath"] = asset_name diff --git a/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py b/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py index 1f49d4bbc5..15ed63c313 100644 --- a/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py @@ -79,8 +79,11 @@ class FusionWorkfileCreator(AutoCreator): if existing_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - self.default_variant, task_name, asset_doc, - project_name, host_name + project_name, + asset_doc, + task_name, + self.default_variant, + host_name, ) data = { "folderPath": asset_name, @@ -104,8 +107,11 @@ class FusionWorkfileCreator(AutoCreator): ): asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - self.default_variant, task_name, asset_doc, - project_name, host_name + project_name, + asset_doc, + task_name, + self.default_variant, + host_name, ) existing_instance["folderPath"] = asset_name existing_instance["task"] = task_name diff --git a/client/ayon_core/hosts/houdini/api/creator_node_shelves.py b/client/ayon_core/hosts/houdini/api/creator_node_shelves.py index f795918834..57fdef753a 100644 --- a/client/ayon_core/hosts/houdini/api/creator_node_shelves.py +++ b/client/ayon_core/hosts/houdini/api/creator_node_shelves.py @@ -91,14 +91,14 @@ def create_interactive(creator_identifier, **kwargs): if isinstance(pane, hou.NetworkEditor): pwd = pane.pwd() product_name = creator.get_product_name( - variant=variant, - task_name=context.get_current_task_name(), + project_name=context.get_current_project_name(), asset_doc=get_asset_by_name( project_name=context.get_current_project_name(), asset_name=context.get_current_asset_name() ), - project_name=context.get_current_project_name(), - host_name=context.host_name + task_name=context.get_current_task_name(), + variant=variant, + host_name=context.host_name, ) tool_fn = CATEGORY_GENERIC_TOOL.get(pwd.childTypeCategory()) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py b/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py index b751bce78b..d66b7f9b94 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py @@ -38,7 +38,11 @@ class CreateWorkfile(plugin.HoudiniCreatorBase, AutoCreator): if current_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - variant, task_name, asset_doc, project_name, host_name + project_name, + asset_doc, + task_name, + variant, + host_name, ) data = { "folderPath": asset_name, @@ -63,7 +67,11 @@ class CreateWorkfile(plugin.HoudiniCreatorBase, AutoCreator): # Update instance context if is not the same asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - variant, task_name, asset_doc, project_name, host_name + project_name, + asset_doc, + task_name, + variant, + host_name, ) current_instance["folderPath"] = asset_name current_instance["task"] = task_name diff --git a/client/ayon_core/hosts/max/plugins/create/create_workfile.py b/client/ayon_core/hosts/max/plugins/create/create_workfile.py index 0af087dd39..784a90a246 100644 --- a/client/ayon_core/hosts/max/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/max/plugins/create/create_workfile.py @@ -31,7 +31,11 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): if current_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - variant, task_name, asset_doc, project_name, host_name + project_name, + asset_doc, + task_name, + variant, + host_name, ) data = { "folderPath": asset_name, @@ -59,7 +63,11 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): # Update instance context if is not the same asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - variant, task_name, asset_doc, project_name, host_name + project_name, + asset_doc, + task_name, + variant, + host_name, ) asset_name = get_asset_name_identifier(asset_doc) diff --git a/client/ayon_core/hosts/maya/api/plugin.py b/client/ayon_core/hosts/maya/api/plugin.py index e3cad2f862..279bd0dcb5 100644 --- a/client/ayon_core/hosts/maya/api/plugin.py +++ b/client/ayon_core/hosts/maya/api/plugin.py @@ -444,6 +444,7 @@ class RenderlayerCreator(NewCreator, MayaCreatorBase): if not self._get_singleton_node(): return + host_name = self.create_context.host_name rs = renderSetup.instance() layers = rs.getRenderLayers() for layer in layers: @@ -464,10 +465,12 @@ class RenderlayerCreator(NewCreator, MayaCreatorBase): } asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - layer.name(), - instance_data["task"], + project_name, asset_doc, - project_name) + instance_data["task"], + layer.name(), + host_name, + ) instance = CreatedInstance( product_type=self.product_type, @@ -577,13 +580,15 @@ class RenderlayerCreator(NewCreator, MayaCreatorBase): def get_product_name( self, - variant, - task_name, - asset_doc, project_name, + asset_doc, + task_name, + variant, host_name=None, instance=None ): + if host_name is None: + host_name = self.create_context.host_name dynamic_data = self.get_dynamic_data( variant, task_name, asset_doc, project_name, host_name, instance ) diff --git a/client/ayon_core/hosts/maya/plugins/create/convert_legacy.py b/client/ayon_core/hosts/maya/plugins/create/convert_legacy.py index 97a438a76f..b23c56fc5b 100644 --- a/client/ayon_core/hosts/maya/plugins/create/convert_legacy.py +++ b/client/ayon_core/hosts/maya/plugins/create/convert_legacy.py @@ -145,10 +145,11 @@ class MayaLegacyConvertor(SubsetConvertorPlugin, asset_doc = get_asset_by_name(project_name, original_data["asset"]) product_name = creator.get_product_name( - original_data["variant"], - data["task"], + project_name, asset_doc, - project_name) + data["task"], + original_data["variant"], + ) original_data["productName"] = product_name # Convert to creator attributes when relevant diff --git a/client/ayon_core/hosts/maya/plugins/create/create_multishot_layout.py b/client/ayon_core/hosts/maya/plugins/create/create_multishot_layout.py index 8b03f4ef98..e7b903312f 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_multishot_layout.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_multishot_layout.py @@ -165,10 +165,11 @@ class CreateMultishotLayout(plugin.MayaCreator): layout_creator.create( product_name=layout_creator.get_product_name( - layout_creator.get_default_variant(), - self.create_context.get_current_task_name(), + self.project_name, asset_doc, - self.project_name), + self.create_context.get_current_task_name(), + layout_creator.get_default_variant(), + ), instance_data=instance_data, pre_create_data={ "groupLoadedAssets": pre_create_data["groupLoadedAssets"] diff --git a/client/ayon_core/hosts/maya/plugins/create/create_workfile.py b/client/ayon_core/hosts/maya/plugins/create/create_workfile.py index 81afd65119..3c238a5b0e 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_workfile.py @@ -37,7 +37,11 @@ class CreateWorkfile(plugin.MayaCreatorBase, AutoCreator): if current_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - variant, task_name, asset_doc, project_name, host_name + project_name, + asset_doc, + task_name, + variant, + host_name, ) data = { "folderPath": asset_name, @@ -61,7 +65,11 @@ class CreateWorkfile(plugin.MayaCreatorBase, AutoCreator): # Update instance context if is not the same asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - variant, task_name, asset_doc, project_name, host_name + project_name, + asset_doc, + task_name, + variant, + host_name, ) asset_name = get_asset_name_identifier(asset_doc) diff --git a/client/ayon_core/hosts/nuke/plugins/create/workfile_creator.py b/client/ayon_core/hosts/nuke/plugins/create/workfile_creator.py index bf376559b3..8fe2990058 100644 --- a/client/ayon_core/hosts/nuke/plugins/create/workfile_creator.py +++ b/client/ayon_core/hosts/nuke/plugins/create/workfile_creator.py @@ -33,8 +33,11 @@ class WorkfileCreator(AutoCreator): asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - self.default_variant, task_name, asset_doc, - project_name, host_name + project_name, + asset_doc, + task_name, + self.default_variant, + host_name, ) instance_data.update({ "asset": asset_name, diff --git a/client/ayon_core/hosts/photoshop/lib.py b/client/ayon_core/hosts/photoshop/lib.py index eb7b4acfd5..e9b7b3f804 100644 --- a/client/ayon_core/hosts/photoshop/lib.py +++ b/client/ayon_core/hosts/photoshop/lib.py @@ -52,10 +52,10 @@ class PSAutoCreator(AutoCreator): if existing_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - self.default_variant, - task_name, - asset_doc, project_name, + asset_doc, + task_name, + self.default_variant, host_name, ) data = { @@ -84,8 +84,11 @@ class PSAutoCreator(AutoCreator): ): asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - self.default_variant, task_name, asset_doc, - project_name, host_name + project_name, + asset_doc, + task_name, + self.default_variant, + host_name, ) existing_instance["folderPath"] = asset_name existing_instance["task"] = task_name diff --git a/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py b/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py index d0a92745bb..11bf92d5fb 100644 --- a/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py @@ -44,10 +44,10 @@ class AutoImageCreator(PSAutoCreator): if existing_instance is None: product_name = self.get_product_name( - self.default_variant, - task_name, - asset_doc, project_name, + asset_doc, + task_name, + self.default_variant, host_name, ) @@ -74,8 +74,11 @@ class AutoImageCreator(PSAutoCreator): or existing_instance["task"] != task_name ): product_name = self.get_product_name( - self.default_variant, task_name, asset_doc, - project_name, host_name + project_name, + asset_doc, + task_name, + self.default_variant, + host_name, ) existing_instance["folderPath"] = asset_name existing_instance["task"] = task_name @@ -124,21 +127,23 @@ class AutoImageCreator(PSAutoCreator): def get_product_name( self, - variant, - task_name, - asset_doc, project_name, + asset_doc, + task_name, + variant, host_name=None, instance=None ): + if host_name is None: + host_name = self.create_context.host_name dynamic_data = prepare_template_data({"layer": "{layer}"}) product_name = get_product_name( project_name, asset_doc, task_name, + host_name, self.product_type, variant, - host_name, dynamic_data=dynamic_data ) return clean_product_name(product_name) diff --git a/client/ayon_core/hosts/substancepainter/plugins/create/create_workfile.py b/client/ayon_core/hosts/substancepainter/plugins/create/create_workfile.py index 209bd87d13..23811dfd29 100644 --- a/client/ayon_core/hosts/substancepainter/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/substancepainter/plugins/create/create_workfile.py @@ -50,7 +50,11 @@ class CreateWorkfile(AutoCreator): self.log.info("Auto-creating workfile instance...") asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - variant, task_name, asset_doc, project_name, host_name + project_name, + asset_doc, + task_name, + variant, + host_name, ) data = { "folderPath": asset_name, @@ -66,7 +70,11 @@ class CreateWorkfile(AutoCreator): # Update instance context if is not the same asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - variant, task_name, asset_doc, project_name, host_name + project_name, + asset_doc, + task_name, + variant, + host_name, ) current_instance["folderPath"] = asset_name current_instance["task"] = task_name diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py index e9bf60c197..6aaae82e4b 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py @@ -59,10 +59,10 @@ This creator publishes color space look file (LUT). self.project_name, asset_name) product_name = self.get_product_name( - variant=instance_data["variant"], - task_name=instance_data["task"] or "Not set", - asset_doc=asset_doc, project_name=self.project_name, + asset_doc=asset_doc, + task_name=instance_data["task"] or "Not set", + variant=instance_data["variant"], ) instance_data["creator_attributes"] = { diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_movie_batch.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_movie_batch.py index e3e64203b9..9b3dfdd334 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_movie_batch.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_movie_batch.py @@ -78,13 +78,13 @@ class BatchMovieCreator(TrayPublishCreator): def _get_product_and_task(self, asset_doc, variant, project_name): """Create product name according to standard template process""" task_name = self._get_task_name(asset_doc) - + host_name = self.create_context.host_name try: product_name = get_product_name( project_name, asset_doc, task_name, - self.create_context.host_name, + host_name, self.product_type, variant, ) @@ -98,6 +98,7 @@ class BatchMovieCreator(TrayPublishCreator): project_name, asset_doc, task_name, + host_name, self.product_type, variant, ) diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_online.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_online.py index 1003a3bfb7..a25da0bf34 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_online.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_online.py @@ -102,10 +102,10 @@ class OnlineCreator(TrayPublishCreator): def get_product_name( self, - variant, - task_name, - asset_doc, project_name, + asset_doc, + task_name, + variant, host_name=None, instance=None ): diff --git a/client/ayon_core/hosts/tvpaint/api/plugin.py b/client/ayon_core/hosts/tvpaint/api/plugin.py index 40bf440dc7..bdd6740020 100644 --- a/client/ayon_core/hosts/tvpaint/api/plugin.py +++ b/client/ayon_core/hosts/tvpaint/api/plugin.py @@ -55,10 +55,10 @@ class TVPaintCreatorCommon: def _custom_get_product_name( self, - variant, - task_name, - asset_doc, project_name, + asset_doc, + task_name, + variant, host_name=None, instance=None ): diff --git a/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py b/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py index 718beb5883..d226e786d5 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py +++ b/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py @@ -778,10 +778,10 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): ) product_name: str = creator.get_product_name( - variant, - task_name, - asset_doc, project_name, + asset_doc, + task_name, + variant, host_name=self.create_context.host_name, ) asset_name = get_asset_name_identifier(asset_doc) @@ -832,10 +832,10 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): variant = render_pass["variant"] product_name = creator.get_product_name( - variant, - task_name, - asset_doc, project_name, + asset_doc, + task_name, + variant, host_name=self.create_context.host_name, instance=render_pass ) @@ -1059,11 +1059,11 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - self.default_variant, - task_name, - asset_doc, project_name, - host_name + asset_doc, + task_name, + self.default_variant, + host_name, ) data = { "folderPath": asset_name, @@ -1113,10 +1113,10 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): ): asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - existing_instance["variant"], - task_name, - asset_doc, project_name, + asset_doc, + task_name, + existing_instance["variant"], host_name, existing_instance ) diff --git a/client/ayon_core/hosts/tvpaint/plugins/create/create_review.py b/client/ayon_core/hosts/tvpaint/plugins/create/create_review.py index f7ec71d1a7..1837726cab 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/create/create_review.py +++ b/client/ayon_core/hosts/tvpaint/plugins/create/create_review.py @@ -41,10 +41,10 @@ class TVPaintReviewCreator(TVPaintAutoCreator): if existing_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - self.default_variant, - task_name, - asset_doc, project_name, + asset_doc, + task_name, + self.default_variant, host_name ) data = { @@ -70,10 +70,10 @@ class TVPaintReviewCreator(TVPaintAutoCreator): ): asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - existing_instance["variant"], - task_name, - asset_doc, project_name, + asset_doc, + task_name, + existing_instance["variant"], host_name, existing_instance ) diff --git a/client/ayon_core/hosts/tvpaint/plugins/create/create_workfile.py b/client/ayon_core/hosts/tvpaint/plugins/create/create_workfile.py index 0d42adf203..14a11750a5 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/tvpaint/plugins/create/create_workfile.py @@ -37,10 +37,10 @@ class TVPaintWorkfileCreator(TVPaintAutoCreator): if existing_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - self.default_variant, - task_name, - asset_doc, project_name, + asset_doc, + task_name, + self.default_variant, host_name ) data = { @@ -63,10 +63,10 @@ class TVPaintWorkfileCreator(TVPaintAutoCreator): ): asset_doc = get_asset_by_name(project_name, asset_name) product_name = self.get_product_name( - existing_instance["variant"], - task_name, - asset_doc, project_name, + asset_doc, + task_name, + existing_instance["variant"], host_name, existing_instance ) diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index a74bb8a690..d15c53b7d2 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -2012,8 +2012,8 @@ class CreateContext: project_name, asset_doc, task_name, - self.host_name, variant, + self.host_name, ) asset_name = get_asset_name_identifier(asset_doc) diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index acb1bb5c07..ee6c558f69 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -509,10 +509,10 @@ class BaseCreator: def get_product_name( self, - variant, - task_name, - asset_doc, project_name, + asset_doc, + task_name, + variant, host_name=None, instance=None ): @@ -532,16 +532,19 @@ class BaseCreator: instance is passed in. Args: - variant(str): Product name variant. In most of cases user input. - task_name(str): For which task product is created. - asset_doc(dict): Asset document for which product is created. - project_name(str): Project name. - host_name(str): Which host creates product. - instance(CreatedInstance|None): Object of 'CreatedInstance' for - which is product name updated. Passed only on product name + project_name (str): Project name. + asset_doc (dict): Asset document for which product is created. + task_name (str): For which task product is created. + variant (str): Product name variant. In most of cases user input. + host_name (Optional[str]): Which host creates product. Defaults + to host name on create context. + instance (Optional[CreatedInstance]): Object of 'CreatedInstance' + for which is product name updated. Passed only on product name update. """ + if host_name is None: + host_name = self.create_context.host_name dynamic_data = self.get_dynamic_data( variant, task_name, asset_doc, project_name, host_name, instance ) diff --git a/client/ayon_core/pipeline/create/legacy_create.py b/client/ayon_core/pipeline/create/legacy_create.py index cf78f5b061..83a1636bca 100644 --- a/client/ayon_core/pipeline/create/legacy_create.py +++ b/client/ayon_core/pipeline/create/legacy_create.py @@ -124,7 +124,7 @@ class LegacyCreator(object): @classmethod def get_product_name( - cls, variant, task_name, asset_id, project_name, host_name=None + cls, project_name, asset_id, task_name, variant, host_name=None ): """Return product name created with entered arguments. @@ -136,10 +136,10 @@ class LegacyCreator(object): By default is output concatenated product type with variant. 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. + asset_id (str): Folder id. + task_name (str): Context's task name. + variant (str): What is entered by user in creator tool. host_name (str): Name of host. Returns: diff --git a/client/ayon_core/pipeline/workfile/workfile_template_builder.py b/client/ayon_core/pipeline/workfile/workfile_template_builder.py index fb82cbbdaa..7dcaf1ad2a 100644 --- a/client/ayon_core/pipeline/workfile/workfile_template_builder.py +++ b/client/ayon_core/pipeline/workfile/workfile_template_builder.py @@ -1808,20 +1808,20 @@ class PlaceholderCreateMixin(object): ) assert asset_doc, "No current asset found in Session" product_name = creator_plugin.get_product_name( - create_variant, - task_name, + project_name, asset_doc["_id"], - project_name + task_name, + create_variant, ) else: asset_doc = get_asset_by_name(project_name, asset_name) assert asset_doc, "No current asset found in Session" product_name = creator_plugin.get_product_name( - create_variant, - task_name, - asset_doc, project_name, + asset_doc, + task_name, + create_variant, self.builder.host_name ) diff --git a/client/ayon_core/tools/creator/window.py b/client/ayon_core/tools/creator/window.py index 6194f019fc..7bf65ea510 100644 --- a/client/ayon_core/tools/creator/window.py +++ b/client/ayon_core/tools/creator/window.py @@ -244,7 +244,7 @@ class CreatorWindow(QtWidgets.QDialog): # Calculate product name with Creator plugin product_name = creator_plugin.get_product_name( - user_input_text, task_name, folder_id, project_name + project_name, folder_id, task_name, user_input_text ) # Force replacement of prohibited symbols # QUESTION should Creator care about this and here should be only diff --git a/client/ayon_core/tools/publisher/control.py b/client/ayon_core/tools/publisher/control.py index db7bf7db69..dd100aae81 100644 --- a/client/ayon_core/tools/publisher/control.py +++ b/client/ayon_core/tools/publisher/control.py @@ -2098,7 +2098,7 @@ class PublisherController(BasePublisherController): instance = self.instances[instance_id] return creator.get_product_name( - variant, task_name, asset_doc, project_name, instance=instance + project_name, asset_doc, task_name, variant, instance=instance ) def trigger_convertor_items(self, convertor_identifiers): From 4e2ae40a9319a648f0b0d5c8473d1f91b1c9b512 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 27 Feb 2024 11:18:03 +0100 Subject: [PATCH 325/573] change 'get_dynamic_data' arguments to be same as in 'get_product_name' --- .../plugins/create/create_render.py | 5 ++-- .../plugins/create/workfile_creator.py | 8 ++++-- .../blender/plugins/create/create_workfile.py | 8 +++--- .../fusion/plugins/create/create_workfile.py | 9 +++++-- .../plugins/create/create_staticmesh.py | 4 +-- .../houdini/plugins/create/create_workfile.py | 8 ++++-- .../max/plugins/create/create_workfile.py | 8 ++++-- client/ayon_core/hosts/maya/api/plugin.py | 2 +- .../create/create_unreal_skeletalmesh.py | 4 +-- .../create/create_unreal_staticmesh.py | 4 +-- .../maya/plugins/create/create_workfile.py | 8 ++++-- .../nuke/plugins/create/workfile_creator.py | 8 ++++-- client/ayon_core/hosts/photoshop/lib.py | 8 ++++-- .../photoshop/plugins/create/create_image.py | 11 ++++++-- client/ayon_core/hosts/tvpaint/api/plugin.py | 2 +- .../tvpaint/plugins/create/create_render.py | 27 ++++++++++++++----- .../pipeline/create/creator_plugins.py | 15 +++++++++-- .../pipeline/create/legacy_create.py | 4 +-- 18 files changed, 103 insertions(+), 40 deletions(-) diff --git a/client/ayon_core/hosts/aftereffects/plugins/create/create_render.py b/client/ayon_core/hosts/aftereffects/plugins/create/create_render.py index d7f7040953..93aec33222 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/create/create_render.py +++ b/client/ayon_core/hosts/aftereffects/plugins/create/create_render.py @@ -217,8 +217,9 @@ class RenderCreator(Creator): that would cause an issue in published file names. """ - def get_dynamic_data(self, variant, task_name, asset_doc, - project_name, host_name, instance): + def get_dynamic_data( + self, project_name, asset_doc, task_name, variant, host_name, instance + ): dynamic_data = {} if instance is not None: composition_name = instance.get("composition_name") diff --git a/client/ayon_core/hosts/aftereffects/plugins/create/workfile_creator.py b/client/ayon_core/hosts/aftereffects/plugins/create/workfile_creator.py index 41eeca8006..282e06d0bf 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/create/workfile_creator.py +++ b/client/ayon_core/hosts/aftereffects/plugins/create/workfile_creator.py @@ -62,8 +62,12 @@ class AEWorkfileCreator(AutoCreator): "variant": self.default_variant, } data.update(self.get_dynamic_data( - self.default_variant, task_name, asset_doc, - project_name, host_name, None + project_name, + asset_doc, + task_name, + self.default_variant, + host_name, + None, )) new_instance = CreatedInstance( diff --git a/client/ayon_core/hosts/blender/plugins/create/create_workfile.py b/client/ayon_core/hosts/blender/plugins/create/create_workfile.py index 696f317305..ead3ed7749 100644 --- a/client/ayon_core/hosts/blender/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/blender/plugins/create/create_workfile.py @@ -47,7 +47,7 @@ class CreateWorkfile(BaseCreator, AutoCreator): project_name, asset_doc, task_name, - self.default_variant, + task_name, host_name, ) data = { @@ -57,10 +57,10 @@ class CreateWorkfile(BaseCreator, AutoCreator): } data.update( self.get_dynamic_data( - task_name, - task_name, - asset_doc, project_name, + asset_doc, + task_name, + task_name, host_name, workfile_instance, ) diff --git a/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py b/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py index 15ed63c313..dfd9da3df1 100644 --- a/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py @@ -91,8 +91,13 @@ class FusionWorkfileCreator(AutoCreator): "variant": self.default_variant, } data.update(self.get_dynamic_data( - self.default_variant, task_name, asset_doc, - project_name, host_name, None + project_name, + asset_doc, + task_name, + self.default_variant, + host_name, + None + )) new_instance = CreatedInstance( diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_staticmesh.py b/client/ayon_core/hosts/houdini/plugins/create/create_staticmesh.py index f96c3faabb..bc8a2507cd 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_staticmesh.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_staticmesh.py @@ -88,14 +88,14 @@ class CreateStaticMesh(plugin.HoudiniCreator): return attrs + [createsubnetroot, vcformat, convert_units] def get_dynamic_data( - self, variant, task_name, asset_doc, project_name, host_name, instance + self, project_name, asset_doc, task_name, variant, host_name, instance ): """ The default prodcut name templates for Unreal include {asset} and thus we should pass that along as dynamic data. """ dynamic_data = super(CreateStaticMesh, self).get_dynamic_data( - variant, task_name, asset_doc, project_name, host_name, instance + project_name, asset_doc, task_name, variant, host_name, instance ) dynamic_data["asset"] = asset_doc["name"] return dynamic_data diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py b/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py index d66b7f9b94..631ef6ce77 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py @@ -52,8 +52,12 @@ class CreateWorkfile(plugin.HoudiniCreatorBase, AutoCreator): data.update( self.get_dynamic_data( - variant, task_name, asset_doc, - project_name, host_name, current_instance) + project_name, + asset_doc, + task_name, + variant, + host_name, + current_instance) ) self.log.info("Auto-creating workfile instance...") current_instance = CreatedInstance( diff --git a/client/ayon_core/hosts/max/plugins/create/create_workfile.py b/client/ayon_core/hosts/max/plugins/create/create_workfile.py index 784a90a246..1552149413 100644 --- a/client/ayon_core/hosts/max/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/max/plugins/create/create_workfile.py @@ -45,8 +45,12 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): data.update( self.get_dynamic_data( - variant, task_name, asset_doc, - project_name, host_name, current_instance) + project_name, + asset_doc, + task_name, + variant, + host_name, + current_instance) ) self.log.info("Auto-creating workfile instance...") instance_node = self.create_node(product_name) diff --git a/client/ayon_core/hosts/maya/api/plugin.py b/client/ayon_core/hosts/maya/api/plugin.py index 279bd0dcb5..c3bc879a4d 100644 --- a/client/ayon_core/hosts/maya/api/plugin.py +++ b/client/ayon_core/hosts/maya/api/plugin.py @@ -590,7 +590,7 @@ class RenderlayerCreator(NewCreator, MayaCreatorBase): if host_name is None: host_name = self.create_context.host_name dynamic_data = self.get_dynamic_data( - variant, task_name, asset_doc, project_name, host_name, instance + project_name, asset_doc, task_name, variant, host_name, instance ) # creator.product_type != 'render' as expected return get_product_name( diff --git a/client/ayon_core/hosts/maya/plugins/create/create_unreal_skeletalmesh.py b/client/ayon_core/hosts/maya/plugins/create/create_unreal_skeletalmesh.py index 783d80412b..8815c4d23d 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_unreal_skeletalmesh.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_unreal_skeletalmesh.py @@ -28,14 +28,14 @@ class CreateUnrealSkeletalMesh(plugin.MayaCreator): self.joint_hints = set(settings.get("joint_hints", [])) def get_dynamic_data( - self, variant, task_name, asset_doc, project_name, host_name, instance + self, project_name, asset_doc, task_name, variant, host_name, instance ): """ The default product name templates for Unreal include {asset} and thus we should pass that along as dynamic data. """ dynamic_data = super(CreateUnrealSkeletalMesh, self).get_dynamic_data( - variant, task_name, asset_doc, project_name, host_name, instance + project_name, asset_doc, task_name, variant, host_name, instance ) dynamic_data["asset"] = asset_doc["name"] return dynamic_data diff --git a/client/ayon_core/hosts/maya/plugins/create/create_unreal_staticmesh.py b/client/ayon_core/hosts/maya/plugins/create/create_unreal_staticmesh.py index 4172168497..58ad1e4133 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_unreal_staticmesh.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_unreal_staticmesh.py @@ -21,14 +21,14 @@ class CreateUnrealStaticMesh(plugin.MayaCreator): self.collision_prefixes = settings["collision_prefixes"] def get_dynamic_data( - self, variant, task_name, asset_doc, project_name, host_name, instance + self, project_name, asset_doc, task_name, variant, host_name, instance ): """ The default product name templates for Unreal include {asset} and thus we should pass that along as dynamic data. """ dynamic_data = super(CreateUnrealStaticMesh, self).get_dynamic_data( - variant, task_name, asset_doc, project_name, host_name, instance + project_name, asset_doc, task_name, variant, host_name, instance ) dynamic_data["asset"] = asset_doc["name"] return dynamic_data diff --git a/client/ayon_core/hosts/maya/plugins/create/create_workfile.py b/client/ayon_core/hosts/maya/plugins/create/create_workfile.py index 3c238a5b0e..5eb32e1c90 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_workfile.py @@ -50,8 +50,12 @@ class CreateWorkfile(plugin.MayaCreatorBase, AutoCreator): } data.update( self.get_dynamic_data( - variant, task_name, asset_doc, - project_name, host_name, current_instance) + project_name, + asset_doc, + task_name, + variant, + host_name, + current_instance) ) self.log.info("Auto-creating workfile instance...") current_instance = CreatedInstance( diff --git a/client/ayon_core/hosts/nuke/plugins/create/workfile_creator.py b/client/ayon_core/hosts/nuke/plugins/create/workfile_creator.py index 8fe2990058..0a0467787a 100644 --- a/client/ayon_core/hosts/nuke/plugins/create/workfile_creator.py +++ b/client/ayon_core/hosts/nuke/plugins/create/workfile_creator.py @@ -45,8 +45,12 @@ class WorkfileCreator(AutoCreator): "variant": self.default_variant }) instance_data.update(self.get_dynamic_data( - self.default_variant, task_name, asset_doc, - project_name, host_name, instance_data + project_name, + asset_doc, + task_name, + self.default_variant, + host_name, + instance_data )) instance = CreatedInstance( diff --git a/client/ayon_core/hosts/photoshop/lib.py b/client/ayon_core/hosts/photoshop/lib.py index e9b7b3f804..6d5be48bc2 100644 --- a/client/ayon_core/hosts/photoshop/lib.py +++ b/client/ayon_core/hosts/photoshop/lib.py @@ -64,8 +64,12 @@ class PSAutoCreator(AutoCreator): "variant": self.default_variant } data.update(self.get_dynamic_data( - self.default_variant, task_name, asset_doc, - project_name, host_name, None + project_name, + asset_doc, + task_name, + self.default_variant, + host_name, + None )) if not self.active_on_create: diff --git a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py b/client/ayon_core/hosts/photoshop/plugins/create/create_image.py index b524245243..8806aad33c 100644 --- a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/create/create_image.py @@ -244,8 +244,15 @@ class ImageCreator(Creator): return item.replace(stub.PUBLISH_ICON, '').replace(stub.LOADED_ICON, '') - def get_dynamic_data(self, variant, task_name, asset_doc, - project_name, host_name, instance): + def get_dynamic_data( + self, + project_name, + asset_doc, + task_name, + variant, + host_name, + instance + ): if instance is not None: layer_name = instance.get("layer_name") if layer_name: diff --git a/client/ayon_core/hosts/tvpaint/api/plugin.py b/client/ayon_core/hosts/tvpaint/api/plugin.py index bdd6740020..ef9f82b783 100644 --- a/client/ayon_core/hosts/tvpaint/api/plugin.py +++ b/client/ayon_core/hosts/tvpaint/api/plugin.py @@ -63,7 +63,7 @@ class TVPaintCreatorCommon: instance=None ): dynamic_data = self.get_dynamic_data( - variant, task_name, asset_doc, project_name, host_name, instance + project_name, asset_doc, task_name, variant, host_name, instance ) return get_product_name( diff --git a/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py b/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py index d226e786d5..dc53ccb9ca 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py +++ b/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py @@ -149,10 +149,10 @@ class CreateRenderlayer(TVPaintCreator): self.mark_for_review = plugin_settings["mark_for_review"] def get_dynamic_data( - self, variant, task_name, asset_doc, project_name, host_name, instance + self, project_name, asset_doc, task_name, variant, host_name, instance ): dynamic_data = super().get_dynamic_data( - variant, task_name, asset_doc, project_name, host_name, instance + project_name, asset_doc, task_name, variant, host_name, instance ) dynamic_data["renderpass"] = self.default_pass_name dynamic_data["renderlayer"] = variant @@ -425,10 +425,10 @@ class CreateRenderPass(TVPaintCreator): self._add_instance_to_context(instance) def get_dynamic_data( - self, variant, task_name, asset_doc, project_name, host_name, instance + self, project_name, asset_doc, task_name, variant, host_name, instance ): dynamic_data = super().get_dynamic_data( - variant, task_name, asset_doc, project_name, host_name, instance + project_name, asset_doc, task_name, variant, host_name, instance ) dynamic_data["renderpass"] = variant dynamic_data["renderlayer"] = "{renderlayer}" @@ -1044,8 +1044,23 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): self.active_on_create = plugin_settings["active_on_create"] self.default_pass_name = plugin_settings["default_pass_name"] - def get_dynamic_data(self, variant, *args, **kwargs): - dynamic_data = super().get_dynamic_data(variant, *args, **kwargs) + def get_dynamic_data( + self, + project_name, + asset_doc, + task_name, + variant, + host_name, + instance + ): + dynamic_data = super().get_dynamic_data( + project_name, + asset_doc, + task_name, + variant, + host_name, + instance + ) dynamic_data["renderpass"] = "{renderpass}" dynamic_data["renderlayer"] = variant return dynamic_data diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index ee6c558f69..886f117dbb 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -497,7 +497,13 @@ class BaseCreator: return self.icon def get_dynamic_data( - self, variant, task_name, asset_doc, project_name, host_name, instance + self, + project_name, + asset_doc, + task_name, + variant, + host_name, + instance ): """Dynamic data for product name filling. @@ -546,7 +552,12 @@ class BaseCreator: if host_name is None: host_name = self.create_context.host_name dynamic_data = self.get_dynamic_data( - variant, task_name, asset_doc, project_name, host_name, instance + project_name, + asset_doc, + task_name, + variant, + host_name, + instance ) return get_product_name( diff --git a/client/ayon_core/pipeline/create/legacy_create.py b/client/ayon_core/pipeline/create/legacy_create.py index 83a1636bca..bb9259d76f 100644 --- a/client/ayon_core/pipeline/create/legacy_create.py +++ b/client/ayon_core/pipeline/create/legacy_create.py @@ -89,7 +89,7 @@ class LegacyCreator(object): @classmethod def get_dynamic_data( - cls, variant, task_name, asset_id, project_name, host_name + cls, project_name, asset_id, task_name, variant, host_name ): """Return dynamic data for current Creator plugin. @@ -148,7 +148,7 @@ class LegacyCreator(object): """ dynamic_data = cls.get_dynamic_data( - variant, task_name, asset_id, project_name, host_name + project_name, asset_id, task_name, variant, host_name ) asset_doc = get_asset_by_id( From c4948f7723993a6a949311a87d0d46148bc248da Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 27 Feb 2024 11:27:15 +0100 Subject: [PATCH 326/573] remove 'Return' from cache method docstrings --- client/ayon_core/hosts/blender/api/plugin.py | 2 -- client/ayon_core/hosts/houdini/api/plugin.py | 3 --- client/ayon_core/hosts/maya/api/plugin.py | 3 --- client/ayon_core/hosts/unreal/api/plugin.py | 4 ---- 4 files changed, 12 deletions(-) diff --git a/client/ayon_core/hosts/blender/api/plugin.py b/client/ayon_core/hosts/blender/api/plugin.py index 447585e0a8..5f9cb4a830 100644 --- a/client/ayon_core/hosts/blender/api/plugin.py +++ b/client/ayon_core/hosts/blender/api/plugin.py @@ -175,8 +175,6 @@ class BaseCreator(Creator): Args: shared_data(Dict[str, Any]): Shared data. - Return: - Dict[str, Any]: Shared data with cached products. """ if not shared_data.get('blender_cached_instances'): cache = {} diff --git a/client/ayon_core/hosts/houdini/api/plugin.py b/client/ayon_core/hosts/houdini/api/plugin.py index 2dd06d8d5e..d819f6c618 100644 --- a/client/ayon_core/hosts/houdini/api/plugin.py +++ b/client/ayon_core/hosts/houdini/api/plugin.py @@ -112,9 +112,6 @@ class HoudiniCreatorBase(object): Args: Dict[str, Any]: Shared data. - Return: - Dict[str, Any]: Shared data dictionary. - """ if shared_data.get("houdini_cached_instances") is None: cache = dict() diff --git a/client/ayon_core/hosts/maya/api/plugin.py b/client/ayon_core/hosts/maya/api/plugin.py index c3bc879a4d..d272124584 100644 --- a/client/ayon_core/hosts/maya/api/plugin.py +++ b/client/ayon_core/hosts/maya/api/plugin.py @@ -103,9 +103,6 @@ class MayaCreatorBase(object): Args: Dict[str, Any]: Shared data. - Return: - Dict[str, Any]: Shared data dictionary. - """ if shared_data.get("maya_cached_instance_data") is None: cache = dict() diff --git a/client/ayon_core/hosts/unreal/api/plugin.py b/client/ayon_core/hosts/unreal/api/plugin.py index f379291742..f31c7c46b9 100644 --- a/client/ayon_core/hosts/unreal/api/plugin.py +++ b/client/ayon_core/hosts/unreal/api/plugin.py @@ -49,11 +49,7 @@ class UnrealBaseCreator(Creator): Args: Dict[str, Any]: Shared data. - Return: - Dict[str, Any]: Shared data dictionary. - """ - if "unreal_cached_instances" in shared_data: return From d9a278e470af8f7542dd53e9f3c3976ccd6c3c00 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 27 Feb 2024 18:42:48 +0800 Subject: [PATCH 327/573] correct object name for the error message --- .../ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py b/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py index 3e966acc87..a55b49b515 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py @@ -41,5 +41,5 @@ class ValidateMeshHasUVs(pyblish.api.InstancePlugin, title="Mesh has missing UVs", message="Model meshes are required to have UVs.

" "Meshes detected with invalid or missing UVs:
" - "{0}".format(invalid) + "{0}".format([err.name for err in invalid]) ) From f3ee01ba72cf25c9c1a9f8c540521025312ff619 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 27 Feb 2024 18:53:27 +0800 Subject: [PATCH 328/573] edit docstring --- .../hosts/max/plugins/publish/validate_mesh_has_uv.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py b/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py index a55b49b515..eda638daae 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py @@ -14,10 +14,9 @@ class ValidateMeshHasUVs(pyblish.api.InstancePlugin, """Validate the current mesh has UVs. - It validates whether the current UV set has non-zero UVs and - at least more than the vertex count. It's not really bulletproof, - but a simple quick validation to check if there are likely - UVs for every face. + It validates whether the current mesh has texture vertex(s). + If the mesh does not have texture vertex(s), it does not + have UVs in Max. """ order = ValidateMeshOrder From de5601e5abbf2ce653fac2bcf8fbab123f326ad9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 27 Feb 2024 12:03:18 +0100 Subject: [PATCH 329/573] keep initialization of ActionItem untouched --- client/ayon_core/tools/launcher/models/actions.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/tools/launcher/models/actions.py b/client/ayon_core/tools/launcher/models/actions.py index 8246bfa007..b975eb36f1 100644 --- a/client/ayon_core/tools/launcher/models/actions.py +++ b/client/ayon_core/tools/launcher/models/actions.py @@ -507,13 +507,11 @@ class ActionsModel: if is_application: action.project_entities[project_name] = project_entity action.project_settings[project_name] = project_settings + label = action.label or identifier variant_label = getattr(action, "label_variant", None) icon = get_action_icon(action) - host_name = identifier.replace("application.", "").split("/")[0] - force_not_open_workfile = not self.should_start_last_workfile( - project_name, host_name, None - ) + item = ActionItem( identifier, label, @@ -521,7 +519,7 @@ class ActionsModel: icon, action.order, is_application, - force_not_open_workfile + False ) action_items[identifier] = item self._action_items[project_name] = action_items From b7889303c0d54c8ecbd112f6ec662f9762030dff Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 27 Feb 2024 12:04:08 +0100 Subject: [PATCH 330/573] move all the logic to '_should_start_last_workfile' --- .../tools/launcher/models/actions.py | 71 +++++++++---------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/client/ayon_core/tools/launcher/models/actions.py b/client/ayon_core/tools/launcher/models/actions.py index b975eb36f1..59f6897200 100644 --- a/client/ayon_core/tools/launcher/models/actions.py +++ b/client/ayon_core/tools/launcher/models/actions.py @@ -293,9 +293,19 @@ class ActionsModel: self._get_action_objects() self._controller.emit_event("actions.refresh.finished") - def should_start_last_workfile(self, project_name, host_name, task_id): + def _should_start_last_workfile( + self, + project_name, + task_id, + identifier, + host_name, + not_open_workfile_actions + ): from ayon_core.lib.applications import should_start_last_workfile + if identifier in not_open_workfile_actions: + return not_open_workfile_actions[identifier] + task_name = None task_type = None if task_id is not None: @@ -303,12 +313,14 @@ class ActionsModel: task_name = task["name"] task_type = task["taskType"] - return should_start_last_workfile( + output = should_start_last_workfile( project_name, host_name, task_name, task_type ) + not_open_workfile_actions[identifier] = output + return output def get_action_items(self, project_name, folder_id, task_id): """Get actions for project. @@ -334,23 +346,16 @@ class ActionsModel: # Handling of 'force_not_open_workfile' for applications if action_item.is_application: action_item = action_item.copy() - - if identifier in not_open_workfile_actions: - action_item.force_not_open_workfile = ( - not_open_workfile_actions[identifier] - ) - else: - host_name = action_item.identifier.replace( - "application.", "" - ).split("/")[0] - start_last_workfile_default = ( - self.should_start_last_workfile( - project_name, host_name, task_id - ) - ) - action_item.force_not_open_workfile = ( - not start_last_workfile_default - ) + start_last_workfile = self._should_start_last_workfile( + project_name, + task_id, + identifier, + action.application.host_name, + not_open_workfile_actions + ) + action_item.force_not_open_workfile = ( + not start_last_workfile + ) output.append(action_item) return output @@ -389,26 +394,14 @@ class ActionsModel: per_action = self._get_no_last_workfile_for_context( project_name, folder_id, task_id ) - action.data["start_last_workfile"] = True - - force_not_open_workfile = None - if identifier in per_action: - force_not_open_workfile = per_action[identifier] - else: - host_name = action_item.identifier.replace( - "application.", "" - ).split("/")[0] - start_last_workfile_default = ( - self.should_start_last_workfile( - project_name, host_name, task_id - ) - ) - force_not_open_workfile = per_action.get( - identifier, not start_last_workfile_default - ) - - if force_not_open_workfile: - action.data["start_last_workfile"] = False + start_last_workfile = self._should_start_last_workfile( + project_name, + task_id, + identifier, + action.application.host_name, + per_action + ) + action.data["start_last_workfile"] = start_last_workfile action.process(session) except Exception as exc: From 3addd98805782356677c23303d62f992c28a4e1b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 27 Feb 2024 13:59:16 +0100 Subject: [PATCH 331/573] Add LoadError handling in ClipLoader class ClipLoader class in the plugin file now handles LoadError exceptions for creating media pool items when processing files. This change ensures proper error handling during media item creation. --- client/ayon_core/hosts/resolve/api/plugin.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/resolve/api/plugin.py b/client/ayon_core/hosts/resolve/api/plugin.py index bcdc30d20f..e95c9da82d 100644 --- a/client/ayon_core/hosts/resolve/api/plugin.py +++ b/client/ayon_core/hosts/resolve/api/plugin.py @@ -11,6 +11,7 @@ from ayon_core.pipeline import ( LoaderPlugin, Anatomy ) +from ayon_core.pipeline.load import LoadError from . import lib from .menu import load_stylesheet @@ -406,7 +407,7 @@ class ClipLoader: self.active_bin ) - assert media_pool_item, AssertionError( + assert media_pool_item, LoadError( "Cannot create media pool item for files: `{}`".format(files) ) @@ -480,6 +481,11 @@ class ClipLoader: files, self.active_bin ) + + assert media_pool_item, LoadError( + "Cannot create media pool item for files: `{}`".format(files) + ) + _clip_property = media_pool_item.GetClipProperty source_in = int(_clip_property("Start")) From 06ae29761440788cb47ea0f8477ccedd48a444f9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 27 Feb 2024 14:10:34 +0100 Subject: [PATCH 332/573] do not cache all values and fix output value --- client/ayon_core/tools/launcher/models/actions.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/ayon_core/tools/launcher/models/actions.py b/client/ayon_core/tools/launcher/models/actions.py index 59f6897200..463d363e3a 100644 --- a/client/ayon_core/tools/launcher/models/actions.py +++ b/client/ayon_core/tools/launcher/models/actions.py @@ -304,7 +304,7 @@ class ActionsModel: from ayon_core.lib.applications import should_start_last_workfile if identifier in not_open_workfile_actions: - return not_open_workfile_actions[identifier] + return not not_open_workfile_actions[identifier] task_name = None task_type = None @@ -319,7 +319,6 @@ class ActionsModel: task_name, task_type ) - not_open_workfile_actions[identifier] = output return output def get_action_items(self, project_name, folder_id, task_id): From 5b04bf1c6c4a2295103e892b323ace4f3ba9731f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 27 Feb 2024 14:11:02 +0100 Subject: [PATCH 333/573] update view on refresh --- client/ayon_core/tools/launcher/ui/actions_widget.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/ayon_core/tools/launcher/ui/actions_widget.py b/client/ayon_core/tools/launcher/ui/actions_widget.py index 6667b4ed5f..617f3b0c91 100644 --- a/client/ayon_core/tools/launcher/ui/actions_widget.py +++ b/client/ayon_core/tools/launcher/ui/actions_widget.py @@ -358,6 +358,8 @@ class ActionsWidget(QtWidgets.QWidget): def _on_model_refresh(self): self._proxy_model.sort(0) + # Force repaint all items + self._view.update() def _on_animation(self): time_now = time.time() From 01e69fa32119152d8969baf4ae59da97686fc86f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 27 Feb 2024 14:23:24 +0100 Subject: [PATCH 334/573] remove usage of system settings --- client/ayon_core/hooks/pre_ocio_hook.py | 2 +- client/ayon_core/hosts/flame/api/plugin.py | 2 +- .../hosts/fusion/plugins/load/load_usd.py | 5 +-- .../hosts/hiero/plugins/load/load_clip.py | 2 +- .../validate_unreal_staticmesh_naming.py | 2 +- client/ayon_core/hosts/maya/api/plugin.py | 4 +- .../maya/plugins/publish/collect_render.py | 2 +- .../maya/plugins/publish/extract_look.py | 8 ++-- .../hosts/substancepainter/api/pipeline.py | 6 +-- .../plugins/create/create_colorspace_look.py | 2 +- .../plugins/load/load_layout_existing.py | 4 +- client/ayon_core/lib/applications.py | 20 ++++------ .../collect_deadline_server_from_instance.py | 2 +- .../deadline/plugins/publish/collect_pools.py | 2 +- .../plugins/publish/submit_max_deadline.py | 2 +- .../plugins/publish/submit_maya_deadline.py | 2 +- client/ayon_core/modules/job_queue/addon.py | 6 +-- .../plugins/publish/start_timer.py | 6 +-- .../plugins/publish/stop_timer.py | 6 +-- client/ayon_core/pipeline/context_tools.py | 15 ++++---- client/ayon_core/pipeline/create/context.py | 7 +--- .../pipeline/create/creator_plugins.py | 38 +++---------------- .../pipeline/create/legacy_create.py | 2 +- client/ayon_core/pipeline/load/plugins.py | 7 ++-- client/ayon_core/pipeline/publish/lib.py | 25 ++---------- client/ayon_core/pipeline/template_data.py | 24 ++++++------ .../workfile/workfile_template_builder.py | 13 +------ .../publish/collect_anatomy_context_data.py | 8 +++- .../plugins/publish/collect_settings.py | 6 +-- client/ayon_core/tools/pyblish_pype/model.py | 10 +---- 30 files changed, 83 insertions(+), 157 deletions(-) diff --git a/client/ayon_core/hooks/pre_ocio_hook.py b/client/ayon_core/hooks/pre_ocio_hook.py index 00ba9a3bcb..fc131baada 100644 --- a/client/ayon_core/hooks/pre_ocio_hook.py +++ b/client/ayon_core/hooks/pre_ocio_hook.py @@ -30,7 +30,7 @@ class OCIOEnvHook(PreLaunchHook): asset_name=self.data["asset_name"], task_name=self.data["task_name"], host_name=self.host_name, - system_settings=self.data["system_settings"] + settings=self.data["project_settings"] ) config_data = get_imageio_config( diff --git a/client/ayon_core/hosts/flame/api/plugin.py b/client/ayon_core/hosts/flame/api/plugin.py index 720e6792b0..c0e27b491d 100644 --- a/client/ayon_core/hosts/flame/api/plugin.py +++ b/client/ayon_core/hosts/flame/api/plugin.py @@ -704,7 +704,7 @@ class ClipLoader(LoaderPlugin): _mapping = None _host_settings = None - def apply_settings(cls, project_settings, system_settings): + def apply_settings(cls, project_settings): plugin_type_settings = ( project_settings diff --git a/client/ayon_core/hosts/fusion/plugins/load/load_usd.py b/client/ayon_core/hosts/fusion/plugins/load/load_usd.py index 9c61894d66..de56d040ac 100644 --- a/client/ayon_core/hosts/fusion/plugins/load/load_usd.py +++ b/client/ayon_core/hosts/fusion/plugins/load/load_usd.py @@ -28,9 +28,8 @@ class FusionLoadUSD(load.LoaderPlugin): tool_type = "uLoader" @classmethod - def apply_settings(cls, project_settings, system_settings): - super(FusionLoadUSD, cls).apply_settings(project_settings, - system_settings) + def apply_settings(cls, project_settings): + super(FusionLoadUSD, cls).apply_settings(project_settings) if cls.enabled: # Enable only in Fusion 18.5+ fusion = get_fusion_module() diff --git a/client/ayon_core/hosts/hiero/plugins/load/load_clip.py b/client/ayon_core/hosts/hiero/plugins/load/load_clip.py index b8c51e7536..37680eaf2d 100644 --- a/client/ayon_core/hosts/hiero/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/hiero/plugins/load/load_clip.py @@ -42,7 +42,7 @@ class LoadClip(phiero.SequenceLoader): clip_name_template = "{asset}_{subset}_{representation}" @classmethod - def apply_settings(cls, project_settings, system_settings): + def apply_settings(cls, project_settings): plugin_type_settings = ( project_settings .get("hiero", {}) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_unreal_staticmesh_naming.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_unreal_staticmesh_naming.py index dbee293074..a5b48a1fe3 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_unreal_staticmesh_naming.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_unreal_staticmesh_naming.py @@ -39,7 +39,7 @@ class ValidateUnrealStaticMeshName(pyblish.api.InstancePlugin, static_mesh_prefix = "" @classmethod - def apply_settings(cls, project_settings, system_settings): + def apply_settings(cls, project_settings): settings = ( project_settings["houdini"]["create"]["CreateStaticMesh"] diff --git a/client/ayon_core/hosts/maya/api/plugin.py b/client/ayon_core/hosts/maya/api/plugin.py index 7a01f1a174..e23c5e9d9d 100644 --- a/client/ayon_core/hosts/maya/api/plugin.py +++ b/client/ayon_core/hosts/maya/api/plugin.py @@ -630,8 +630,8 @@ class Loader(LoaderPlugin): load_settings = {} # defined in settings @classmethod - def apply_settings(cls, project_settings, system_settings): - super(Loader, cls).apply_settings(project_settings, system_settings) + def apply_settings(cls, project_settings): + super(Loader, cls).apply_settings(project_settings) cls.load_settings = project_settings['maya']['load'] def get_custom_namespace_and_group(self, context, options, loader_key): diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_render.py b/client/ayon_core/hosts/maya/plugins/publish/collect_render.py index f91cca1f38..0b3f1c9956 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/collect_render.py +++ b/client/ayon_core/hosts/maya/plugins/publish/collect_render.py @@ -293,7 +293,7 @@ class CollectMayaRender(pyblish.api.InstancePlugin): "colorspaceView": colorspace_data["view"], } - rr_settings = context.data["system_settings"]["royalrender"] + rr_settings = context.data["project_settings"]["royalrender"] if rr_settings["enabled"]: data["rrPathName"] = instance.data.get("rrPathName") self.log.debug(data["rrPathName"]) diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_look.py b/client/ayon_core/hosts/maya/plugins/publish/extract_look.py index 29390e2c7d..469608100d 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_look.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_look.py @@ -104,11 +104,10 @@ class TextureProcessor: log = logging.getLogger(self.__class__.__name__) self.log = log - def apply_settings(self, system_settings, project_settings): + def apply_settings(self, project_settings): """Apply OpenPype system/project settings to the TextureProcessor Args: - system_settings (dict): OpenPype system settings project_settings (dict): OpenPype project settings Returns: @@ -250,7 +249,7 @@ class MakeTX(TextureProcessor): super(MakeTX, self).__init__(log=log) self.extra_args = [] - def apply_settings(self, system_settings, project_settings): + def apply_settings(self, project_settings): # Allow extra maketx arguments from project settings args_settings = ( project_settings["maya"]["publish"] @@ -488,8 +487,7 @@ class ExtractLook(publish.Extractor): }.items(): if instance.data.get(key, False): processor = Processor(log=self.log) - processor.apply_settings(context.data["system_settings"], - context.data["project_settings"]) + processor.apply_settings(context.data["project_settings"]) processors.append(processor) if processors: diff --git a/client/ayon_core/hosts/substancepainter/api/pipeline.py b/client/ayon_core/hosts/substancepainter/api/pipeline.py index 03cb22136c..843c120d8e 100644 --- a/client/ayon_core/hosts/substancepainter/api/pipeline.py +++ b/client/ayon_core/hosts/substancepainter/api/pipeline.py @@ -14,7 +14,7 @@ import pyblish.api from ayon_core.host import HostBase, IWorkfileHost, ILoadHost, IPublishHost from ayon_core.settings import ( get_current_project_settings, - get_system_settings + get_project_settings, ) from ayon_core.pipeline.template_data import get_template_data_with_names @@ -252,9 +252,9 @@ class SubstanceHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): project_name = self.get_current_project_name() asset_name = self.get_current_asset_name() task_name = self.get_current_asset_name() - system_settings = get_system_settings() + project_settings = get_project_settings(project_name) formatting_data = get_template_data_with_names( - project_name, asset_name, task_name, system_settings + project_name, asset_name, task_name, project_settings ) anatomy = Anatomy(project_name) formatting_data["root"] = anatomy.roots diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py index 3969294f1e..b5bf6c1b11 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py @@ -148,7 +148,7 @@ This creator publishes color space look file (LUT). ) ] - def apply_settings(self, project_settings, system_settings): + def apply_settings(self, project_settings): host = self.create_context.host host_name = host.name project_name = host.get_current_project_name() diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py b/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py index 6f390b4920..6c10849c4e 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py @@ -33,9 +33,9 @@ class ExistingLayoutLoader(plugin.Loader): delete_unmatched_assets = True @classmethod - def apply_settings(cls, project_settings, *args, **kwargs): + def apply_settings(cls, project_settings): super(ExistingLayoutLoader, cls).apply_settings( - project_settings, *args, **kwargs + project_settings ) cls.delete_unmatched_assets = ( project_settings["unreal"]["delete_unmatched_assets"] diff --git a/client/ayon_core/lib/applications.py b/client/ayon_core/lib/applications.py index 02325a7d8a..92cb251d77 100644 --- a/client/ayon_core/lib/applications.py +++ b/client/ayon_core/lib/applications.py @@ -13,10 +13,7 @@ import six from ayon_core import AYON_CORE_ROOT from ayon_core.client import get_asset_name_identifier -from ayon_core.settings import ( - get_system_settings, - get_project_settings, -) +from ayon_core.settings import get_project_settings, get_studio_settings from .log import Logger from .profiles_filtering import filter_profiles from .local_settings import get_ayon_username @@ -405,7 +402,7 @@ class ApplicationManager: if self._studio_settings is not None: settings = copy.deepcopy(self._studio_settings) else: - settings = get_system_settings( + settings = get_studio_settings( clear_metadata=False, exclude_locals=False ) @@ -1398,8 +1395,9 @@ class EnvironmentPrepData(dict): if data.get("env") is None: data["env"] = os.environ.copy() - if "system_settings" not in data: - data["system_settings"] = get_system_settings() + project_name = data["project_doct"]["name"] + if "project_settings" not in data: + data["project_settings"] = get_project_settings(project_name) super(EnvironmentPrepData, self).__init__(data) @@ -1524,8 +1522,8 @@ def prepare_app_environments( # Use environments from local settings filtered_local_envs = {} # NOTE Overrides for environment variables are not implemented in AYON. - # system_settings = data["system_settings"] - # whitelist_envs = system_settings["general"].get("local_env_white_list") + # project_settings = data["project_settings"] + # whitelist_envs = project_settings["general"].get("local_env_white_list") # if whitelist_envs: # local_settings = get_local_settings() # local_envs = local_settings.get("environments") or {} @@ -1689,9 +1687,7 @@ def prepare_context_environments(data, env_group=None, addons_manager=None): # Load project specific environments project_name = project_doc["name"] project_settings = get_project_settings(project_name) - system_settings = get_system_settings() data["project_settings"] = project_settings - data["system_settings"] = system_settings app = data["app"] context_env = { @@ -1731,7 +1727,7 @@ def prepare_context_environments(data, env_group=None, addons_manager=None): ) workdir_data = get_template_data( - project_doc, asset_doc, task_name, app.host_name, system_settings + project_doc, asset_doc, task_name, app.host_name, project_settings ) data["workdir_data"] = workdir_data diff --git a/client/ayon_core/modules/deadline/plugins/publish/collect_deadline_server_from_instance.py b/client/ayon_core/modules/deadline/plugins/publish/collect_deadline_server_from_instance.py index 445971f2b0..ea4b7a213e 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/collect_deadline_server_from_instance.py +++ b/client/ayon_core/modules/deadline/plugins/publish/collect_deadline_server_from_instance.py @@ -46,7 +46,7 @@ class CollectDeadlineServerFromInstance(pyblish.api.InstancePlugin): from maya import cmds deadline_settings = ( render_instance.context.data - ["system_settings"] + ["project_settings"] ["deadline"] ) diff --git a/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py b/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py index 6c35012173..25951a56b6 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py +++ b/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py @@ -44,7 +44,7 @@ class CollectDeadlinePools(pyblish.api.InstancePlugin, secondary_pool = None @classmethod - def apply_settings(cls, project_settings, system_settings): + def apply_settings(cls, project_settings): # deadline.publish.CollectDeadlinePools settings = project_settings["deadline"]["publish"]["CollectDeadlinePools"] # noqa cls.primary_pool = settings.get("primary_pool", None) diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_max_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_max_deadline.py index 4eaaedac34..1abefa515a 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_max_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_max_deadline.py @@ -48,7 +48,7 @@ class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline, group = None @classmethod - def apply_settings(cls, project_settings, system_settings): + def apply_settings(cls, project_settings): settings = project_settings["deadline"]["publish"]["MaxSubmitDeadline"] # noqa # Take some defaults from settings diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_maya_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_maya_deadline.py index afa3a3dd14..0e871eb90e 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -116,7 +116,7 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline, strict_error_checking = True @classmethod - def apply_settings(cls, project_settings, system_settings): + def apply_settings(cls, project_settings): settings = project_settings["deadline"]["publish"]["MayaSubmitDeadline"] # noqa # Take some defaults from settings diff --git a/client/ayon_core/modules/job_queue/addon.py b/client/ayon_core/modules/job_queue/addon.py index b28e915ac0..32d06d0040 100644 --- a/client/ayon_core/modules/job_queue/addon.py +++ b/client/ayon_core/modules/job_queue/addon.py @@ -42,7 +42,7 @@ import copy import platform from ayon_core.addon import AYONAddon, click_wrap -from ayon_core.settings import get_system_settings +from ayon_core.settings import get_studio_settings class JobQueueAddon(AYONAddon): @@ -122,7 +122,7 @@ class JobQueueAddon(AYONAddon): @classmethod def get_jobs_root_from_settings(cls): - studio_settings = get_system_settings() + studio_settings = get_studio_settings() jobs_root_mapping = studio_settings.get(cls.name, {}).get("jobs_root") converted_mapping = cls._roots_mapping_conversion(jobs_root_mapping) @@ -152,7 +152,7 @@ class JobQueueAddon(AYONAddon): @classmethod def get_server_url_from_settings(cls): - studio_settings = get_system_settings() + studio_settings = get_studio_settings() return cls.url_conversion( studio_settings .get(cls.name, {}) diff --git a/client/ayon_core/modules/timers_manager/plugins/publish/start_timer.py b/client/ayon_core/modules/timers_manager/plugins/publish/start_timer.py index b551c01815..182efbc4ae 100644 --- a/client/ayon_core/modules/timers_manager/plugins/publish/start_timer.py +++ b/client/ayon_core/modules/timers_manager/plugins/publish/start_timer.py @@ -1,6 +1,6 @@ """ Requires: - context -> system_settings + context -> project_settings context -> ayonAddonsManager """ @@ -18,8 +18,8 @@ class StartTimer(pyblish.api.ContextPlugin): self.log.debug("TimersManager is disabled") return - studio_settings = context.data["system_settings"] - if not studio_settings["timers_manager"]["disregard_publishing"]: + project_settings = context.data["project_settings"] + if not project_settings["timers_manager"]["disregard_publishing"]: self.log.debug("Publish is not affecting running timers.") return diff --git a/client/ayon_core/modules/timers_manager/plugins/publish/stop_timer.py b/client/ayon_core/modules/timers_manager/plugins/publish/stop_timer.py index 9c3a63e78e..eafd8cb450 100644 --- a/client/ayon_core/modules/timers_manager/plugins/publish/stop_timer.py +++ b/client/ayon_core/modules/timers_manager/plugins/publish/stop_timer.py @@ -1,6 +1,6 @@ """ Requires: - context -> system_settings + context -> project_settings context -> ayonAddonsManager """ @@ -19,8 +19,8 @@ class StopTimer(pyblish.api.ContextPlugin): self.log.debug("TimersManager is disabled") return - studio_settings = context.data["system_settings"] - if not studio_settings["timers_manager"]["disregard_publishing"]: + project_settings = context.data["project_settings"] + if not project_settings["timers_manager"]["disregard_publishing"]: self.log.debug("Publish is not affecting running timers.") return diff --git a/client/ayon_core/pipeline/context_tools.py b/client/ayon_core/pipeline/context_tools.py index 7c0db0be27..336ba74707 100644 --- a/client/ayon_core/pipeline/context_tools.py +++ b/client/ayon_core/pipeline/context_tools.py @@ -460,14 +460,14 @@ def is_representation_from_latest(representation): return version_is_latest(project_name, representation["parent"]) -def get_template_data_from_session(session=None, system_settings=None): +def get_template_data_from_session(session=None, settings=None): """Template data for template fill from session keys. Args: session (Union[Dict[str, str], None]): The Session to use. If not provided use the currently active global Session. - system_settings (Union[Dict[str, Any], Any]): Prepared system settings. - Optional are auto received if not passed. + settings (Optional[Dict[str, Any]]): Prepared studio or project + settings. Returns: Dict[str, Any]: All available data from session. @@ -486,15 +486,16 @@ def get_template_data_from_session(session=None, system_settings=None): host_name = get_current_host_name() return get_template_data_with_names( - project_name, asset_name, task_name, host_name, system_settings + project_name, asset_name, task_name, host_name, settings ) -def get_current_context_template_data(system_settings=None): +def get_current_context_template_data(settings=None): """Prepare template data for current context. Args: - system_settings (Optional[Dict[str, Any]]): Prepared system settings. + settings (Optional[Dict[str, Any]]): Prepared studio or + project settings. Returns: Dict[str, Any] Template data for current context. @@ -507,7 +508,7 @@ def get_current_context_template_data(system_settings=None): host_name = get_current_host_name() return get_template_data_with_names( - project_name, asset_name, task_name, host_name, system_settings + project_name, asset_name, task_name, host_name, settings ) diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index c50170f070..3defedf7fa 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -16,10 +16,7 @@ from ayon_core.client import ( get_asset_by_name, get_asset_name_identifier, ) -from ayon_core.settings import ( - get_system_settings, - get_project_settings -) +from ayon_core.settings import get_project_settings from ayon_core.lib.attribute_definitions import ( UnknownDef, serialize_attr_defs, @@ -1774,7 +1771,6 @@ class CreateContext: def _reset_creator_plugins(self): # Prepare settings - system_settings = get_system_settings() project_settings = get_project_settings(self.project_name) # Discover and prepare creators @@ -1812,7 +1808,6 @@ class CreateContext: creator = creator_class( project_settings, - system_settings, self, self.headless ) diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index 6fa0d2ffa1..a48505f999 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -6,8 +6,8 @@ from abc import ABCMeta, abstractmethod import six -from ayon_core.settings import get_system_settings, get_project_settings -from ayon_core.lib import Logger, is_func_signature_supported +from ayon_core.settings import get_project_settings +from ayon_core.lib import Logger from ayon_core.pipeline.plugin_discover import ( discover, register_plugin, @@ -201,7 +201,7 @@ class BaseCreator: settings_name = None def __init__( - self, project_settings, system_settings, create_context, headless=False + self, project_settings, create_context, headless=False ): # Reference to CreateContext self.create_context = create_context @@ -211,34 +211,7 @@ class BaseCreator: # - we may use UI inside processing this attribute should be checked self.headless = headless - expect_system_settings = False - if is_func_signature_supported( - self.apply_settings, project_settings - ): - self.apply_settings(project_settings) - else: - expect_system_settings = True - # Backwards compatibility for system settings - self.apply_settings(project_settings, system_settings) - - init_use_base = any( - self.__class__.__init__ is cls.__init__ - for cls in { - BaseCreator, - Creator, - HiddenCreator, - AutoCreator, - } - ) - if not init_use_base or expect_system_settings: - self.log.warning(( - "WARNING: Source - Create plugin {}." - " System settings argument will not be passed to" - " '__init__' and 'apply_settings' methods in future versions" - " of OpenPype. Planned version to drop the support" - " is 3.16.6 or 3.17.0. Please contact Ynput core team if you" - " need to keep system settings." - ).format(self.__class__.__name__)) + self.apply_settings(project_settings) @staticmethod def _get_settings_values(project_settings, category_name, plugin_name): @@ -838,11 +811,10 @@ def discover_legacy_creator_plugins(): plugins = discover(LegacyCreator) project_name = get_current_project_name() - system_settings = get_system_settings() project_settings = get_project_settings(project_name) for plugin in plugins: try: - plugin.apply_settings(project_settings, system_settings) + plugin.apply_settings(project_settings) except Exception: log.warning( "Failed to apply settings to creator {}".format( diff --git a/client/ayon_core/pipeline/create/legacy_create.py b/client/ayon_core/pipeline/create/legacy_create.py index bc1528b9cd..d9105d95df 100644 --- a/client/ayon_core/pipeline/create/legacy_create.py +++ b/client/ayon_core/pipeline/create/legacy_create.py @@ -44,7 +44,7 @@ class LegacyCreator(object): self.data.update(data or {}) @classmethod - def apply_settings(cls, project_settings, system_settings): + def apply_settings(cls, project_settings): """Apply OpenPype settings to a plugin class.""" host_name = os.environ.get("AYON_HOST_NAME") diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 962417c6f2..82dab90c09 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -1,7 +1,7 @@ import os import logging -from ayon_core.settings import get_system_settings, get_project_settings +from ayon_core.settings import get_project_settings from ayon_core.pipeline import schema from ayon_core.pipeline.plugin_discover import ( discover, @@ -37,7 +37,7 @@ class LoaderPlugin(list): log.propagate = True @classmethod - def apply_settings(cls, project_settings, system_settings): + def apply_settings(cls, project_settings): host_name = os.environ.get("AYON_HOST_NAME") plugin_type = "load" plugin_type_settings = ( @@ -262,11 +262,10 @@ def discover_loader_plugins(project_name=None): plugins = discover(LoaderPlugin) if not project_name: project_name = get_current_project_name() - system_settings = get_system_settings() project_settings = get_project_settings(project_name) for plugin in plugins: try: - plugin.apply_settings(project_settings, system_settings) + plugin.apply_settings(project_settings) except Exception: log.warning( "Failed to apply settings to loader {}".format( diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 7d980b4bbe..dcf4afab4a 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -13,12 +13,8 @@ from ayon_core.lib import ( Logger, import_filepath, filter_profiles, - is_func_signature_supported, -) -from ayon_core.settings import ( - get_project_settings, - get_system_settings, ) +from ayon_core.settings import get_project_settings from ayon_core.pipeline import ( tempdir, Anatomy @@ -421,8 +417,8 @@ def apply_plugin_settings_automatically(plugin, settings, logger=None): def filter_pyblish_plugins(plugins): """Pyblish plugin filter which applies AYON settings. - Apply OpenPype settings on discovered plugins. On plugin with implemented - class method 'def apply_settings(cls, project_settings, system_settings)' + Apply settings on discovered plugins. On plugin with implemented + class method 'def apply_settings(cls, project_settings)' is called the method. Default behavior looks for plugin name and current host name to look for @@ -440,7 +436,6 @@ def filter_pyblish_plugins(plugins): project_name = os.environ.get("AYON_PROJECT_NAME") project_settings = get_project_settings(project_name) - system_settings = get_system_settings() # iterate over plugins for plugin in plugins[:]: @@ -452,19 +447,7 @@ def filter_pyblish_plugins(plugins): # - can be used to target settings from custom settings place # - skip default behavior when successful try: - # Support to pass only project settings - # - make sure that both settings are passed, when can be - # - that covers cases when *args are in method parameters - both_supported = is_func_signature_supported( - apply_settings_func, project_settings, system_settings - ) - project_supported = is_func_signature_supported( - apply_settings_func, project_settings - ) - if not both_supported and project_supported: - plugin.apply_settings(project_settings) - else: - plugin.apply_settings(project_settings, system_settings) + plugin.apply_settings(project_settings) except Exception: log.warning( diff --git a/client/ayon_core/pipeline/template_data.py b/client/ayon_core/pipeline/template_data.py index a5ca84c754..e9c57521d4 100644 --- a/client/ayon_core/pipeline/template_data.py +++ b/client/ayon_core/pipeline/template_data.py @@ -1,9 +1,9 @@ from ayon_core.client import get_project, get_asset_by_name -from ayon_core.settings import get_system_settings +from ayon_core.settings import get_studio_settings from ayon_core.lib.local_settings import get_ayon_username -def get_general_template_data(system_settings=None): +def get_general_template_data(settings=None): """General template data based on system settings or machine. Output contains formatting keys: @@ -12,12 +12,12 @@ def get_general_template_data(system_settings=None): - 'user' - User's name using 'get_ayon_username' Args: - system_settings (Dict[str, Any]): System settings. + settings (Dict[str, Any]): Studio or project settings. """ - if not system_settings: - system_settings = get_system_settings() - core_settings = system_settings["core"] + if not settings: + settings = get_studio_settings() + core_settings = settings["core"] return { "studio": { "name": core_settings["studio_name"], @@ -154,7 +154,7 @@ def get_template_data( asset_doc=None, task_name=None, host_name=None, - system_settings=None + settings=None ): """Prepare data for templates filling from entered documents and info. @@ -174,14 +174,14 @@ def get_template_data( asset_doc (Dict[str, Any]): Mongo document of asset from MongoDB. task_name (Union[str, None]): Task name under passed asset. host_name (Union[str, None]): Used to fill '{app}' key. - system_settings (Union[Dict, None]): Prepared system settings. + settings (Union[Dict, None]): Prepared studio or project settings. They're queried if not passed (may be slower). Returns: Dict[str, Any]: Data prepared for filling workdir template. """ - template_data = get_general_template_data(system_settings) + template_data = get_general_template_data(settings) template_data.update(get_project_template_data(project_doc)) if asset_doc: template_data.update(get_asset_template_data( @@ -203,7 +203,7 @@ def get_template_data_with_names( asset_name=None, task_name=None, host_name=None, - system_settings=None + settings=None ): """Prepare data for templates filling from entered entity names and info. @@ -218,7 +218,7 @@ def get_template_data_with_names( task_name (Union[str, None]): Task name under passed asset. host_name (Union[str, None]):Used to fill '{app}' key. because workdir template may contain `{app}` key. - system_settings (Union[Dict, None]): Prepared system settings. + settings (Union[Dict, None]): Prepared studio or project settings. They're queried if not passed. Returns: @@ -236,5 +236,5 @@ def get_template_data_with_names( fields=["name", "data.parents", "data.tasks"] ) return get_template_data( - project_doc, asset_doc, task_name, host_name, system_settings + project_doc, asset_doc, task_name, host_name, settings ) diff --git a/client/ayon_core/pipeline/workfile/workfile_template_builder.py b/client/ayon_core/pipeline/workfile/workfile_template_builder.py index 3004e55861..d07d870f6e 100644 --- a/client/ayon_core/pipeline/workfile/workfile_template_builder.py +++ b/client/ayon_core/pipeline/workfile/workfile_template_builder.py @@ -27,10 +27,7 @@ from ayon_core.client import ( get_representations, get_ayon_server_api_connection, ) -from ayon_core.settings import ( - get_project_settings, - get_system_settings, -) +from ayon_core.settings import get_project_settings from ayon_core.host import IWorkfileHost, HostBase from ayon_core.lib import ( Logger, @@ -118,7 +115,6 @@ class AbstractTemplateBuilder(object): self._creators_by_name = None self._create_context = None - self._system_settings = None self._project_settings = None self._current_asset_doc = None @@ -152,12 +148,6 @@ class AbstractTemplateBuilder(object): "task_name": self.current_task_name } - @property - def system_settings(self): - if self._system_settings is None: - self._system_settings = get_system_settings() - return self._system_settings - @property def project_settings(self): if self._project_settings is None: @@ -256,7 +246,6 @@ class AbstractTemplateBuilder(object): self._linked_asset_docs = None self._task_type = None - self._system_settings = None self._project_settings = None self.clear_shared_data() diff --git a/client/ayon_core/plugins/publish/collect_anatomy_context_data.py b/client/ayon_core/plugins/publish/collect_anatomy_context_data.py index fbdf26e76c..b5bb579498 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_context_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_context_data.py @@ -47,7 +47,7 @@ class CollectAnatomyContextData(pyblish.api.ContextPlugin): def process(self, context): host_name = context.data["hostName"] - system_settings = context.data["system_settings"] + project_settings = context.data["project_settings"] project_entity = context.data["projectEntity"] asset_entity = context.data.get("assetEntity") task_name = None @@ -55,7 +55,11 @@ class CollectAnatomyContextData(pyblish.api.ContextPlugin): task_name = context.data["task"] anatomy_data = get_template_data( - project_entity, asset_entity, task_name, host_name, system_settings + project_entity, + asset_entity, + task_name, + host_name, + project_settings ) anatomy_data.update(context.data.get("datetimeData") or {}) diff --git a/client/ayon_core/plugins/publish/collect_settings.py b/client/ayon_core/plugins/publish/collect_settings.py index 4e3331209d..66b89a114c 100644 --- a/client/ayon_core/plugins/publish/collect_settings.py +++ b/client/ayon_core/plugins/publish/collect_settings.py @@ -1,8 +1,5 @@ from pyblish import api -from ayon_core.settings import ( - get_current_project_settings, - get_system_settings, -) +from ayon_core.settings import get_current_project_settings class CollectSettings(api.ContextPlugin): @@ -13,4 +10,3 @@ class CollectSettings(api.ContextPlugin): def process(self, context): context.data["project_settings"] = get_current_project_settings() - context.data["system_settings"] = get_system_settings() diff --git a/client/ayon_core/tools/pyblish_pype/model.py b/client/ayon_core/tools/pyblish_pype/model.py index 4c91fb567f..3a402f386e 100644 --- a/client/ayon_core/tools/pyblish_pype/model.py +++ b/client/ayon_core/tools/pyblish_pype/model.py @@ -34,8 +34,6 @@ import qtawesome from six import text_type from .constants import PluginStates, InstanceStates, GroupStates, Roles -from ayon_core.settings import get_system_settings - # ItemTypes UserType = QtGui.QStandardItem.UserType @@ -105,12 +103,8 @@ class IntentModel(QtGui.QStandardItemModel): self._item_count = 0 self.default_index = 0 - intent_settings = ( - get_system_settings() - .get("modules", {}) - .get("ftrack", {}) - .get("intent", {}) - ) + # Intent settings are not available in core addon + intent_settings = {} items = intent_settings.get("items", {}) if not items: From fdb18f1f39d561e342a5cdc64dd40d9b3942e6a0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 27 Feb 2024 14:23:33 +0100 Subject: [PATCH 335/573] remove 'get_system_settings' function --- client/ayon_core/settings/__init__.py | 2 -- client/ayon_core/settings/lib.py | 4 ---- 2 files changed, 6 deletions(-) diff --git a/client/ayon_core/settings/__init__.py b/client/ayon_core/settings/__init__.py index d32b5f3391..ca76c550b8 100644 --- a/client/ayon_core/settings/__init__.py +++ b/client/ayon_core/settings/__init__.py @@ -1,7 +1,6 @@ from .lib import ( get_ayon_settings, get_studio_settings, - get_system_settings, get_project_settings, get_general_environments, get_current_project_settings, @@ -11,7 +10,6 @@ from .lib import ( __all__ = ( "get_ayon_settings", "get_studio_settings", - "get_system_settings", "get_general_environments", "get_project_settings", "get_current_project_settings", diff --git a/client/ayon_core/settings/lib.py b/client/ayon_core/settings/lib.py index eadf3ba544..69525d5b86 100644 --- a/client/ayon_core/settings/lib.py +++ b/client/ayon_core/settings/lib.py @@ -163,10 +163,6 @@ def get_studio_settings(*args, **kwargs): return _AyonSettingsCache.get_value_by_project(None) -# Backward compatibility -get_system_settings = get_studio_settings - - def get_project_settings(project_name, *args, **kwargs): return _AyonSettingsCache.get_value_by_project(project_name) From cd694266b8375e8b5e96974b869fe5360fcdf89c Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 27 Feb 2024 21:34:45 +0800 Subject: [PATCH 336/573] improve error message and docstring --- .../plugins/publish/validate_mesh_has_uv.py | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py b/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py index eda638daae..4cef1a5658 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py @@ -14,9 +14,12 @@ class ValidateMeshHasUVs(pyblish.api.InstancePlugin, """Validate the current mesh has UVs. - It validates whether the current mesh has texture vertex(s). - If the mesh does not have texture vertex(s), it does not + This validator only checks if the mesh has UVs but not + whether the faces of the mesh have UVs. + It validates whether the current mesh has texture vertices. + If the mesh does not have texture vertices, it does not have UVs in Max. + """ order = ValidateMeshOrder @@ -36,9 +39,18 @@ class ValidateMeshHasUVs(pyblish.api.InstancePlugin, def process(self, instance): invalid = self.get_invalid(instance) if invalid: - raise PublishValidationError( - title="Mesh has missing UVs", - message="Model meshes are required to have UVs.

" - "Meshes detected with invalid or missing UVs:
" - "{0}".format([err.name for err in invalid]) + bullet_point_invalid_statement = "\n".join( + "- {}".format(invalid.name) for invalid + in invalid ) + report = ( + "Model meshes are required to have UVs.\n\n" + "Meshes detected with invalid or missing UVs:\n" + f"{bullet_point_invalid_statement}\n\n" + ) + raise PublishValidationError( + report, + description=( + "Model meshes are required to have UVs.\n\n" + "Meshes detected with no texture vertice(s) or missing UVs"), + title="Mesh has missing UVs") From 254958f29bf7891c9fd41e23171f7dfbc6c4abe6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 27 Feb 2024 15:04:46 +0100 Subject: [PATCH 337/573] Add check for MediaPoolItem existence before processing. Improve data handling in timeline items retrieval function. - Added a check to skip processing if MediaPoolItem doesn't exist. - Enhanced data handling within the timeline items retrieval function. - this is for case some adjustment clips or fusion clips are on timeline --- client/ayon_core/hosts/resolve/api/lib.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ayon_core/hosts/resolve/api/lib.py b/client/ayon_core/hosts/resolve/api/lib.py index 5eb88afdcb..2d15c93f87 100644 --- a/client/ayon_core/hosts/resolve/api/lib.py +++ b/client/ayon_core/hosts/resolve/api/lib.py @@ -409,6 +409,9 @@ def get_current_timeline_items( } # get track item object and its color for clip_index, ti in enumerate(_clips[track_index]): + if not ti.GetMediaPoolItem(): + continue + data = _data.copy() data["clip"] = { "item": ti, From 9a95cde26b08165c6d31227f66897d562a269101 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 27 Feb 2024 23:06:11 +0100 Subject: [PATCH 338/573] Add trigger workflow to sync issues with ClickUp - Added a trigger workflow to sync GitHub issues with ClickUp - Triggered by manual dispatch or labeled issue event --- .../workflows/issue_to_clickup_trigger.yml | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/issue_to_clickup_trigger.yml diff --git a/.github/workflows/issue_to_clickup_trigger.yml b/.github/workflows/issue_to_clickup_trigger.yml new file mode 100644 index 0000000000..d3c026b4a5 --- /dev/null +++ b/.github/workflows/issue_to_clickup_trigger.yml @@ -0,0 +1,28 @@ +name: Sync Issues to ClickUp [trigger] + +on: + workflow_dispatch: + inputs: + issue-number: + required: true + issues: + types: [labeled] + + +jobs: + call-ci-tools-issue-sync: + if: github.event.inputs.issue-number != '' || github.event_name == 'issues' && contains(github.event.issue.labels.*.name, 'backlog') + uses: ynput/ci-tools/.github/workflows/issue_to_clickup_ref.yml@main + with: + # issue number should be taken either from inputs or from the event + issue-number: ${{ github.event.inputs.issue-number || github.event.issue.number }} + repo-owner: ${{ github.event.repository.owner.login }} + repo-name: ${{ github.event.repository.name }} + secrets: + token: ${{ secrets.YNPUT_BOT_TOKEN }} + cu_api_key: ${{ secrets.CLICKUP_API_KEY }} + cu_team_id: ${{ secrets.CLICKUP_TEAM_ID }} + cu_folder_id: ${{ secrets.CLICKUP_FOLDER_ID }} + cu_list_id: ${{ secrets.CLICKUP_LIST_ID }} + cu_field_domain_id: ${{ secrets.CLICKUP_DOMAIN_FIELD_ID }} + cu_field_type_id: ${{ secrets.CLICKUP_ISSUETYPE_FIELD_ID }} From 16b28fe3bd3956974550a55f31cdf073dd3c9c15 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Feb 2024 17:54:19 +0800 Subject: [PATCH 339/573] tweak docstring --- .../hosts/max/plugins/publish/validate_mesh_has_uv.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py b/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py index 4cef1a5658..846e537b69 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py @@ -15,7 +15,8 @@ class ValidateMeshHasUVs(pyblish.api.InstancePlugin, """Validate the current mesh has UVs. This validator only checks if the mesh has UVs but not - whether the faces of the mesh have UVs. + whether all the individual faces of the mesh have UVs. + It validates whether the current mesh has texture vertices. If the mesh does not have texture vertices, it does not have UVs in Max. @@ -52,5 +53,5 @@ class ValidateMeshHasUVs(pyblish.api.InstancePlugin, report, description=( "Model meshes are required to have UVs.\n\n" - "Meshes detected with no texture vertice(s) or missing UVs"), + "Meshes detected with no texture vertice or missing UVs"), title="Mesh has missing UVs") From d85b4ee90e4632f0a35075cee2ed6cd43eda81e8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Feb 2024 11:45:01 +0100 Subject: [PATCH 340/573] change AYON prefix in houdini --- client/ayon_core/hosts/houdini/api/plugin.py | 6 +++--- .../ayon_core/hosts/houdini/plugins/create/create_review.py | 2 +- .../hosts/houdini/plugins/publish/validate_subset_name.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/hosts/houdini/api/plugin.py b/client/ayon_core/hosts/houdini/api/plugin.py index d819f6c618..13cf3c9949 100644 --- a/client/ayon_core/hosts/houdini/api/plugin.py +++ b/client/ayon_core/hosts/houdini/api/plugin.py @@ -243,8 +243,8 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase): node_path = instance.path() node_data["instance_id"] = node_path node_data["instance_node"] = node_path - if "AYONProductName" in node_data: - node_data["productName"] = node_data.pop("AYONProductName") + if "AYON_productName" in node_data: + node_data["productName"] = node_data.pop("AYON_productName") created_instance = CreatedInstance.from_existing( node_data, self @@ -269,7 +269,7 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase): # Never store instance node and instance id since that data comes # from the node's path if "productName" in values: - values["AYONProductName"] = values.pop("productName") + values["AYON_productName"] = values.pop("productName") values.pop("instance_node", None) values.pop("instance_id", None) imprint(node, values, update=update) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_review.py b/client/ayon_core/hosts/houdini/plugins/create/create_review.py index fe15a21c91..18f7ce498d 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_review.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_review.py @@ -34,7 +34,7 @@ class CreateReview(plugin.HoudiniCreator): filepath = "{root}/{product_name}/{product_name}.$F4.{ext}".format( root=hou.text.expandString("$HIP/pyblish"), # keep dynamic link to product name - product_name="`chs(\"AYONProductName\")`", + product_name="`chs(\"AYON_productName\")`", ext=pre_create_data.get("image_format") or "png" ) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py index 13522b7482..e94f09568d 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py @@ -91,7 +91,7 @@ class ValidateSubsetName(pyblish.api.InstancePlugin, ) instance.data["productName"] = product_name - rop_node.parm("AYONProductName").set(product_name) + rop_node.parm("AYON_productName").set(product_name) cls.log.debug( "Product name on rop node '%s' has been set to '%s'.", From fadf820fea6bcd4c40dd7a2ce43f43cf322a3b02 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Feb 2024 18:53:44 +0800 Subject: [PATCH 341/573] improve the code and the error message & docstring --- .../publish/help/validate_model_name.xml | 26 +++ .../plugins/publish/validate_model_name.py | 160 ++++++++++-------- 2 files changed, 116 insertions(+), 70 deletions(-) create mode 100644 client/ayon_core/hosts/max/plugins/publish/help/validate_model_name.xml diff --git a/client/ayon_core/hosts/max/plugins/publish/help/validate_model_name.xml b/client/ayon_core/hosts/max/plugins/publish/help/validate_model_name.xml new file mode 100644 index 0000000000..e41146910a --- /dev/null +++ b/client/ayon_core/hosts/max/plugins/publish/help/validate_model_name.xml @@ -0,0 +1,26 @@ + + + +Invalid Model Name +## Nodes found with Invalid Model Name + +Nodes were detected in your scene which have invalid model name which does not +match the regex you preset in AYON setting. +### How to repair? +Make sure the model name aligns with validation regex in your AYON setting. + + + +### Invalid nodes + +{nodes} + + +### How could this happen? + +This often happens if you have mesh with the model naming does not match +with regex in the setting. + + + + \ No newline at end of file diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py b/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py index 87a9132989..a0cad4e454 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py @@ -3,30 +3,34 @@ import re import pyblish.api -from pymxs import runtime as rt from ayon_core.hosts.max.api.action import SelectInvalidAction from ayon_core.pipeline.publish import ( OptionalPyblishPluginMixin, - PublishValidationError, - ValidateContentsOrder) - + PublishXmlValidationError, + ValidateContentsOrder +) class ValidateModelName(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): - """Validate Model Name - Validation regex is (.*)_(?P.*)_(GEO) by default. - e.g. {SOME_RANDOM_NAME}_{YOUR_SUBSET_NAME}_GEO should be your - default model name + """Validate Model Name. - The regex of (?P.*) can be replaced by (?P.*) - and (?P.*). - e.g. - - (.*)_(?P.*)_(GEO) check if your model name is - {SOME_RANDOM_NAME}_{CURRENT_ASSET_NAME}_GEO - - (.*)_(?P.*)_(GEO) check if your model name is - {SOME_RANDOM_NAME}_{CURRENT_PROJECT_NAME}_GEO + Validation regex is `(.*)_(?P.*)_(GEO)` by default. + The setting supports the following regex group name: + - project + - asset + - subset + + Examples: + `{SOME_RANDOM_NAME}_{YOUR_SUBSET_NAME}_GEO` should be your + default model name. + The regex of `(?P.*)` can be replaced by `(?P.*)` + and `(?P.*)`. + `(.*)_(?P.*)_(GEO)` check if your model name is + `{SOME_RANDOM_NAME}_{CURRENT_ASSET_NAME}_GEO` + `(.*)_(?P.*)_(GEO)` check if your model name is + `{SOME_RANDOM_NAME}_{CURRENT_PROJECT_NAME}_GEO` """ optional = True @@ -35,66 +39,82 @@ class ValidateModelName(pyblish.api.InstancePlugin, families = ["model"] label = "Validate Model Name" actions = [SelectInvalidAction] - regex = "" - - @classmethod - def get_invalid(cls, instance): - invalid = [] - model_names = [model.name for model in instance.data.get("members")] - cls.log.debug(model_names) - if not model_names: - cls.log.error("No Model found in the OP Data.") - invalid.append(model_names) - for name in model_names: - invalid_model_name = cls.get_invalid_model_name(instance, name) - invalid.extend(invalid_model_name) - - return invalid - - @classmethod - def get_invalid_model_name(cls, instance, name): - invalid = [] - regex = cls.regex - reg = re.compile(regex) - matched_name = reg.match(name) - project_name = instance.context.data["projectName"] - current_asset_name = instance.context.data["folderPath"] - if matched_name is None: - cls.log.error("invalid model name on: {}".format(name)) - cls.log.error("name doesn't match regex {}".format(regex)) - invalid.append((rt.getNodeByName(name), - "Model name doesn't match regex")) - else: - if "asset" in reg.groupindex: - if matched_name.group("asset") != current_asset_name: - cls.log.error( - "Invalid asset name of the model {}.".format(name) - ) - invalid.append((rt.getNodeByName(name), - "Model with invalid asset name")) - if "subset" in reg.groupindex: - if matched_name.group("subset") != instance.name: - cls.log.error( - "Invalid subset name of the model {}.".format(name) - ) - invalid.append((rt.getNodeByName(name), - "Model with invalid subset name")) - if "project" in reg.groupindex: - if matched_name.group("project") != project_name: - cls.log.error( - "Invalid project name of the model {}.".format(name) - ) - invalid.append((rt.getNodeByName(name), - "Model with invalid project name")) - return invalid + # defined by settings + regex = r"(.*)_(?P.*)_(GEO)" + # cache + regex_compiled = None def process(self, instance): if not self.is_active(instance.data): - self.log.debug("Skipping Validate Model Name...") return invalid = self.get_invalid(instance) + if invalid: + names = "\n".join( + "- {}".format(node.name) for node in invalid + ) + raise PublishXmlValidationError( + plugin=self, + message="Nodes found with invalid model names: {}".format(invalid), + formatting_data={"nodes": names} + ) + + @classmethod + def get_invalid(cls, instance): + if not cls.regex: + cls.log.warning("No regex pattern set. Nothing to validate.") + return + + members = instance.data.get("members") + if not members: + cls.log.error("No members found in the instance.") + return + + cls.regex_compiled = re.compile(cls.regex) + + invalid = [] + for obj in members: + if cls.invalid_name(instance, obj): + invalid.append(obj) + return invalid + + @classmethod + def invalid_name(cls, instance, obj): + """Function to check the object has invalid name + regarding to the validation regex in the AYON setttings + + Args: + instance (pyblish.api.instance): Instance + obj (str): object name + + Returns: + str: invalid object + """ + regex = cls.regex_compiled + name = obj.name + match = regex.match(name) + + if match is None: + cls.log.error("Invalid model name on: %s", name) + cls.log.error("Name doesn't match regex {}".format(regex.pattern)) + return obj + + # Validate regex groups + invalid = False + compare = { + "project": instance.context.data["projectName"], + "asset": instance.context.data["folderPath"], + "subset": instance.context.data["subset"], + } + for key, required_value in compare.items(): + if key in regex.groupindex: + if match.group(key) != required_value: + cls.log.error( + "Invalid %s name for the model %s, " + "required name is %s", + key, name, required_value + ) + invalid = True if invalid: - raise PublishValidationError( - "Model naming is invalid. See the log.") + return obj From 3597efbe9f14f06caafb1e0cec1feb48a4a9d56a Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Feb 2024 19:48:28 +0800 Subject: [PATCH 342/573] remove unneccessary settings --- .../publish/validate_instance_in_context.py | 38 ++- client/ayon_core/settings/__init__.py | 18 -- .../defaults/project_settings/max.json | 105 --------- client/ayon_core/settings/lib.py | 223 ------------------ client/ayon_core/settings/local_settings.md | 79 ------- 5 files changed, 16 insertions(+), 447 deletions(-) delete mode 100644 client/ayon_core/settings/__init__.py delete mode 100644 client/ayon_core/settings/defaults/project_settings/max.json delete mode 100644 client/ayon_core/settings/lib.py delete mode 100644 client/ayon_core/settings/local_settings.md diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py b/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py index e4d078b36c..788e9fb19c 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py @@ -42,36 +42,30 @@ class ValidateInstanceInContext(pyblish.api.InstancePlugin, context_asset = instance.context.data["folderPath"] task = rt.getUserProp(instance_node, "task") context_task = instance.context.data["task"] + invalid = [] if asset != context_asset: - raise PublishValidationError( - message=( - "Instance '{}' publishes to different asset than current " - "context: {}. Current context: {}".format( - instance.name, asset, context_asset - ) - ), - description=( - "## Publishing to a different asset\n" - "There are publish instances present which are publishing " - "into a different asset than your current context.\n\n" - "Usually this is not what you want but there can be cases " - "where you might want to publish into another asset or " - "shot. If that's the case you can disable the validation " - "on the instance to ignore it." + invalid.append( + "Instance '{}' publishes to different asset than current " + "context: {}. Current context: {}".format( + instance.name, asset, context_asset ) ) if task != context_task: + invalid.append( + "Instance '{}' publishes to different task than current " + "context: {}. Current context: {}".format( + instance.name, task, context_task + ) + ) + + if invalid: + message = "\n".join(invalid) raise PublishValidationError( - message=( - "Instance '{}' publishes to different task than current " - "context: {}. Current context: {}".format( - instance.name, task, context_task - ) - ), + message=message, description=( "## Publishing to a different asset\n" "There are publish instances present which are publishing " - "into a different asset than your current context.\n\n" + "into a different asset or task than your current context.\n\n" "Usually this is not what you want but there can be cases " "where you might want to publish into another asset or " "shot. If that's the case you can disable the validation " diff --git a/client/ayon_core/settings/__init__.py b/client/ayon_core/settings/__init__.py deleted file mode 100644 index d32b5f3391..0000000000 --- a/client/ayon_core/settings/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -from .lib import ( - get_ayon_settings, - get_studio_settings, - get_system_settings, - get_project_settings, - get_general_environments, - get_current_project_settings, -) - - -__all__ = ( - "get_ayon_settings", - "get_studio_settings", - "get_system_settings", - "get_general_environments", - "get_project_settings", - "get_current_project_settings", -) diff --git a/client/ayon_core/settings/defaults/project_settings/max.json b/client/ayon_core/settings/defaults/project_settings/max.json deleted file mode 100644 index 7927472489..0000000000 --- a/client/ayon_core/settings/defaults/project_settings/max.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "unit_scale_settings": { - "enabled": true, - "scene_unit_scale": "Meters" - }, - "imageio": { - "activate_host_color_management": true, - "ocio_config": { - "override_global_config": false, - "filepath": [] - }, - "file_rules": { - "activate_host_rules": false, - "rules": {} - } - }, - "RenderSettings": { - "default_render_image_folder": "renders/3dsmax", - "aov_separator": "underscore", - "image_format": "exr", - "multipass": true - }, - "CreateReview": { - "review_width": 1920, - "review_height": 1080, - "percentSize": 100.0, - "keep_images": false, - "image_format": "png", - "visual_style": "Realistic", - "viewport_preset": "Quality", - "anti_aliasing": "None", - "vp_texture": true - }, - "PointCloud": { - "attribute": { - "Age": "age", - "Radius": "radius", - "Position": "position", - "Rotation": "rotation", - "Scale": "scale", - "Velocity": "velocity", - "Color": "color", - "TextureCoordinate": "texcoord", - "MaterialID": "matid", - "custFloats": "custFloats", - "custVecs": "custVecs" - } - }, - "publish": { - "ValidateInstanceInContext": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateFrameRange": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateAttributes": { - "enabled": false, - "attributes": {} - }, - "ValidateCameraAttributes": { - "enabled": true, - "optional": true, - "active": false, - "fov": 45.0, - "nearrange": 0.0, - "farrange": 1000.0, - "nearclip": 1.0, - "farclip": 1000.0 - }, - "ValidateLoadedPlugin": { - "enabled": false, - "optional": true, - "family_plugins_mapping": [] - }, - "ExtractModelObj": { - "enabled": true, - "optional": true, - "active": false - }, - "ExtractModelFbx": { - "enabled": true, - "optional": true, - "active": false - }, - "ExtractModelUSD": { - "enabled": true, - "optional": true, - "active": false - }, - "ExtractModel": { - "enabled": true, - "optional": true, - "active": true - }, - "ExtractMaxSceneRaw": { - "enabled": true, - "optional": true, - "active": true - } - } -} diff --git a/client/ayon_core/settings/lib.py b/client/ayon_core/settings/lib.py deleted file mode 100644 index eadf3ba544..0000000000 --- a/client/ayon_core/settings/lib.py +++ /dev/null @@ -1,223 +0,0 @@ -import os -import json -import logging -import collections -import copy -import time - -from ayon_core.client import get_ayon_server_api_connection - -log = logging.getLogger(__name__) - - -class CacheItem: - lifetime = 10 - - def __init__(self, value, outdate_time=None): - self._value = value - if outdate_time is None: - outdate_time = time.time() + self.lifetime - self._outdate_time = outdate_time - - @classmethod - def create_outdated(cls): - return cls({}, 0) - - def get_value(self): - return copy.deepcopy(self._value) - - def update_value(self, value): - self._value = value - self._outdate_time = time.time() + self.lifetime - - @property - def is_outdated(self): - return time.time() > self._outdate_time - - -class _AyonSettingsCache: - use_bundles = None - variant = None - addon_versions = CacheItem.create_outdated() - studio_settings = CacheItem.create_outdated() - cache_by_project_name = collections.defaultdict( - CacheItem.create_outdated) - - @classmethod - def _use_bundles(cls): - if _AyonSettingsCache.use_bundles is None: - con = get_ayon_server_api_connection() - major, minor, _, _, _ = con.get_server_version_tuple() - use_bundles = True - if (major, minor) < (0, 3): - use_bundles = False - _AyonSettingsCache.use_bundles = use_bundles - return _AyonSettingsCache.use_bundles - - @classmethod - def _get_variant(cls): - if _AyonSettingsCache.variant is None: - from ayon_core.lib import is_staging_enabled, is_dev_mode_enabled - - variant = "production" - if is_dev_mode_enabled(): - variant = cls._get_bundle_name() - elif is_staging_enabled(): - variant = "staging" - - # Cache variant - _AyonSettingsCache.variant = variant - - # Set the variant to global ayon api connection - con = get_ayon_server_api_connection() - con.set_default_settings_variant(variant) - return _AyonSettingsCache.variant - - @classmethod - def _get_bundle_name(cls): - return os.environ["AYON_BUNDLE_NAME"] - - @classmethod - def get_value_by_project(cls, project_name): - cache_item = _AyonSettingsCache.cache_by_project_name[project_name] - if cache_item.is_outdated: - con = get_ayon_server_api_connection() - if cls._use_bundles(): - value = con.get_addons_settings( - bundle_name=cls._get_bundle_name(), - project_name=project_name, - variant=cls._get_variant() - ) - else: - value = con.get_addons_settings(project_name) - cache_item.update_value(value) - return cache_item.get_value() - - @classmethod - def _get_addon_versions_from_bundle(cls): - con = get_ayon_server_api_connection() - expected_bundle = cls._get_bundle_name() - bundles = con.get_bundles()["bundles"] - bundle = next( - ( - bundle - for bundle in bundles - if bundle["name"] == expected_bundle - ), - None - ) - if bundle is not None: - return bundle["addons"] - return {} - - @classmethod - def get_addon_versions(cls): - cache_item = _AyonSettingsCache.addon_versions - if cache_item.is_outdated: - if cls._use_bundles(): - addons = cls._get_addon_versions_from_bundle() - else: - con = get_ayon_server_api_connection() - settings_data = con.get_addons_settings( - only_values=False, - variant=cls._get_variant() - ) - addons = settings_data["versions"] - cache_item.update_value(addons) - - return cache_item.get_value() - - -def get_site_local_overrides(project_name, site_name, local_settings=None): - """Site overrides from local settings for passet project and site name. - - Deprecated: - This function is not implemented for AYON and will be removed. - - Args: - project_name (str): For which project are overrides. - site_name (str): For which site are overrides needed. - local_settings (dict): Preloaded local settings. They are loaded - automatically if not passed. - """ - - return {} - - -def get_ayon_settings(project_name=None): - """AYON studio settings. - - Raw AYON settings values. - - Args: - project_name (Optional[str]): Project name. - - Returns: - dict[str, Any]: AYON settings. - """ - - return _AyonSettingsCache.get_value_by_project(project_name) - - -def get_studio_settings(*args, **kwargs): - return _AyonSettingsCache.get_value_by_project(None) - - -# Backward compatibility -get_system_settings = get_studio_settings - - -def get_project_settings(project_name, *args, **kwargs): - return _AyonSettingsCache.get_value_by_project(project_name) - - -def get_general_environments(studio_settings=None): - """General studio environment variables. - - Args: - studio_settings (Optional[dict]): Pre-queried studio settings. - - Returns: - dict[str, Any]: General studio environment variables. - - """ - if studio_settings is None: - studio_settings = get_ayon_settings() - return json.loads(studio_settings["core"]["environments"]) - - -def get_project_environments(project_name, project_settings=None): - """Project environment variables. - - Args: - project_name (str): Project name. - project_settings (Optional[dict]): Pre-queried project settings. - - Returns: - dict[str, Any]: Project environment variables. - - """ - if project_settings is None: - project_settings = get_project_settings(project_name) - return json.loads( - project_settings["core"]["project_environments"] - ) - - -def get_current_project_settings(): - """Project settings for current context project. - - Project name should be stored in environment variable `AYON_PROJECT_NAME`. - This function should be used only in host context where environment - variable must be set and should not happen that any part of process will - change the value of the enviornment variable. - """ - project_name = os.environ.get("AYON_PROJECT_NAME") - if not project_name: - raise ValueError( - "Missing context project in environemt variable `AYON_PROJECT_NAME`." - ) - return get_project_settings(project_name) - - - diff --git a/client/ayon_core/settings/local_settings.md b/client/ayon_core/settings/local_settings.md deleted file mode 100644 index fbb5cf3df1..0000000000 --- a/client/ayon_core/settings/local_settings.md +++ /dev/null @@ -1,79 +0,0 @@ -# Structure of local settings -- local settings do not have any validation schemas right now this should help to see what is stored to local settings and how it works -- they are stored by identifier site_id which should be unified identifier of workstation -- all keys may and may not available on load -- contain main categories: `general`, `applications`, `projects` - -## Categories -### General -- ATM contain only label of site -```json -{ - "general": { - "site_label": "MySite" - } -} -``` - -### Applications -- modifications of application executables -- output should match application groups and variants -```json -{ - "applications": { - "": { - "": { - "executable": "/my/path/to/nuke_12_2" - } - } - } -} -``` - -### Projects -- project specific modifications -- default project is stored under constant key defined in `pype.settings.contants` -```json -{ - "projects": { - "": { - "active_site": "", - "remote_site": "", - "roots": { - "": { - "": "" - } - } - } - } -} -``` - -## Final document -```json -{ - "_id": "", - "site_id": "", - "general": { - "site_label": "MySite" - }, - "applications": { - "": { - "": { - "executable": "" - } - } - }, - "projects": { - "": { - "active_site": "", - "remote_site": "", - "roots": { - "": { - "": "" - } - } - } - } -} -``` From d98c055a048591168d64b3e0ec11243bbbf8bde6 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Feb 2024 20:14:43 +0800 Subject: [PATCH 343/573] some tweaks on codes --- .../plugins/publish/validate_mesh_has_uv.py | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py b/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py index 846e537b69..21339c1d41 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py @@ -33,9 +33,21 @@ class ValidateMeshHasUVs(pyblish.api.InstancePlugin, @classmethod def get_invalid(cls, instance): - invalid = [member for member in instance.data["members"] - if not member.mesh.numTVerts > 0] - return invalid + invalid_mesh_type = [member for member in instance.data["members"] + if not rt.isProperty(member, "mesh")] + if invalid_mesh_type: + cls.log.error("Non-mesh type objects detected:\n".join( + "-{}".format(invalid.name) for invalid + in invalid_mesh_type)) + return invalid_mesh_type + + invalid_uv = [member for member in instance.data["members"] + if not member.mesh.numTVerts > 0] + if invalid_uv: + cls.log.error("Meshes detected with invalid UVs:\n".join( + "-{}".format(invalid.name) for invalid + in invalid_uv)) + return invalid_uv def process(self, instance): invalid = self.get_invalid(instance) @@ -47,7 +59,7 @@ class ValidateMeshHasUVs(pyblish.api.InstancePlugin, report = ( "Model meshes are required to have UVs.\n\n" "Meshes detected with invalid or missing UVs:\n" - f"{bullet_point_invalid_statement}\n\n" + f"{bullet_point_invalid_statement}\n" ) raise PublishValidationError( report, From 446160f758f2bed3225bb898c9ca605808fbffa4 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Feb 2024 20:15:31 +0800 Subject: [PATCH 344/573] cosmetic for docstring --- .../hosts/max/plugins/publish/validate_instance_in_context.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py b/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py index 788e9fb19c..93f977c15c 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py @@ -23,7 +23,6 @@ class ValidateInstanceInContext(pyblish.api.InstancePlugin, Action on this validator will select invalid instances. """ - order = ValidateContentsOrder label = "Instance in same Context" optional = True From 05d308d975c33de04d01c24d8c58bbc1a2a3436a Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Wed, 28 Feb 2024 12:41:43 +0000 Subject: [PATCH 345/573] Added missing hosts to collect_audio --- client/ayon_core/plugins/publish/collect_audio.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/collect_audio.py b/client/ayon_core/plugins/publish/collect_audio.py index f0efd546e7..af6598ad59 100644 --- a/client/ayon_core/plugins/publish/collect_audio.py +++ b/client/ayon_core/plugins/publish/collect_audio.py @@ -40,7 +40,10 @@ class CollectAudio(pyblish.api.ContextPlugin): "webpublisher", "aftereffects", "flame", - "unreal" + "unreal", + "blender", + "houdini", + "max", ] audio_product_name = "audioMain" From 0e35253bdee1a0bdef553ab7b8d05823280de6f9 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Feb 2024 20:44:33 +0800 Subject: [PATCH 346/573] add a check on non-mesh objects in the validator --- .../max/plugins/publish/validate_mesh_has_uv.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py b/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py index 21339c1d41..5594a6a789 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py @@ -36,17 +36,13 @@ class ValidateMeshHasUVs(pyblish.api.InstancePlugin, invalid_mesh_type = [member for member in instance.data["members"] if not rt.isProperty(member, "mesh")] if invalid_mesh_type: - cls.log.error("Non-mesh type objects detected:\n".join( - "-{}".format(invalid.name) for invalid - in invalid_mesh_type)) + cls.log.error("Non-mesh type objects detected") return invalid_mesh_type invalid_uv = [member for member in instance.data["members"] if not member.mesh.numTVerts > 0] if invalid_uv: - cls.log.error("Meshes detected with invalid UVs:\n".join( - "-{}".format(invalid.name) for invalid - in invalid_uv)) + cls.log.error("Meshes detected with invalid UVs") return invalid_uv def process(self, instance): @@ -57,13 +53,14 @@ class ValidateMeshHasUVs(pyblish.api.InstancePlugin, in invalid ) report = ( - "Model meshes are required to have UVs.\n\n" + "Non-mesh objects found or mesh objects" + " do not have UVs.\n\n" "Meshes detected with invalid or missing UVs:\n" f"{bullet_point_invalid_statement}\n" ) raise PublishValidationError( report, description=( - "Model meshes are required to have UVs.\n\n" + "Non-mesh objects detected or the meshes do not have any UVs.\n\n" "Meshes detected with no texture vertice or missing UVs"), - title="Mesh has missing UVs") + title="Non-mesh objects found or mesh has missing UVs") From ce94823a0f09a707bbcbc51f87941c4d756e0df4 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Feb 2024 20:51:16 +0800 Subject: [PATCH 347/573] restore unrelated deletion of unrelated code --- client/ayon_core/settings/local_settings.md | 79 +++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 client/ayon_core/settings/local_settings.md diff --git a/client/ayon_core/settings/local_settings.md b/client/ayon_core/settings/local_settings.md new file mode 100644 index 0000000000..6694bb60a8 --- /dev/null +++ b/client/ayon_core/settings/local_settings.md @@ -0,0 +1,79 @@ +# Structure of local settings +- local settings do not have any validation schemas right now this should help to see what is stored to local settings and how it works +- they are stored by identifier site_id which should be unified identifier of workstation +- all keys may and may not available on load +- contain main categories: `general`, `applications`, `projects` + +## Categories +### General +- ATM contain only label of site +```json +{ + "general": { + "site_label": "MySite" + } +} +``` + +### Applications +- modifications of application executables +- output should match application groups and variants +```json +{ + "applications": { + "": { + "": { + "executable": "/my/path/to/nuke_12_2" + } + } + } +} +``` + +### Projects +- project specific modifications +- default project is stored under constant key defined in `pype.settings.contants` +```json +{ + "projects": { + "": { + "active_site": "", + "remote_site": "", + "roots": { + "": { + "": "" + } + } + } + } +} +``` + +## Final document +```json +{ + "_id": "", + "site_id": "", + "general": { + "site_label": "MySite" + }, + "applications": { + "": { + "": { + "executable": "" + } + } + }, + "projects": { + "": { + "active_site": "", + "remote_site": "", + "roots": { + "": { + "": "" + } + } + } + } +} +``` \ No newline at end of file From 3cf206a61622e5e99fcdfa5706e9d3da16fbfe50 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Feb 2024 20:54:00 +0800 Subject: [PATCH 348/573] restore unrelated deletion of unrelated code --- client/ayon_core/settings/local_settings.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/settings/local_settings.md b/client/ayon_core/settings/local_settings.md index 6694bb60a8..fbb5cf3df1 100644 --- a/client/ayon_core/settings/local_settings.md +++ b/client/ayon_core/settings/local_settings.md @@ -76,4 +76,4 @@ } } } -``` \ No newline at end of file +``` From e5ab9a78cb89f38f42d02588afbfdd3075094e1f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Feb 2024 14:28:31 +0100 Subject: [PATCH 349/573] hierarchy model can return FolderItem by path --- .../tools/ayon_utils/models/hierarchy.py | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/ayon_utils/models/hierarchy.py b/client/ayon_core/tools/ayon_utils/models/hierarchy.py index 07773dfb78..efd6a57f9a 100644 --- a/client/ayon_core/tools/ayon_utils/models/hierarchy.py +++ b/client/ayon_core/tools/ayon_utils/models/hierarchy.py @@ -307,8 +307,44 @@ class HierarchyModel(object): }) return output + def get_folder_items_by_paths(self, project_name, folder_paths): + """Get folder items by ids. + + This function will query folders if they are not in cache. But the + queried items are not added to cache back. + + Args: + project_name (str): Name of project where to look for folders. + folder_paths (Iterable[str]): Folder paths. + + Returns: + dict[str, Union[FolderItem, None]]: Folder items by id. + """ + + folder_paths = set(folder_paths) + output = {folder_path: None for folder_path in folder_paths} + if not folder_paths: + return output + + if self._folders_items[project_name].is_valid: + cache_data = self._folders_items[project_name].get_data() + for folder_item in cache_data.values(): + if folder_item.path in folder_paths: + output[folder_item.path] = folder_item + return output + folders = ayon_api.get_folders( + project_name, + folder_paths=folder_paths, + fields=["id", "name", "label", "parentId", "path", "folderType"] + ) + # Make sure all folder ids are in output + for folder in folders: + item = _get_folder_item_from_entity(folder) + output[item.path] = item + return output + def get_folder_item(self, project_name, folder_id): - """Get folder items by id. + """Get folder item by id. This function will query folder if they is not in cache. But the queried items are not added to cache back. @@ -325,6 +361,25 @@ class HierarchyModel(object): ) return items.get(folder_id) + def get_folder_item_by_path(self, project_name, folder_path): + """Get folder item by path. + + This function will query folder if they is not in cache. But the + queried items are not added to cache back. + + Args: + project_name (str): Name of project where to look for folders. + folder_path (str): Folder path. + + Returns: + Union[FolderItem, None]: Folder item. + + """ + items = self.get_folder_items_by_paths( + project_name, [folder_path] + ) + return items.get(folder_path) + def get_task_items(self, project_name, folder_id, sender): if not project_name or not folder_id: return [] From 7c81a526231249fecc78f53bf273fd2169a210ee Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Feb 2024 14:29:04 +0100 Subject: [PATCH 350/573] task type is stored to Qt model item --- client/ayon_core/tools/ayon_utils/widgets/tasks_widget.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/tools/ayon_utils/widgets/tasks_widget.py b/client/ayon_core/tools/ayon_utils/widgets/tasks_widget.py index 3d6cc47fe3..43759cda68 100644 --- a/client/ayon_core/tools/ayon_utils/widgets/tasks_widget.py +++ b/client/ayon_core/tools/ayon_utils/widgets/tasks_widget.py @@ -214,6 +214,7 @@ class TasksQtModel(QtGui.QStandardItemModel): item.setData(task_item.label, QtCore.Qt.DisplayRole) item.setData(name, ITEM_NAME_ROLE) item.setData(task_item.id, ITEM_ID_ROLE) + item.setData(task_item.task_type, TASK_TYPE_ROLE) item.setData(task_item.parent_id, PARENT_ID_ROLE) item.setData(icon, QtCore.Qt.DecorationRole) From 65865c2e8fb9c0dd2c36ee1752ffec56198d5884 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Feb 2024 14:29:31 +0100 Subject: [PATCH 351/573] added more public functions to folder/task widgets --- .../ayon_utils/widgets/folders_widget.py | 38 ++++++++++- .../tools/ayon_utils/widgets/tasks_widget.py | 65 ++++++++++++++++++- 2 files changed, 99 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/tools/ayon_utils/widgets/folders_widget.py b/client/ayon_core/tools/ayon_utils/widgets/folders_widget.py index cf81d1c8ff..e42a5b635c 100644 --- a/client/ayon_core/tools/ayon_utils/widgets/folders_widget.py +++ b/client/ayon_core/tools/ayon_utils/widgets/folders_widget.py @@ -91,6 +91,21 @@ class FoldersQtModel(QtGui.QStandardItemModel): return QtCore.QModelIndex() return self.indexFromItem(item) + def get_item_id_by_path(self, folder_path): + """Get folder id by path. + + Args: + folder_path (str): Folder path. + + Returns: + Union[str, None]: Folder id or None if folder is not available. + + """ + for folder_id, item in self._items_by_id.values(): + if item.data(FOLDER_PATH_ROLE) == folder_path: + return folder_id + return None + def get_project_name(self): """Project name which model currently use. @@ -431,8 +446,10 @@ class FoldersWidget(QtWidgets.QWidget): Args: folder_id (Union[str, None]): Folder id or None to deselect. - """ + Returns: + bool: Requested folder was selected. + """ if folder_id is None: self._folders_view.clearSelection() return True @@ -453,6 +470,25 @@ class FoldersWidget(QtWidgets.QWidget): ) return True + def set_selected_folder_path(self, folder_path): + """Set selected folder by path. + + Args: + folder_path (str): Folder path. + + Returns: + bool: Requested folder was selected. + + """ + if folder_path is None: + self._folders_view.clearSelection() + return True + + folder_id = self._folders_model.get_item_id_by_path(folder_path) + if folder_id is None: + return False + return self.set_selected_folder(folder_id) + def set_deselectable(self, enabled): """Set deselectable mode. diff --git a/client/ayon_core/tools/ayon_utils/widgets/tasks_widget.py b/client/ayon_core/tools/ayon_utils/widgets/tasks_widget.py index 43759cda68..437eef2180 100644 --- a/client/ayon_core/tools/ayon_utils/widgets/tasks_widget.py +++ b/client/ayon_core/tools/ayon_utils/widgets/tasks_widget.py @@ -359,6 +359,64 @@ class TasksWidget(QtWidgets.QWidget): self._tasks_model.refresh() + def get_selected_task_info(self): + _, task_id, task_name, task_type = self._get_selected_item_ids() + return { + "task_id": task_id, + "task_name": task_name, + "task_type": task_type, + } + + def get_selected_task_name(self): + """Get selected task name. + + Returns: + Union[str, None]: Task name. + """ + + _, _, task_name, _ = self._get_selected_item_ids() + return task_name + + def get_selected_task_type(self): + """Get selected task type. + + Returns: + Union[str, None]: Task type. + + """ + _, _, _, task_type = self._get_selected_item_ids() + return task_type + + def set_selected_task(self, task_name): + """Set selected task by name. + + Args: + task_name (str): Task name. + + Returns: + bool: Task was selected. + + """ + if task_name is None: + self._tasks_view.clearSelection() + return True + + if task_name == self.get_selected_task_name(): + return True + index = self._tasks_model.get_index_by_name(task_name) + if not index.isValid(): + return False + + proxy_index = self._tasks_proxy_model.mapFromSource(index) + if not proxy_index.isValid(): + return False + + selection_model = self._folders_view.selectionModel() + selection_model.setCurrentIndex( + proxy_index, QtCore.QItemSelectionModel.SelectCurrent + ) + return True + def _on_tasks_refresh_finished(self, event): """Tasks were refreshed in controller. @@ -396,10 +454,11 @@ class TasksWidget(QtWidgets.QWidget): for index in selection_model.selectedIndexes(): task_id = index.data(ITEM_ID_ROLE) task_name = index.data(ITEM_NAME_ROLE) + task_type = index.data(TASK_TYPE_ROLE) parent_id = index.data(PARENT_ID_ROLE) if task_name is not None: - return parent_id, task_id, task_name - return self._selected_folder_id, None, None + return parent_id, task_id, task_name, task_type + return self._selected_folder_id, None, None, None def _on_selection_change(self): # Don't trigger task change during refresh @@ -408,7 +467,7 @@ class TasksWidget(QtWidgets.QWidget): if self._tasks_model.is_refreshing: return - parent_id, task_id, task_name = self._get_selected_item_ids() + parent_id, task_id, task_name, _ = self._get_selected_item_ids() self._controller.set_selected_task(task_id, task_name) self.selection_changed.emit() From 175185d177e4a9ba96f411a12db5074127c37eae Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Feb 2024 14:30:59 +0100 Subject: [PATCH 352/573] initial commit --- client/ayon_core/tools/publisher/control.py | 172 +++---------- .../ayon_core/tools/publisher/control_qt.py | 6 - .../{assets_widget.py => assets_dialog.py} | 95 +------ .../widgets/create_context_widgets.py | 239 ++++++++++++++++++ .../tools/publisher/widgets/create_widget.py | 92 +++---- .../tools/publisher/widgets/tasks_widget.py | 181 ------------- .../tools/publisher/widgets/widgets.py | 2 +- .../ayon_core/tools/traypublisher/window.py | 6 +- 8 files changed, 324 insertions(+), 469 deletions(-) rename client/ayon_core/tools/publisher/widgets/{assets_widget.py => assets_dialog.py} (75%) create mode 100644 client/ayon_core/tools/publisher/widgets/create_context_widgets.py diff --git a/client/ayon_core/tools/publisher/control.py b/client/ayon_core/tools/publisher/control.py index 487748afd9..a7c23112b0 100644 --- a/client/ayon_core/tools/publisher/control.py +++ b/client/ayon_core/tools/publisher/control.py @@ -14,12 +14,10 @@ import arrow import pyblish.api from ayon_core.client import ( - get_assets, - get_asset_by_id, + get_asset_by_name, get_subsets, - get_asset_name_identifier, ) -from ayon_core.lib.events import EventSystem +from ayon_core.lib.events import QueuedEventSystem from ayon_core.lib.attribute_definitions import ( UIDef, serialize_attr_defs, @@ -43,6 +41,7 @@ from ayon_core.pipeline.create.context import ( ConvertorsOperationFailed, ) from ayon_core.pipeline.publish import get_publish_instance_label +from ayon_core.tools.ayon_utils.models import HierarchyModel # Define constant for plugin orders offset PLUGIN_ORDER_OFFSET = 0.5 @@ -69,101 +68,17 @@ class MainThreadItem: class AssetDocsCache: """Cache asset documents for creation part.""" - projection = { - "_id": True, - "name": True, - "data.visualParent": True, - "data.tasks": True, - "data.parents": True, - } - def __init__(self, controller): self._controller = controller - self._asset_docs = None - self._asset_docs_hierarchy = None - self._task_names_by_asset_name = {} - self._asset_docs_by_name = {} self._full_asset_docs_by_name = {} def reset(self): - self._asset_docs = None - self._asset_docs_hierarchy = None - self._task_names_by_asset_name = {} - self._asset_docs_by_name = {} self._full_asset_docs_by_name = {} - def _query(self): - if self._asset_docs is not None: - return - - project_name = self._controller.project_name - asset_docs = list(get_assets( - project_name, fields=self.projection.keys() - )) - asset_docs_by_name = {} - task_names_by_asset_name = {} - for asset_doc in asset_docs: - if "data" not in asset_doc: - asset_doc["data"] = {"tasks": {}, "visualParent": None} - elif "tasks" not in asset_doc["data"]: - asset_doc["data"]["tasks"] = {} - - asset_name = get_asset_name_identifier(asset_doc) - asset_tasks = asset_doc["data"]["tasks"] - task_names_by_asset_name[asset_name] = list(asset_tasks.keys()) - asset_docs_by_name[asset_name] = asset_doc - - self._asset_docs = asset_docs - self._asset_docs_by_name = asset_docs_by_name - self._task_names_by_asset_name = task_names_by_asset_name - - def get_asset_docs(self): - self._query() - return copy.deepcopy(self._asset_docs) - - def get_asset_hierarchy(self): - """Prepare asset documents into hierarchy. - - Convert ObjectId to string. Asset id is not used during whole - process of publisher but asset name is used rather. - - Returns: - Dict[Union[str, None]: Any]: Mapping of parent id to it's children. - Top level assets have parent id 'None'. - """ - - if self._asset_docs_hierarchy is None: - _queue = collections.deque(self.get_asset_docs()) - - output = collections.defaultdict(list) - while _queue: - asset_doc = _queue.popleft() - asset_doc["_id"] = str(asset_doc["_id"]) - parent_id = asset_doc["data"]["visualParent"] - if parent_id is not None: - parent_id = str(parent_id) - asset_doc["data"]["visualParent"] = parent_id - output[parent_id].append(asset_doc) - self._asset_docs_hierarchy = output - return copy.deepcopy(self._asset_docs_hierarchy) - - def get_task_names_by_asset_name(self): - self._query() - return copy.deepcopy(self._task_names_by_asset_name) - - def get_asset_by_name(self, asset_name): - self._query() - asset_doc = self._asset_docs_by_name.get(asset_name) - if asset_doc is None: - return None - return copy.deepcopy(asset_doc) - def get_full_asset_by_name(self, asset_name): - self._query() if asset_name not in self._full_asset_docs_by_name: - asset_doc = self._asset_docs_by_name.get(asset_name) project_name = self._controller.project_name - full_asset_doc = get_asset_by_id(project_name, asset_doc["_id"]) + full_asset_doc = get_asset_by_name(project_name, asset_name) self._full_asset_docs_by_name[asset_name] = full_asset_doc return copy.deepcopy(self._full_asset_docs_by_name[asset_name]) @@ -1104,18 +1019,6 @@ class AbstractPublisherController(object): pass - @abstractmethod - def get_asset_docs(self): - pass - - @abstractmethod - def get_asset_hierarchy(self): - pass - - @abstractmethod - def get_task_names_by_asset_names(self, asset_names): - pass - @abstractmethod def get_existing_subset_names(self, asset_name): pass @@ -1499,13 +1402,22 @@ class BasePublisherController(AbstractPublisherController): """ if self._event_system is None: - self._event_system = EventSystem() + self._event_system = QueuedEventSystem() return self._event_system - def _emit_event(self, topic, data=None): + # Events system + def emit_event(self, topic, data=None, source=None): + """Use implemented event system to trigger event.""" + if data is None: data = {} - self.event_system.emit(topic, data, "controller") + self.event_system.emit(topic, data, source) + + def register_event_callback(self, topic, callback): + self.event_system.add_callback(topic, callback) + + def _emit_event(self, topic, data=None): + self.emit_event(topic, data, "controller") def _get_host_is_valid(self): return self._host_is_valid @@ -1738,6 +1650,7 @@ class PublisherController(BasePublisherController): self._resetting_instances = False # Cacher of avalon documents + self._hierarchy_model = HierarchyModel(self) self._asset_docs_cache = AssetDocsCache(self) @property @@ -1794,11 +1707,24 @@ class PublisherController(BasePublisherController): """Publish plugins.""" return self._create_context.publish_plugins - # --- Publish specific callbacks --- - def get_asset_docs(self): - """Get asset documents from cache for whole project.""" - return self._asset_docs_cache.get_asset_docs() + # Hierarchy model + def get_folder_items(self, project_name, sender=None): + return self._hierarchy_model.get_folder_items(project_name, sender) + def get_task_items(self, project_name, folder_id, sender=None): + return self._hierarchy_model.get_task_items( + project_name, folder_id, sender + ) + + def get_folder_entity(self, project_name, folder_id): + return self._hierarchy_model.get_folder_entity( + project_name, folder_id + ) + + def get_task_entity(self, project_name, task_id): + return self._hierarchy_model.get_task_entity(project_name, task_id) + + # --- Publish specific callbacks --- def get_context_title(self): """Get context title for artist shown at the top of main window.""" @@ -1813,32 +1739,18 @@ class PublisherController(BasePublisherController): return context_title - def get_asset_hierarchy(self): - """Prepare asset documents into hierarchy.""" - - return self._asset_docs_cache.get_asset_hierarchy() - - def get_task_names_by_asset_names(self, asset_names): - """Prepare task names by asset name.""" - task_names_by_asset_name = ( - self._asset_docs_cache.get_task_names_by_asset_name() - ) - result = {} - for asset_name in asset_names: - result[asset_name] = set( - task_names_by_asset_name.get(asset_name) or [] - ) - return result - def get_existing_subset_names(self, asset_name): project_name = self.project_name - asset_doc = self._asset_docs_cache.get_asset_by_name(asset_name) - if not asset_doc: + folder_item = self._hierarchy_model.get_folder_item_by_path( + project_name, asset_name + ) + if not folder_item: return None - asset_id = asset_doc["_id"] subset_docs = get_subsets( - project_name, asset_ids=[asset_id], fields=["name"] + project_name, + asset_ids=[folder_item.entity_id], + fields=["name"] ) return { subset_doc["name"] @@ -1858,8 +1770,6 @@ class PublisherController(BasePublisherController): # Reset avalon context self._create_context.reset_current_context() - self._asset_docs_cache.reset() - self._reset_plugins() # Publish part must be reset after plugins self._reset_publish() diff --git a/client/ayon_core/tools/publisher/control_qt.py b/client/ayon_core/tools/publisher/control_qt.py index 3d56c08131..53fed7866a 100644 --- a/client/ayon_core/tools/publisher/control_qt.py +++ b/client/ayon_core/tools/publisher/control_qt.py @@ -251,15 +251,9 @@ class QtRemotePublishController(BasePublisherController): pass - def get_asset_docs(self): - pass - def get_asset_hierarchy(self): pass - def get_task_names_by_asset_names(self, asset_names): - pass - def get_existing_subset_names(self, asset_name): pass diff --git a/client/ayon_core/tools/publisher/widgets/assets_widget.py b/client/ayon_core/tools/publisher/widgets/assets_dialog.py similarity index 75% rename from client/ayon_core/tools/publisher/widgets/assets_widget.py rename to client/ayon_core/tools/publisher/widgets/assets_dialog.py index 1c5016de99..9b54767624 100644 --- a/client/ayon_core/tools/publisher/widgets/assets_widget.py +++ b/client/ayon_core/tools/publisher/widgets/assets_dialog.py @@ -2,99 +2,13 @@ import collections from qtpy import QtWidgets, QtCore, QtGui +from ayon_core.tools.utils.assets_widget import ( + get_asset_icon, +) from ayon_core.tools.utils import ( PlaceholderLineEdit, RecursiveSortFilterProxyModel, ) -from ayon_core.tools.utils.assets_widget import ( - SingleSelectAssetsWidget, - ASSET_ID_ROLE, - ASSET_NAME_ROLE, - ASSET_PATH_ROLE, - get_asset_icon, -) - - -class CreateWidgetAssetsWidget(SingleSelectAssetsWidget): - current_context_required = QtCore.Signal() - header_height_changed = QtCore.Signal(int) - - def __init__(self, controller, parent): - self._controller = controller - super(CreateWidgetAssetsWidget, self).__init__(parent) - - self.set_refresh_btn_visibility(False) - self.set_current_asset_btn_visibility(False) - - self._last_selection = None - self._enabled = None - - self._last_filter_height = None - - def get_project_name(self): - return self._controller.project_name - - def get_selected_asset_name(self): - selection_model = self._view.selectionModel() - indexes = selection_model.selectedRows() - for index in indexes: - return index.data(ASSET_PATH_ROLE) - return None - - def _check_header_height(self): - """Catch header height changes. - - Label on top of creaters should have same height so Creators view has - same offset. - """ - height = self.header_widget.height() - if height != self._last_filter_height: - self._last_filter_height = height - self.header_height_changed.emit(height) - - def resizeEvent(self, event): - super(CreateWidgetAssetsWidget, self).resizeEvent(event) - self._check_header_height() - - def showEvent(self, event): - super(CreateWidgetAssetsWidget, self).showEvent(event) - self._check_header_height() - - def _on_current_asset_click(self): - self.current_context_required.emit() - - def set_enabled(self, enabled): - if self._enabled == enabled: - return - self._enabled = enabled - if not enabled: - self._last_selection = self.get_selected_asset_id() - self._clear_selection() - elif self._last_selection is not None: - self.select_asset(self._last_selection) - - def _select_indexes(self, *args, **kwargs): - super(CreateWidgetAssetsWidget, self)._select_indexes(*args, **kwargs) - if self._enabled: - return - self._last_selection = self.get_selected_asset_id() - self._clear_selection() - - def update_current_asset(self): - # Hide set current asset if there is no one - asset_name = self._get_current_asset_name() - self.set_current_asset_btn_visibility(bool(asset_name)) - - def _get_current_asset_name(self): - return self._controller.current_asset_name - - def _create_source_model(self): - return AssetsHierarchyModel(self._controller) - - def _refresh_model(self): - self._model.reset() - self._on_model_refresh(self._model.rowCount() > 0) - class AssetsHierarchyModel(QtGui.QStandardItemModel): """Assets hierarchy model. @@ -119,7 +33,8 @@ class AssetsHierarchyModel(QtGui.QStandardItemModel): self._items_by_name = {} self._items_by_path = {} self._items_by_asset_id = {} - assets_by_parent_id = self._controller.get_asset_hierarchy() + # assets_by_parent_id = self._controller.get_asset_hierarchy() + assets_by_parent_id = {} items_by_name = {} items_by_path = {} diff --git a/client/ayon_core/tools/publisher/widgets/create_context_widgets.py b/client/ayon_core/tools/publisher/widgets/create_context_widgets.py new file mode 100644 index 0000000000..317d922251 --- /dev/null +++ b/client/ayon_core/tools/publisher/widgets/create_context_widgets.py @@ -0,0 +1,239 @@ +from qtpy import QtWidgets, QtCore, QtGui + +from ayon_core.lib.events import QueuedEventSystem +from ayon_core.tools.utils import PlaceholderLineEdit + +from ayon_core.tools.ayon_utils.widgets import FoldersWidget, TasksWidget + + +class CreateSelectionModel(object): + """Model handling selection changes. + + Triggering events: + - "selection.project.changed" + - "selection.folder.changed" + - "selection.task.changed" + """ + + event_source = "publisher.create.selection.model" + + def __init__(self, controller): + self._controller = controller + + self._project_name = None + self._folder_id = None + self._task_name = None + self._task_id = None + + def get_selected_project_name(self): + return self._project_name + + def set_selected_project(self, project_name): + if project_name == self._project_name: + return + + self._project_name = project_name + self._controller.emit_event( + "selection.project.changed", + {"project_name": project_name}, + self.event_source + ) + + def get_selected_folder_id(self): + return self._folder_id + + def set_selected_folder(self, folder_id): + print(folder_id, self._folder_id) + if folder_id == self._folder_id: + return + + self._folder_id = folder_id + self._controller.emit_event( + "selection.folder.changed", + { + "project_name": self._project_name, + "folder_id": folder_id, + }, + self.event_source + ) + + def get_selected_task_name(self): + return self._task_name + + def get_selected_task_id(self): + return self._task_id + + def set_selected_task(self, task_id, task_name): + if task_id == self._task_id: + return + + self._task_name = task_name + self._task_id = task_id + self._controller.emit_event( + "selection.task.changed", + { + "project_name": self._project_name, + "folder_id": self._folder_id, + "task_name": task_name, + "task_id": task_id, + }, + self.event_source + ) + + +class CreateHierarchyController: + def __init__(self, controller): + self._event_system = QueuedEventSystem() + self._controller = controller + self._selection_model = CreateSelectionModel(controller) + + # Events system + @property + def event_system(self): + return self._event_system + + def emit_event(self, topic, data=None, source=None): + """Use implemented event system to trigger event.""" + + if data is None: + data = {} + print("emit_event", topic, data, source) + self.event_system.emit(topic, data, source) + + def register_event_callback(self, topic, callback): + self.event_system.add_callback(topic, callback) + + def get_project_name(self): + return self._controller.project_name + + def get_folder_items(self, project_name, sender=None): + return self._controller.get_folder_items(project_name, sender) + + def get_task_items(self, project_name, folder_id, sender=None): + return self._controller.get_task_items( + project_name, folder_id, sender + ) + + # Selection model + def set_selected_project(self, project_name): + self._selection_model.set_selected_project(project_name) + + def set_selected_folder(self, folder_id): + self._selection_model.set_selected_folder(folder_id) + + def set_selected_task(self, task_id, task_name): + self._selection_model.set_selected_task(task_id, task_name) + + +class CreateContextWidget(QtWidgets.QWidget): + folder_changed = QtCore.Signal() + task_changed = QtCore.Signal() + + def __init__(self, controller, parent): + super(CreateContextWidget, self).__init__(parent) + + self._controller = controller + self._enabled = True + self._last_folder_id = None + self._last_selected_task_name = None + + headers_widget = QtWidgets.QWidget(self) + + folder_filter_input = PlaceholderLineEdit(headers_widget) + folder_filter_input.setPlaceholderText("Filter folders..") + + current_context_btn = QtWidgets.QPushButton( + "Go to current context", headers_widget + ) + current_context_btn.setToolTip("Go to current context") + current_context_btn.setVisible(False) + + headers_layout = QtWidgets.QHBoxLayout(headers_widget) + headers_layout.setContentsMargins(0, 0, 0, 0) + headers_layout.addWidget(folder_filter_input, 1) + headers_layout.addWidget(current_context_btn, 0) + + hierarchy_controller = CreateHierarchyController(controller) + + folders_widget = FoldersWidget(hierarchy_controller, self) + tasks_widget = TasksWidget(hierarchy_controller, self) + + main_layout = QtWidgets.QVBoxLayout(self) + main_layout.setContentsMargins(0, 0, 0, 0) + main_layout.setSpacing(0) + main_layout.addWidget(headers_widget, 0) + main_layout.addWidget(folders_widget, 2) + main_layout.addWidget(tasks_widget, 1) + + folders_widget.selection_changed.connect(self._on_folder_change) + tasks_widget.selection_changed.connect(self._on_task_change) + current_context_btn.clicked.connect(self._on_current_context_click) + + self._folder_filter_input = folder_filter_input + self._current_context_btn = current_context_btn + self._folders_widget = folders_widget + self._tasks_widget = tasks_widget + self._hierarchy_controller = hierarchy_controller + + def get_selected_folder_id(self): + return self._folders_widget.get_selected_folder_id() + + def get_selected_folder_path(self): + return self._folders_widget.get_selected_folder_path() + + def get_selected_task_name(self): + return self._tasks_widget.get_selected_task_name() + + def get_selected_task_type(self): + return self._tasks_widget.get_selected_task_type() + + def update_current_context_btn(self): + # Hide set current asset if there is no one + folder_path = self._controller.current_asset_name + self._current_context_btn.setVisible(bool(folder_path)) + + def set_selected_context(self, folder_path, task_name): + self._folders_widget.set_selected_folder_path(folder_path) + self._tasks_widget.set_selected_task(task_name) + + def is_enabled(self): + return self._enabled + + def set_enabled(self, enabled): + if enabled is self._enabled: + return + + self.setEnabled(enabled) + self._enabled = enabled + + if not enabled: + self._last_folder_id = self.get_selected_folder_id() + self._folders_widget.set_selected_folder(None) + last_selected_task_name = self.get_selected_task_name() + if last_selected_task_name: + self._last_selected_task_name = last_selected_task_name + self._clear_selection() + + elif self._last_selected_task_name is not None: + self.set_selected_folder(self._last_folder_id) + self.select_task_name(self._last_selected_task_name) + + def refresh(self): + self._hierarchy_controller.set_selected_project( + self._controller.project_name + ) + self._folders_widget.set_project_name(self._controller.project_name) + + def _clear_selection(self): + self._folders_widget.set_selected_folder(None) + + def _on_folder_change(self): + self.folder_changed.emit() + + def _on_task_change(self): + self.task_changed.emit() + + def _on_current_context_click(self): + # TODO implement + folder_path = self._controller.current_asset_name + task_name = self._controller.current_task_name diff --git a/client/ayon_core/tools/publisher/widgets/create_widget.py b/client/ayon_core/tools/publisher/widgets/create_widget.py index 8eae205882..daaa167460 100644 --- a/client/ayon_core/tools/publisher/widgets/create_widget.py +++ b/client/ayon_core/tools/publisher/widgets/create_widget.py @@ -14,8 +14,7 @@ from .widgets import ( IconValuePixmapLabel, CreateBtn, ) -from .assets_widget import CreateWidgetAssetsWidget -from .tasks_widget import CreateWidgetTasksWidget +from .create_context_widgets import CreateContextWidget from .precreate_widget import PreCreateWidget from ..constants import ( VARIANT_TOOLTIP, @@ -121,16 +120,7 @@ class CreateWidget(QtWidgets.QWidget): main_splitter_widget = QtWidgets.QSplitter(self) - context_widget = QtWidgets.QWidget(main_splitter_widget) - - assets_widget = CreateWidgetAssetsWidget(controller, context_widget) - tasks_widget = CreateWidgetTasksWidget(controller, context_widget) - - context_layout = QtWidgets.QVBoxLayout(context_widget) - context_layout.setContentsMargins(0, 0, 0, 0) - context_layout.setSpacing(0) - context_layout.addWidget(assets_widget, 2) - context_layout.addWidget(tasks_widget, 1) + context_widget = CreateContextWidget(controller, main_splitter_widget) # --- Creators view --- creators_widget = QtWidgets.QWidget(main_splitter_widget) @@ -279,11 +269,8 @@ class CreateWidget(QtWidgets.QWidget): ) variant_hints_btn.clicked.connect(self._on_variant_btn_click) variant_hints_menu.triggered.connect(self._on_variant_action) - assets_widget.selection_changed.connect(self._on_asset_change) - assets_widget.current_context_required.connect( - self._on_current_session_context_request - ) - tasks_widget.task_changed.connect(self._on_task_change) + context_widget.folder_changed.connect(self._on_folder_change) + context_widget.task_changed.connect(self._on_task_change) thumbnail_widget.thumbnail_created.connect(self._on_thumbnail_create) thumbnail_widget.thumbnail_cleared.connect(self._on_thumbnail_clear) @@ -299,8 +286,6 @@ class CreateWidget(QtWidgets.QWidget): self._creators_splitter = creators_splitter self._context_widget = context_widget - self._assets_widget = assets_widget - self._tasks_widget = tasks_widget self.subset_name_input = subset_name_input @@ -324,7 +309,7 @@ class CreateWidget(QtWidgets.QWidget): self._first_show = True self._last_thumbnail_path = None - self._last_current_context_asset = None + self._last_current_context_folder_path = None self._last_current_context_task = None self._use_current_context = True @@ -340,31 +325,35 @@ class CreateWidget(QtWidgets.QWidget): return self._context_widget.isEnabled() def _get_asset_name(self): - asset_name = None + folder_path = None if self._context_change_is_enabled(): - asset_name = self._assets_widget.get_selected_asset_name() + folder_path = self._context_widget.get_selected_folder_path() - if asset_name is None: - asset_name = self.current_asset_name - return asset_name or None + if folder_path is None: + folder_path = self.current_asset_name + return folder_path or None + + def _get_folder_id(self): + folder_id = None + if self._context_widget.isEnabled(): + folder_id = self._context_widget.get_selected_folder_id() + return folder_id def _get_task_name(self): task_name = None if self._context_change_is_enabled(): # Don't use selection of task if asset is not set - asset_name = self._assets_widget.get_selected_asset_name() - if asset_name: - task_name = self._tasks_widget.get_selected_task_name() + folder_path = self._context_widget.get_selected_folder_path() + if folder_path: + task_name = self._context_widget.get_selected_task_name() if not task_name: task_name = self.current_task_name return task_name def _set_context_enabled(self, enabled): - self._assets_widget.set_enabled(enabled) - self._tasks_widget.set_enabled(enabled) check_prereq = self._context_widget.isEnabled() != enabled - self._context_widget.setEnabled(enabled) + self._context_widget.set_enabled(enabled) if check_prereq: self._invalidate_prereq() @@ -375,12 +364,12 @@ class CreateWidget(QtWidgets.QWidget): self._use_current_context = True def refresh(self): - current_asset_name = self._controller.current_asset_name + current_folder_path = self._controller.current_asset_name current_task_name = self._controller.current_task_name # Get context before refresh to keep selection of asset and # task widgets - asset_name = self._get_asset_name() + folder_path = self._get_asset_name() task_name = self._get_task_name() # Replace by current context if last loaded context was @@ -388,16 +377,16 @@ class CreateWidget(QtWidgets.QWidget): if ( self._use_current_context or ( - self._last_current_context_asset - and asset_name == self._last_current_context_asset + self._last_current_context_folder_path + and folder_path == self._last_current_context_folder_path and task_name == self._last_current_context_task ) ): - asset_name = current_asset_name + folder_path = current_folder_path task_name = current_task_name # Store values for future refresh - self._last_current_context_asset = current_asset_name + self._last_current_context_folder_path = current_folder_path self._last_current_context_task = current_task_name self._use_current_context = False @@ -407,18 +396,16 @@ class CreateWidget(QtWidgets.QWidget): # name self._set_context_enabled(False) - self._assets_widget.refresh() - # Refresh data before update of creators - self._refresh_asset() + self._context_widget.refresh() + self._refresh_product_name() + # Then refresh creators which may trigger callbacks using refreshed # data self._refresh_creators() - self._assets_widget.update_current_asset() - self._assets_widget.select_asset_by_name(asset_name) - self._tasks_widget.set_asset_name(asset_name) - self._tasks_widget.select_task_name(task_name) + self._context_widget.update_current_context_btn() + self._context_widget.set_selected_context(folder_path, task_name) self._invalidate_prereq_deffered() @@ -460,7 +447,7 @@ class CreateWidget(QtWidgets.QWidget): self._on_variant_change() - def _refresh_asset(self): + def _refresh_product_name(self): asset_name = self._get_asset_name() # Skip if asset did not change @@ -545,11 +532,8 @@ class CreateWidget(QtWidgets.QWidget): # Trigger refresh only if is visible self.refresh() - def _on_asset_change(self): - self._refresh_asset() - - asset_name = self._assets_widget.get_selected_asset_name() - self._tasks_widget.set_asset_name(asset_name) + def _on_folder_change(self): + self._refresh_product_name() if self._context_change_is_enabled(): self._invalidate_prereq_deffered() @@ -564,12 +548,6 @@ class CreateWidget(QtWidgets.QWidget): def _on_thumbnail_clear(self): self._last_thumbnail_path = None - def _on_current_session_context_request(self): - self._assets_widget.select_current_asset() - task_name = self.current_task_name - if task_name: - self._tasks_widget.select_task_name(task_name) - def _on_creator_item_change(self, new_index, _old_index): identifier = None if new_index.isValid(): @@ -616,7 +594,7 @@ class CreateWidget(QtWidgets.QWidget): != self._context_change_is_enabled() ): self._set_context_enabled(creator_item.create_allow_context_change) - self._refresh_asset() + self._refresh_product_name() self._thumbnail_widget.setVisible( creator_item.create_allow_thumbnail diff --git a/client/ayon_core/tools/publisher/widgets/tasks_widget.py b/client/ayon_core/tools/publisher/widgets/tasks_widget.py index 9a1b22b9a5..37c32ccb97 100644 --- a/client/ayon_core/tools/publisher/widgets/tasks_widget.py +++ b/client/ayon_core/tools/publisher/widgets/tasks_widget.py @@ -1,6 +1,5 @@ from qtpy import QtWidgets, QtCore, QtGui -from ayon_core.tools.utils.views import DeselectableTreeView from ayon_core.tools.utils.lib import get_default_task_icon TASK_NAME_ROLE = QtCore.Qt.UserRole + 1 @@ -144,183 +143,3 @@ class TasksModel(QtGui.QStandardItemModel): return super(TasksModel, self).headerData(section, orientation, role) - -class TasksProxyModel(QtCore.QSortFilterProxyModel): - def lessThan(self, x_index, y_index): - x_order = x_index.data(TASK_ORDER_ROLE) - y_order = y_index.data(TASK_ORDER_ROLE) - if x_order is not None and y_order is not None: - if x_order < y_order: - return True - if x_order > y_order: - return False - - elif x_order is None and y_order is not None: - return True - - elif y_order is None and x_order is not None: - return False - - x_name = x_index.data(QtCore.Qt.DisplayRole) - y_name = y_index.data(QtCore.Qt.DisplayRole) - if x_name == y_name: - return True - - if x_name == tuple(sorted((x_name, y_name)))[0]: - return True - return False - - -class CreateWidgetTasksWidget(QtWidgets.QWidget): - """Widget showing active Tasks - - Deprecated: - This widget will be removed soon. Please do not use it in new code. - """ - - task_changed = QtCore.Signal() - - def __init__(self, controller, parent): - self._controller = controller - - self._enabled = None - - super(CreateWidgetTasksWidget, self).__init__(parent) - - tasks_view = DeselectableTreeView(self) - tasks_view.setIndentation(0) - tasks_view.setSortingEnabled(True) - tasks_view.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) - - header_view = tasks_view.header() - header_view.setSortIndicator(0, QtCore.Qt.AscendingOrder) - - tasks_model = TasksModel(self._controller) - tasks_proxy = TasksProxyModel() - tasks_proxy.setSourceModel(tasks_model) - tasks_view.setModel(tasks_proxy) - - layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.addWidget(tasks_view) - - selection_model = tasks_view.selectionModel() - selection_model.selectionChanged.connect(self._on_task_change) - - self._tasks_model = tasks_model - self._tasks_proxy = tasks_proxy - self._tasks_view = tasks_view - - self._last_selected_task_name = None - - def refresh(self): - self._tasks_model.refresh() - - def set_asset_id(self, asset_id): - # Try and preserve the last selected task and reselect it - # after switching assets. If there's no currently selected - # asset keep whatever the "last selected" was prior to it. - current = self.get_selected_task_name() - if current: - self._last_selected_task_name = current - - self._tasks_model.set_asset_id(asset_id) - - if self._last_selected_task_name: - self.select_task_name(self._last_selected_task_name) - - # Force a task changed emit. - self.task_changed.emit() - - def _clear_selection(self): - selection_model = self._tasks_view.selectionModel() - selection_model.clearSelection() - - def select_task_name(self, task_name): - """Select a task by name. - - If the task does not exist in the current model then selection is only - cleared. - - Args: - task_name (str): Name of the task to select. - - """ - task_view_model = self._tasks_view.model() - if not task_view_model: - return - - # Clear selection - selection_model = self._tasks_view.selectionModel() - selection_model.clearSelection() - - # Select the task - mode = ( - QtCore.QItemSelectionModel.Select - | QtCore.QItemSelectionModel.Rows - ) - for row in range(task_view_model.rowCount()): - index = task_view_model.index(row, 0) - name = index.data(TASK_NAME_ROLE) - if name == task_name: - selection_model.select(index, mode) - - # Set the currently active index - self._tasks_view.setCurrentIndex(index) - break - - last_selected_task_name = self.get_selected_task_name() - if last_selected_task_name: - self._last_selected_task_name = last_selected_task_name - - if not self._enabled: - current = self.get_selected_task_name() - if current: - self._last_selected_task_name = current - self._clear_selection() - - def get_selected_task_name(self): - """Return name of task at current index (selected) - - Returns: - str: Name of the current task. - - """ - index = self._tasks_view.currentIndex() - selection_model = self._tasks_view.selectionModel() - if index.isValid() and selection_model.isSelected(index): - return index.data(TASK_NAME_ROLE) - return None - - def get_selected_task_type(self): - index = self._tasks_view.currentIndex() - selection_model = self._tasks_view.selectionModel() - if index.isValid() and selection_model.isSelected(index): - return index.data(TASK_TYPE_ROLE) - return None - - def set_asset_name(self, asset_name): - current = self.get_selected_task_name() - if current: - self._last_selected_task_name = current - - self._tasks_model.set_asset_names([asset_name]) - if self._last_selected_task_name and self._enabled: - self.select_task_name(self._last_selected_task_name) - - # Force a task changed emit. - self.task_changed.emit() - - def set_enabled(self, enabled): - self._enabled = enabled - if not enabled: - last_selected_task_name = self.get_selected_task_name() - if last_selected_task_name: - self._last_selected_task_name = last_selected_task_name - self._clear_selection() - - elif self._last_selected_task_name is not None: - self.select_task_name(self._last_selected_task_name) - - def _on_task_change(self): - self.task_changed.emit() diff --git a/client/ayon_core/tools/publisher/widgets/widgets.py b/client/ayon_core/tools/publisher/widgets/widgets.py index bd5ab250bd..94825432ee 100644 --- a/client/ayon_core/tools/publisher/widgets/widgets.py +++ b/client/ayon_core/tools/publisher/widgets/widgets.py @@ -26,7 +26,7 @@ from ayon_core.pipeline.create import ( TaskNotSetError, ) from .thumbnail_widget import ThumbnailWidget -from .assets_widget import AssetsDialog +from .assets_dialog import AssetsDialog from .tasks_widget import TasksModel from .icons import ( get_pixmap, diff --git a/client/ayon_core/tools/traypublisher/window.py b/client/ayon_core/tools/traypublisher/window.py index 79386d7ea0..07ec826db7 100644 --- a/client/ayon_core/tools/traypublisher/window.py +++ b/client/ayon_core/tools/traypublisher/window.py @@ -30,8 +30,8 @@ class TrayPublisherController(QtPublisherController): def host(self): return self._host - def reset_project_data_cache(self): - self._asset_docs_cache.reset() + def reset_hierarchy_cache(self): + self._hierarchy_model.reset() class TrayPublisherRegistry(JSONSettingRegistry): @@ -248,7 +248,7 @@ class TrayPublishWindow(PublisherWindow): def _on_project_select(self, project_name): # TODO register project specific plugin paths self._controller.save_changes(False) - self._controller.reset_project_data_cache() + self._controller.reset_hierarchy_cache() self.reset() if not self._controller.instances: From ef1d3ada61816131fe3eaaba72ed35a69ae66605 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Feb 2024 14:59:01 +0100 Subject: [PATCH 353/573] fix typo in 'project_doc' --- client/ayon_core/lib/applications.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/lib/applications.py b/client/ayon_core/lib/applications.py index 78acc57872..8f1a1d10ea 100644 --- a/client/ayon_core/lib/applications.py +++ b/client/ayon_core/lib/applications.py @@ -1395,7 +1395,7 @@ class EnvironmentPrepData(dict): if data.get("env") is None: data["env"] = os.environ.copy() - project_name = data["project_doct"]["name"] + project_name = data["project_doc"]["name"] if "project_settings" not in data: data["project_settings"] = get_project_settings(project_name) From f92de3c351c99643eb561955e32dc4aae50af505 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Feb 2024 14:59:08 +0100 Subject: [PATCH 354/573] fix typo in onilne --- client/ayon_core/hosts/fusion/plugins/load/load_sequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/fusion/plugins/load/load_sequence.py b/client/ayon_core/hosts/fusion/plugins/load/load_sequence.py index 5c183f5159..ad737aabed 100644 --- a/client/ayon_core/hosts/fusion/plugins/load/load_sequence.py +++ b/client/ayon_core/hosts/fusion/plugins/load/load_sequence.py @@ -136,7 +136,7 @@ class FusionLoadSequence(load.LoaderPlugin): "render", "plate", "image", - "onilne", + "online", ] representations = ["*"] extensions = set( From 1027a41e8a7b5d57a2e96af9708d0e783378f8ba Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Feb 2024 15:14:46 +0100 Subject: [PATCH 355/573] fix path calculated from hierarchy item --- client/ayon_core/tools/ayon_utils/models/hierarchy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/ayon_utils/models/hierarchy.py b/client/ayon_core/tools/ayon_utils/models/hierarchy.py index efd6a57f9a..10495cf10b 100644 --- a/client/ayon_core/tools/ayon_utils/models/hierarchy.py +++ b/client/ayon_core/tools/ayon_utils/models/hierarchy.py @@ -191,12 +191,12 @@ def _get_folder_item_from_hierarchy_item(item): name = item["name"] path_parts = list(item["parents"]) path_parts.append(name) - + path = "/" + "/".join(path_parts) return FolderItem( item["id"], item["parentId"], name, - "/".join(path_parts), + path, item["folderType"], item["label"], None, From c75c9d8d70fc1668ae2afc18cb2d0035bfc8ef62 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Feb 2024 15:15:18 +0100 Subject: [PATCH 356/573] create context selection is using hierarchy model --- client/ayon_core/tools/publisher/control.py | 9 +++ .../widgets/create_context_widgets.py | 71 +++++++++++++++---- .../tools/publisher/widgets/create_widget.py | 9 +-- 3 files changed, 72 insertions(+), 17 deletions(-) diff --git a/client/ayon_core/tools/publisher/control.py b/client/ayon_core/tools/publisher/control.py index c0dcad9fcc..b4101b1f86 100644 --- a/client/ayon_core/tools/publisher/control.py +++ b/client/ayon_core/tools/publisher/control.py @@ -1725,6 +1725,15 @@ class PublisherController(BasePublisherController): def get_task_entity(self, project_name, task_id): return self._hierarchy_model.get_task_entity(project_name, task_id) + # Publisher custom method + def get_folder_id_from_path(self, folder_path): + folder_item = self._hierarchy_model.get_folder_item_by_path( + self.project_name, folder_path + ) + if folder_item: + return folder_item.entity_id + return None + # --- Publish specific callbacks --- def get_context_title(self): """Get context title for artist shown at the top of main window.""" diff --git a/client/ayon_core/tools/publisher/widgets/create_context_widgets.py b/client/ayon_core/tools/publisher/widgets/create_context_widgets.py index 317d922251..72db38d629 100644 --- a/client/ayon_core/tools/publisher/widgets/create_context_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/create_context_widgets.py @@ -3,6 +3,7 @@ from qtpy import QtWidgets, QtCore, QtGui from ayon_core.lib.events import QueuedEventSystem from ayon_core.tools.utils import PlaceholderLineEdit +from ayon_core.tools.ayon_utils.models import HierarchyExpectedSelection from ayon_core.tools.ayon_utils.widgets import FoldersWidget, TasksWidget @@ -43,7 +44,6 @@ class CreateSelectionModel(object): return self._folder_id def set_selected_folder(self, folder_id): - print(folder_id, self._folder_id) if folder_id == self._folder_id: return @@ -85,7 +85,10 @@ class CreateHierarchyController: def __init__(self, controller): self._event_system = QueuedEventSystem() self._controller = controller - self._selection_model = CreateSelectionModel(controller) + self._selection_model = CreateSelectionModel(self) + self._expected_selection = HierarchyExpectedSelection( + self, handle_project=False + ) # Events system @property @@ -97,7 +100,6 @@ class CreateHierarchyController: if data is None: data = {} - print("emit_event", topic, data, source) self.event_system.emit(topic, data, source) def register_event_callback(self, topic, callback): @@ -124,6 +126,21 @@ class CreateHierarchyController: def set_selected_task(self, task_id, task_name): self._selection_model.set_selected_task(task_id, task_name) + # Expected selection + def get_expected_selection_data(self): + return self._expected_selection.get_expected_selection_data() + + def set_expected_selection(self, project_name, folder_id, task_name): + self._expected_selection.set_expected_selection( + project_name, folder_id, task_name + ) + + def expected_folder_selected(self, folder_id): + self._expected_selection.expected_folder_selected(folder_id) + + def expected_task_selected(self, folder_id, task_name): + self._expected_selection.expected_task_selected(folder_id, task_name) + class CreateContextWidget(QtWidgets.QWidget): folder_changed = QtCore.Signal() @@ -134,6 +151,7 @@ class CreateContextWidget(QtWidgets.QWidget): self._controller = controller self._enabled = True + self._last_project_name = None self._last_folder_id = None self._last_selected_task_name = None @@ -155,8 +173,12 @@ class CreateContextWidget(QtWidgets.QWidget): hierarchy_controller = CreateHierarchyController(controller) - folders_widget = FoldersWidget(hierarchy_controller, self) - tasks_widget = TasksWidget(hierarchy_controller, self) + folders_widget = FoldersWidget( + hierarchy_controller, self, handle_expected_selection=True + ) + tasks_widget = TasksWidget( + hierarchy_controller, self, handle_expected_selection=True + ) main_layout = QtWidgets.QVBoxLayout(self) main_layout.setContentsMargins(0, 0, 0, 0) @@ -168,6 +190,7 @@ class CreateContextWidget(QtWidgets.QWidget): folders_widget.selection_changed.connect(self._on_folder_change) tasks_widget.selection_changed.connect(self._on_task_change) current_context_btn.clicked.connect(self._on_current_context_click) + folder_filter_input.textChanged.connect(self._on_folder_filter_change) self._folder_filter_input = folder_filter_input self._current_context_btn = current_context_btn @@ -192,9 +215,12 @@ class CreateContextWidget(QtWidgets.QWidget): folder_path = self._controller.current_asset_name self._current_context_btn.setVisible(bool(folder_path)) - def set_selected_context(self, folder_path, task_name): - self._folders_widget.set_selected_folder_path(folder_path) - self._tasks_widget.set_selected_task(task_name) + def set_selected_context(self, folder_id, task_name): + self._hierarchy_controller.set_expected_selection( + self._controller.project_name, + folder_id, + task_name + ) def is_enabled(self): return self._enabled @@ -215,14 +241,27 @@ class CreateContextWidget(QtWidgets.QWidget): self._clear_selection() elif self._last_selected_task_name is not None: - self.set_selected_folder(self._last_folder_id) - self.select_task_name(self._last_selected_task_name) + self._hierarchy_controller.set_expected_selection( + self._last_project_name, + self._last_folder_id, + self._last_selected_task_name + ) def refresh(self): + self._last_project_name = self._controller.project_name + folder_id = self._last_folder_id + task_name = self._last_selected_task_name + if folder_id is None: + folder_path = self._controller.current_asset_name + folder_id = self._controller.get_folder_id_from_path(folder_path) + task_name = self._controller.current_task_name self._hierarchy_controller.set_selected_project( - self._controller.project_name + self._last_project_name + ) + self._folders_widget.set_project_name(self._last_project_name) + self._hierarchy_controller.set_expected_selection( + self._last_project_name, folder_id, task_name ) - self._folders_widget.set_project_name(self._controller.project_name) def _clear_selection(self): self._folders_widget.set_selected_folder(None) @@ -234,6 +273,12 @@ class CreateContextWidget(QtWidgets.QWidget): self.task_changed.emit() def _on_current_context_click(self): - # TODO implement folder_path = self._controller.current_asset_name task_name = self._controller.current_task_name + folder_id = self._controller.get_folder_id_from_path(folder_path) + self._hierarchy_controller.set_expected_selection( + self._last_project_name, folder_id, task_name + ) + + def _on_folder_filter_change(self, text): + self._folders_widget.set_name_filter(text) diff --git a/client/ayon_core/tools/publisher/widgets/create_widget.py b/client/ayon_core/tools/publisher/widgets/create_widget.py index 13abd562ba..4287e9ce07 100644 --- a/client/ayon_core/tools/publisher/widgets/create_widget.py +++ b/client/ayon_core/tools/publisher/widgets/create_widget.py @@ -322,7 +322,7 @@ class CreateWidget(QtWidgets.QWidget): return self._controller.current_task_name def _context_change_is_enabled(self): - return self._context_widget.isEnabled() + return self._context_widget.is_enabled() def _get_asset_name(self): folder_path = None @@ -335,7 +335,7 @@ class CreateWidget(QtWidgets.QWidget): def _get_folder_id(self): folder_id = None - if self._context_widget.isEnabled(): + if self._context_widget.is_enabled(): folder_id = self._context_widget.get_selected_folder_id() return folder_id @@ -352,7 +352,7 @@ class CreateWidget(QtWidgets.QWidget): return task_name def _set_context_enabled(self, enabled): - check_prereq = self._context_widget.isEnabled() != enabled + check_prereq = self._context_widget.is_enabled() != enabled self._context_widget.set_enabled(enabled) if check_prereq: self._invalidate_prereq() @@ -404,8 +404,9 @@ class CreateWidget(QtWidgets.QWidget): # data self._refresh_creators() + folder_id = self._controller.get_folder_id_from_path(folder_path) self._context_widget.update_current_context_btn() - self._context_widget.set_selected_context(folder_path, task_name) + self._context_widget.set_selected_context(folder_id, task_name) self._invalidate_prereq_deffered() From 4044feb98856df439508de494b230234fa3239ee Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Feb 2024 15:54:04 +0100 Subject: [PATCH 357/573] validation of folder paths happens in controller --- client/ayon_core/tools/publisher/control.py | 12 ++++++ .../tools/publisher/widgets/widgets.py | 40 +++++++++---------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/client/ayon_core/tools/publisher/control.py b/client/ayon_core/tools/publisher/control.py index b4101b1f86..3beef4030a 100644 --- a/client/ayon_core/tools/publisher/control.py +++ b/client/ayon_core/tools/publisher/control.py @@ -1734,6 +1734,18 @@ class PublisherController(BasePublisherController): return folder_item.entity_id return None + def are_folder_paths_valid(self, folder_paths): + if not folder_paths: + return True + folder_paths = set(folder_paths) + folder_items = self._hierarchy_model.get_folder_items_by_paths( + self.project_name, folder_paths + ) + for folder_item in folder_items.values(): + if folder_item is None: + return False + return True + # --- Publish specific callbacks --- def get_context_title(self): """Get context title for artist shown at the top of main window.""" diff --git a/client/ayon_core/tools/publisher/widgets/widgets.py b/client/ayon_core/tools/publisher/widgets/widgets.py index 7b7d842d7d..6a3f097fe9 100644 --- a/client/ayon_core/tools/publisher/widgets/widgets.py +++ b/client/ayon_core/tools/publisher/widgets/widgets.py @@ -465,6 +465,7 @@ class AssetsField(BaseClickableFrame): icon_btn.clicked.connect(self._mouse_release_callback) dialog.finished.connect(self._on_dialog_finish) + self._controller = controller self._dialog = dialog self._name_input = name_input self._icon_btn = icon_btn @@ -539,38 +540,33 @@ class AssetsField(BaseClickableFrame): self._name_input.setText(text) self._name_input.end(False) - def set_selected_items(self, asset_names=None): + def set_selected_items(self, folder_paths=None): """Set asset names for selection of instances. Passed asset names are validated and if there are 2 or more different asset names then multiselection text is shown. Args: - asset_names (list, tuple, set, NoneType): List of asset names. + folder_paths (list, tuple, set, NoneType): List of folder paths. + """ - if asset_names is None: - asset_names = [] + if folder_paths is None: + folder_paths = [] self._has_value_changed = False - self._origin_value = list(asset_names) - self._selected_items = list(asset_names) - is_valid = True - if not asset_names: + self._origin_value = list(folder_paths) + self._selected_items = list(folder_paths) + is_valid = self._controller.are_folder_paths_valid(folder_paths) + if not folder_paths: self.set_text("") - elif len(asset_names) == 1: - asset_name = tuple(asset_names)[0] - is_valid = self._dialog.name_is_valid(asset_name) - self.set_text(asset_name) + elif len(folder_paths) == 1: + folder_path = tuple(folder_paths)[0] + self.set_text(folder_path) else: - for asset_name in asset_names: - is_valid = self._dialog.name_is_valid(asset_name) - if not is_valid: - break - multiselection_text = self._multiselection_text if multiselection_text is None: - multiselection_text = "|".join(asset_names) + multiselection_text = "|".join(folder_paths) self.set_text(multiselection_text) self._set_is_valid(is_valid) @@ -746,11 +742,11 @@ class TasksCombobox(QtWidgets.QComboBox): """ return list(self._selected_items) - def set_asset_names(self, asset_names): + def set_folder_paths(self, asset_names): """Set asset names for which should show tasks.""" self._ignore_index_change = True - self._model.set_asset_names(asset_names) + self._model.set_folder_paths(asset_names) self._proxy_model.set_filter_empty(False) self._proxy_model.sort(0) @@ -822,7 +818,7 @@ class TasksCombobox(QtWidgets.QComboBox): self._ignore_index_change = True - self._model.set_asset_names(asset_names) + self._model.set_folder_paths(asset_names) self._has_value_changed = False @@ -1265,7 +1261,7 @@ class GlobalAttrsWidget(QtWidgets.QWidget): def _on_asset_change(self): asset_names = self.asset_value_widget.get_selected_items() - self.task_value_widget.set_asset_names(asset_names) + self.task_value_widget.set_folder_paths(asset_names) self._on_value_change() def _on_task_change(self): From de2f48ff54daa1b27b534dc8a607f6d1761397bd Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Feb 2024 16:05:03 +0100 Subject: [PATCH 358/573] fixed change of context on existing instances --- client/ayon_core/tools/publisher/control.py | 71 ++++-- .../ayon_core/tools/publisher/control_qt.py | 10 +- .../tools/publisher/widgets/assets_dialog.py | 224 ++++-------------- .../widgets/create_context_widgets.py | 6 +- .../tools/publisher/widgets/create_widget.py | 42 ++-- .../tools/publisher/widgets/tasks_widget.py | 50 ++-- .../tools/publisher/widgets/widgets.py | 4 +- 7 files changed, 159 insertions(+), 248 deletions(-) diff --git a/client/ayon_core/tools/publisher/control.py b/client/ayon_core/tools/publisher/control.py index 3beef4030a..f59ffa9588 100644 --- a/client/ayon_core/tools/publisher/control.py +++ b/client/ayon_core/tools/publisher/control.py @@ -12,6 +12,7 @@ from abc import ABCMeta, abstractmethod import six import arrow import pyblish.api +import ayon_api from ayon_core.client import ( get_asset_by_name, @@ -70,17 +71,17 @@ class AssetDocsCache: def __init__(self, controller): self._controller = controller - self._full_asset_docs_by_name = {} + self._asset_docs_by_path = {} def reset(self): - self._full_asset_docs_by_name = {} + self._asset_docs_by_path = {} - def get_full_asset_by_name(self, asset_name): - if asset_name not in self._full_asset_docs_by_name: + def get_asset_doc_by_folder_path(self, folder_path): + if folder_path not in self._asset_docs_by_path: project_name = self._controller.project_name - full_asset_doc = get_asset_by_name(project_name, asset_name) - self._full_asset_docs_by_name[asset_name] = full_asset_doc - return copy.deepcopy(self._full_asset_docs_by_name[asset_name]) + asset_doc = get_asset_by_name(project_name, folder_path) + self._asset_docs_by_path[folder_path] = asset_doc + return copy.deepcopy(self._asset_docs_by_path[folder_path]) class PublishReportMaker: @@ -951,13 +952,13 @@ class AbstractPublisherController(object): @property @abstractmethod - def current_asset_name(self): - """Current context asset name. + def current_folder_path(self): + """Current context folder path. Returns: - Union[str, None]: Name of asset. - """ + Union[str, None]: Folder path. + """ pass @property @@ -1021,7 +1022,7 @@ class AbstractPublisherController(object): pass @abstractmethod - def get_existing_product_names(self, asset_name): + def get_existing_product_names(self, folder_path): pass @abstractmethod @@ -1665,7 +1666,7 @@ class PublisherController(BasePublisherController): return self._create_context.get_current_project_name() @property - def current_asset_name(self): + def current_folder_path(self): """Current context asset name defined by host. Returns: @@ -1727,6 +1728,8 @@ class PublisherController(BasePublisherController): # Publisher custom method def get_folder_id_from_path(self, folder_path): + if not folder_path: + return None folder_item = self._hierarchy_model.get_folder_item_by_path( self.project_name, folder_path ) @@ -1734,6 +1737,34 @@ class PublisherController(BasePublisherController): return folder_item.entity_id return None + def get_task_names_by_folder_paths(self, folder_paths): + # TODO implement model and cache values + if not folder_paths: + return {} + folder_items = self._hierarchy_model.get_folder_items_by_paths( + self.project_name, folder_paths + ) + folder_paths_by_id = { + folder_item.entity_id: folder_item.path + for folder_item in folder_items.values() + if folder_item + } + tasks = ayon_api.get_tasks( + self.project_name, + folder_ids=set(folder_paths_by_id), + fields=["name", "folderId"] + ) + output = { + folder_path: set() + for folder_path in folder_paths + } + for task in tasks: + folder_path = folder_paths_by_id.get(task["folderId"]) + if folder_path: + output[folder_path].add(task["name"]) + + return output + def are_folder_paths_valid(self, folder_paths): if not folder_paths: return True @@ -1761,10 +1792,12 @@ class PublisherController(BasePublisherController): return context_title - def get_existing_product_names(self, asset_name): + def get_existing_product_names(self, folder_path): + if not folder_path: + return None project_name = self.project_name folder_item = self._hierarchy_model.get_folder_item_by_path( - project_name, asset_name + project_name, folder_path ) if not folder_item: return None @@ -2006,7 +2039,7 @@ class PublisherController(BasePublisherController): creator_identifier, variant, task_name, - asset_name, + folder_path, instance_id=None ): """Get product name based on passed data. @@ -2016,14 +2049,16 @@ class PublisherController(BasePublisherController): responsible for product name creation. variant (str): Variant value from user's input. task_name (str): Name of task for which is instance created. - asset_name (str): Name of asset for which is instance created. + folder_path (str): Folder path for which is instance created. instance_id (Union[str, None]): Existing instance id when product name is updated. """ creator = self._creators[creator_identifier] project_name = self.project_name - asset_doc = self._asset_docs_cache.get_full_asset_by_name(asset_name) + asset_doc = self._asset_docs_cache.get_asset_doc_by_folder_path( + folder_path + ) instance = None if instance_id: instance = self.instances[instance_id] diff --git a/client/ayon_core/tools/publisher/control_qt.py b/client/ayon_core/tools/publisher/control_qt.py index aae550681d..46b1228dc9 100644 --- a/client/ayon_core/tools/publisher/control_qt.py +++ b/client/ayon_core/tools/publisher/control_qt.py @@ -212,13 +212,13 @@ class QtRemotePublishController(BasePublisherController): pass @abstractproperty - def current_asset_name(self): - """Current context asset name from client. + def current_folder_path(self): + """Current context folder path from host. Returns: - Union[str, None]: Name of asset. - """ + Union[str, None]: Folder path. + """ pass @abstractproperty @@ -254,7 +254,7 @@ class QtRemotePublishController(BasePublisherController): def get_asset_hierarchy(self): pass - def get_existing_product_names(self, asset_name): + def get_existing_product_names(self, folder_path): pass @property diff --git a/client/ayon_core/tools/publisher/widgets/assets_dialog.py b/client/ayon_core/tools/publisher/widgets/assets_dialog.py index 9b54767624..3609095231 100644 --- a/client/ayon_core/tools/publisher/widgets/assets_dialog.py +++ b/client/ayon_core/tools/publisher/widgets/assets_dialog.py @@ -1,142 +1,48 @@ -import collections - from qtpy import QtWidgets, QtCore, QtGui -from ayon_core.tools.utils.assets_widget import ( - get_asset_icon, -) -from ayon_core.tools.utils import ( - PlaceholderLineEdit, - RecursiveSortFilterProxyModel, -) +from ayon_core.lib.events import QueuedEventSystem +from ayon_core.tools.ayon_utils.widgets import FoldersWidget +from ayon_core.tools.utils import PlaceholderLineEdit -class AssetsHierarchyModel(QtGui.QStandardItemModel): - """Assets hierarchy model. - - For selecting asset for which an instance should be created. - - Uses controller to load asset hierarchy. All asset documents are stored by - their parents. - """ +class FoldersDialogController: def __init__(self, controller): - super(AssetsHierarchyModel, self).__init__() + self._event_system = QueuedEventSystem() self._controller = controller - self._items_by_name = {} - self._items_by_path = {} - self._items_by_asset_id = {} + @property + def event_system(self): + return self._event_system - def reset(self): - self.clear() + def emit_event(self, topic, data=None, source=None): + """Use implemented event system to trigger event.""" - self._items_by_name = {} - self._items_by_path = {} - self._items_by_asset_id = {} - # assets_by_parent_id = self._controller.get_asset_hierarchy() - assets_by_parent_id = {} + if data is None: + data = {} + self.event_system.emit(topic, data, source) - items_by_name = {} - items_by_path = {} - items_by_asset_id = {} - _queue = collections.deque() - _queue.append((self.invisibleRootItem(), None, None)) - while _queue: - parent_item, parent_id, parent_path = _queue.popleft() - children = assets_by_parent_id.get(parent_id) - if not children: - continue + def register_event_callback(self, topic, callback): + self.event_system.add_callback(topic, callback) - children_by_name = { - child["name"]: child - for child in children - } - items = [] - for name in sorted(children_by_name.keys()): - child = children_by_name[name] - child_id = child["_id"] - if parent_path: - child_path = "{}/{}".format(parent_path, name) - else: - child_path = "/{}".format(name) + def get_folder_items(self, project_name, sender=None): + return self._controller.get_folder_items(project_name, sender) - 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) - item.setData(child_path, ASSET_PATH_ROLE) - - items_by_name[name] = item - items_by_path[child_path] = item - items_by_asset_id[child_id] = item - items.append(item) - _queue.append((item, child_id, child_path)) - - parent_item.appendRows(items) - - self._items_by_name = items_by_name - self._items_by_path = items_by_path - self._items_by_asset_id = items_by_asset_id - - def get_index_by_asset_id(self, asset_id): - item = self._items_by_asset_id.get(asset_id) - if item is not None: - return item.index() - return QtCore.QModelIndex() - - def get_index_by_asset_name(self, asset_name): - item = self._items_by_path.get(asset_name) - if item is None: - item = self._items_by_name.get(asset_name) - - if item is None: - return QtCore.QModelIndex() - return item.index() - - def name_is_valid(self, item_name): - return item_name in self._items_by_path - - -class AssetDialogView(QtWidgets.QTreeView): - double_clicked = QtCore.Signal(QtCore.QModelIndex) - - def mouseDoubleClickEvent(self, event): - index = self.indexAt(event.pos()) - if index.isValid(): - self.double_clicked.emit(index) - event.accept() + def set_selected_folder(self, folder_id): + pass class AssetsDialog(QtWidgets.QDialog): - """Dialog to select asset for a context of instance.""" + """Dialog to select folder for a context of instance.""" def __init__(self, controller, parent): super(AssetsDialog, self).__init__(parent) - self.setWindowTitle("Select asset") - - model = AssetsHierarchyModel(controller) - proxy_model = RecursiveSortFilterProxyModel() - proxy_model.setSourceModel(model) - proxy_model.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) + self.setWindowTitle("Select folder") filter_input = PlaceholderLineEdit(self) filter_input.setPlaceholderText("Filter folders..") - asset_view = AssetDialogView(self) - asset_view.setModel(proxy_model) - asset_view.setHeaderHidden(True) - asset_view.setFrameShape(QtWidgets.QFrame.NoFrame) - asset_view.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) - asset_view.setAlternatingRowColors(True) - asset_view.setSelectionBehavior(QtWidgets.QTreeView.SelectRows) - asset_view.setAllColumnsShowFocus(True) + folders_controller = FoldersDialogController(controller) + folders_widget = FoldersWidget(folders_controller, self) ok_btn = QtWidgets.QPushButton("OK", self) cancel_btn = QtWidgets.QPushButton("Cancel", self) @@ -148,28 +54,26 @@ class AssetsDialog(QtWidgets.QDialog): layout = QtWidgets.QVBoxLayout(self) layout.addWidget(filter_input, 0) - layout.addWidget(asset_view, 1) + layout.addWidget(folders_widget, 1) layout.addLayout(btns_layout, 0) controller.event_system.add_callback( "controller.reset.finished", self._on_controller_reset ) - asset_view.double_clicked.connect(self._on_ok_clicked) + folders_widget.double_clicked.connect(self._on_ok_clicked) filter_input.textChanged.connect(self._on_filter_change) ok_btn.clicked.connect(self._on_ok_clicked) cancel_btn.clicked.connect(self._on_cancel_clicked) + self._controller = controller self._filter_input = filter_input self._ok_btn = ok_btn self._cancel_btn = cancel_btn - self._model = model - self._proxy_model = proxy_model + self._folders_widget = folders_widget - self._asset_view = asset_view - - self._selected_asset = None + self._selected_folder_path = None # Soft refresh is enabled # - reset will happen at all cost if soft reset is enabled # - adds ability to call reset on multiple places without repeating @@ -194,7 +98,7 @@ class AssetsDialog(QtWidgets.QDialog): self._soft_reset_enabled = True def showEvent(self, event): - """Refresh asset model on show.""" + """Refresh folders widget on show.""" super(AssetsDialog, self).showEvent(event) if self._first_show: self._first_show = False @@ -203,76 +107,44 @@ class AssetsDialog(QtWidgets.QDialog): self.reset(False) def reset(self, force=True): - """Reset asset model.""" + """Reset widget.""" if not force and not self._soft_reset_enabled: return if self._soft_reset_enabled: self._soft_reset_enabled = False - self._model.reset() - - def name_is_valid(self, name): - """Is asset name valid. - - Args: - name(str): Asset name that should be checked. - """ - # Make sure we're reset - self.reset(False) - # Valid the name by model - return self._model.name_is_valid(name) + self._folders_widget.set_project_name(self._controller.project_name) def _on_filter_change(self, text): - """Trigger change of filter of assets.""" - self._proxy_model.setFilterFixedString(text) + """Trigger change of filter of folders.""" + self._folders_widget.set_name_filter(text) def _on_cancel_clicked(self): self.done(0) def _on_ok_clicked(self): - index = self._asset_view.currentIndex() - asset_name = None - if index.isValid(): - asset_name = index.data(ASSET_PATH_ROLE) - self._selected_asset = asset_name + self._selected_folder_path = ( + self._folders_widget.get_selected_folder_path() + ) self.done(1) - def set_selected_assets(self, asset_names): - """Change preselected asset before showing the dialog. + def set_selected_folders(self, folder_paths): + """Change preselected folder before showing the dialog. This also resets model and clean filter. """ self.reset(False) - self._asset_view.collapseAll() self._filter_input.setText("") - indexes = [] - for asset_name in asset_names: - index = self._model.get_index_by_asset_name(asset_name) - if index.isValid(): - indexes.append(index) + folder_id = None + for folder_path in folder_paths: + folder_id = self._controller.get_folder_id_from_path(folder_path) + if folder_id: + break + if folder_id: + self._folders_widget.set_selected_folder(folder_id) - if not indexes: - return - - index_deque = collections.deque() - for index in indexes: - index_deque.append(index) - - all_indexes = [] - while index_deque: - index = index_deque.popleft() - all_indexes.append(index) - - parent_index = index.parent() - if parent_index.isValid(): - index_deque.append(parent_index) - - for index in all_indexes: - proxy_index = self._proxy_model.mapFromSource(index) - self._asset_view.expand(proxy_index) - - def get_selected_asset(self): - """Get selected asset name.""" - return self._selected_asset + def get_selected_folder_path(self): + """Get selected folder path.""" + return self._selected_folder_path diff --git a/client/ayon_core/tools/publisher/widgets/create_context_widgets.py b/client/ayon_core/tools/publisher/widgets/create_context_widgets.py index 72db38d629..7107f786c3 100644 --- a/client/ayon_core/tools/publisher/widgets/create_context_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/create_context_widgets.py @@ -212,7 +212,7 @@ class CreateContextWidget(QtWidgets.QWidget): def update_current_context_btn(self): # Hide set current asset if there is no one - folder_path = self._controller.current_asset_name + folder_path = self._controller.current_folder_path self._current_context_btn.setVisible(bool(folder_path)) def set_selected_context(self, folder_id, task_name): @@ -252,7 +252,7 @@ class CreateContextWidget(QtWidgets.QWidget): folder_id = self._last_folder_id task_name = self._last_selected_task_name if folder_id is None: - folder_path = self._controller.current_asset_name + folder_path = self._controller.current_folder_path folder_id = self._controller.get_folder_id_from_path(folder_path) task_name = self._controller.current_task_name self._hierarchy_controller.set_selected_project( @@ -273,7 +273,7 @@ class CreateContextWidget(QtWidgets.QWidget): self.task_changed.emit() def _on_current_context_click(self): - folder_path = self._controller.current_asset_name + folder_path = self._controller.current_folder_path task_name = self._controller.current_task_name folder_id = self._controller.get_folder_id_from_path(folder_path) self._hierarchy_controller.set_expected_selection( diff --git a/client/ayon_core/tools/publisher/widgets/create_widget.py b/client/ayon_core/tools/publisher/widgets/create_widget.py index 4287e9ce07..728f1937d8 100644 --- a/client/ayon_core/tools/publisher/widgets/create_widget.py +++ b/client/ayon_core/tools/publisher/widgets/create_widget.py @@ -108,7 +108,7 @@ class CreateWidget(QtWidgets.QWidget): self._controller = controller - self._asset_name = None + self._folder_path = None self._product_names = None self._selected_creator = None @@ -314,8 +314,8 @@ class CreateWidget(QtWidgets.QWidget): self._use_current_context = True @property - def current_asset_name(self): - return self._controller.current_asset_name + def current_folder_path(self): + return self._controller.current_folder_path @property def current_task_name(self): @@ -324,13 +324,13 @@ class CreateWidget(QtWidgets.QWidget): def _context_change_is_enabled(self): return self._context_widget.is_enabled() - def _get_asset_name(self): + def _get_folder_path(self): folder_path = None if self._context_change_is_enabled(): folder_path = self._context_widget.get_selected_folder_path() if folder_path is None: - folder_path = self.current_asset_name + folder_path = self.current_folder_path return folder_path or None def _get_folder_id(self): @@ -364,12 +364,12 @@ class CreateWidget(QtWidgets.QWidget): self._use_current_context = True def refresh(self): - current_folder_path = self._controller.current_asset_name + current_folder_path = self._controller.current_folder_path current_task_name = self._controller.current_task_name # Get context before refresh to keep selection of asset and # task widgets - folder_path = self._get_asset_name() + folder_path = self._get_folder_path() task_name = self._get_task_name() # Replace by current context if last loaded context was @@ -427,7 +427,7 @@ class CreateWidget(QtWidgets.QWidget): if ( self._context_change_is_enabled() - and self._get_asset_name() is None + and self._get_folder_path() is None ): # QUESTION how to handle invalid asset? prereq_available = False @@ -449,23 +449,25 @@ class CreateWidget(QtWidgets.QWidget): self._on_variant_change() def _refresh_product_name(self): - asset_name = self._get_asset_name() + folder_path = self._get_folder_path() # Skip if asset did not change - if self._asset_name and self._asset_name == asset_name: + if self._folder_path and self._folder_path == folder_path: return - # Make sure `_asset_name` and `_product_names` variables are reset - self._asset_name = asset_name + # Make sure `_folder_path` and `_product_names` variables are reset + self._folder_path = folder_path self._product_names = None - if asset_name is None: + if folder_path is None: return - product_names = self._controller.get_existing_product_names(asset_name) + product_names = self._controller.get_existing_product_names( + folder_path + ) self._product_names = product_names if product_names is None: - self.product_name_input.setText("< Asset is not set >") + self.product_name_input.setText("< Folder is not set >") def _refresh_creators(self): # Refresh creators and add their product types to list @@ -664,13 +666,13 @@ class CreateWidget(QtWidgets.QWidget): self.product_name_input.setText("< Valid variant >") return - asset_name = self._get_asset_name() + folder_path = self._get_folder_path() task_name = self._get_task_name() creator_idenfier = self._selected_creator.identifier # Calculate product name with Creator plugin try: product_name = self._controller.get_product_name( - creator_idenfier, variant_value, task_name, asset_name + creator_idenfier, variant_value, task_name, folder_path ) except TaskNotSetError: self._create_btn.setEnabled(False) @@ -777,11 +779,11 @@ class CreateWidget(QtWidgets.QWidget): variant = self.variant_input.text() # Care about product name only if context change is enabled product_name = None - asset_name = None + folder_path = None task_name = None if self._context_change_is_enabled(): product_name = self.product_name_input.text() - asset_name = self._get_asset_name() + folder_path = self._get_folder_path() task_name = self._get_task_name() pre_create_data = self._pre_create_widget.current_value() @@ -793,7 +795,7 @@ class CreateWidget(QtWidgets.QWidget): # Where to define these data? # - what data show be stored? instance_data = { - "folderPath": asset_name, + "folderPath": folder_path, "task": task_name, "variant": variant, "productType": product_type diff --git a/client/ayon_core/tools/publisher/widgets/tasks_widget.py b/client/ayon_core/tools/publisher/widgets/tasks_widget.py index 37c32ccb97..179475a3ad 100644 --- a/client/ayon_core/tools/publisher/widgets/tasks_widget.py +++ b/client/ayon_core/tools/publisher/widgets/tasks_widget.py @@ -10,11 +10,11 @@ TASK_ORDER_ROLE = QtCore.Qt.UserRole + 3 class TasksModel(QtGui.QStandardItemModel): """Tasks model. - Task model must have set context of asset documents. + Task model must have set context of folder paths. - Items in model are based on 0-infinite asset documents. Always contain - an interserction of context asset tasks. When no assets are in context - them model is empty if 2 or more are in context assets that don't have + Items in model are based on 0-infinite folders. Always contain + an interserction of context folder tasks. When no folders are in context + them model is empty if 2 or more are in context folders that don't have tasks with same names then model is empty too. Args: @@ -27,21 +27,21 @@ class TasksModel(QtGui.QStandardItemModel): self._allow_empty_task = allow_empty_task self._controller = controller self._items_by_name = {} - self._asset_names = [] - self._task_names_by_asset_name = {} + self._folder_paths = [] + self._task_names_by_folder_path = {} - def set_asset_names(self, asset_names): + def set_folder_paths(self, folder_paths): """Set assets context.""" - self._asset_names = asset_names + self._folder_paths = folder_paths self.reset() @staticmethod - def get_intersection_of_tasks(task_names_by_asset_name): + def get_intersection_of_tasks(task_names_by_folder_path): """Calculate intersection of task names from passed data. Example: ``` - # Passed `task_names_by_asset_name` + # Passed `task_names_by_folder_path` { "asset_1": ["compositing", "animation"], "asset_2": ["compositing", "editorial"] @@ -54,10 +54,10 @@ class TasksModel(QtGui.QStandardItemModel): ``` Args: - task_names_by_asset_name (dict): Task names in iterable by parent. + task_names_by_folder_path (dict): Task names in iterable by parent. """ tasks = None - for task_names in task_names_by_asset_name.values(): + for task_names in task_names_by_folder_path.values(): if tasks is None: tasks = set(task_names) else: @@ -67,41 +67,43 @@ class TasksModel(QtGui.QStandardItemModel): break return tasks or set() - def is_task_name_valid(self, asset_name, task_name): - """Is task name available for asset. + def is_task_name_valid(self, folder_path, task_name): + """Is task name available for folder. Args: - asset_name (str): Name of asset where should look for task. - task_name (str): Name of task which should be available in asset's + folder_path (str): Name of asset where should look for task. + task_name (str): Name of task which should be available in folder tasks. """ - if asset_name not in self._task_names_by_asset_name: + if folder_path not in self._task_names_by_folder_path: return False if self._allow_empty_task and not task_name: return True - task_names = self._task_names_by_asset_name[asset_name] + task_names = self._task_names_by_folder_path[folder_path] if task_name in task_names: return True return False def reset(self): """Update model by current context.""" - if not self._asset_names: + if not self._folder_paths: self._items_by_name = {} - self._task_names_by_asset_name = {} + self._task_names_by_folder_path = {} self.clear() return - task_names_by_asset_name = ( - self._controller.get_task_names_by_asset_names(self._asset_names) + task_names_by_folder_path = ( + self._controller.get_task_names_by_folder_paths( + self._folder_paths + ) ) - self._task_names_by_asset_name = task_names_by_asset_name + self._task_names_by_folder_path = task_names_by_folder_path new_task_names = self.get_intersection_of_tasks( - task_names_by_asset_name + task_names_by_folder_path ) if self._allow_empty_task: new_task_names.add("") diff --git a/client/ayon_core/tools/publisher/widgets/widgets.py b/client/ayon_core/tools/publisher/widgets/widgets.py index 6a3f097fe9..fb387dba85 100644 --- a/client/ayon_core/tools/publisher/widgets/widgets.py +++ b/client/ayon_core/tools/publisher/widgets/widgets.py @@ -481,7 +481,7 @@ class AssetsField(BaseClickableFrame): if not result: return - asset_name = self._dialog.get_selected_asset() + asset_name = self._dialog.get_selected_folder_path() if asset_name is None: return @@ -495,7 +495,7 @@ class AssetsField(BaseClickableFrame): self.value_changed.emit() def _mouse_release_callback(self): - self._dialog.set_selected_assets(self._selected_items) + self._dialog.set_selected_folders(self._selected_items) self._dialog.open() def set_multiselection_text(self, text): From 259983768cac374ae721df59694d00ae2a413eb5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Feb 2024 16:11:38 +0100 Subject: [PATCH 359/573] use folder naming in widgets --- .../{assets_dialog.py => folders_dialog.py} | 6 +- .../tools/publisher/widgets/widgets.py | 174 +++++++++--------- 2 files changed, 90 insertions(+), 90 deletions(-) rename client/ayon_core/tools/publisher/widgets/{assets_dialog.py => folders_dialog.py} (97%) diff --git a/client/ayon_core/tools/publisher/widgets/assets_dialog.py b/client/ayon_core/tools/publisher/widgets/folders_dialog.py similarity index 97% rename from client/ayon_core/tools/publisher/widgets/assets_dialog.py rename to client/ayon_core/tools/publisher/widgets/folders_dialog.py index 3609095231..bccbb600a8 100644 --- a/client/ayon_core/tools/publisher/widgets/assets_dialog.py +++ b/client/ayon_core/tools/publisher/widgets/folders_dialog.py @@ -31,11 +31,11 @@ class FoldersDialogController: pass -class AssetsDialog(QtWidgets.QDialog): +class FoldersDialog(QtWidgets.QDialog): """Dialog to select folder for a context of instance.""" def __init__(self, controller, parent): - super(AssetsDialog, self).__init__(parent) + super(FoldersDialog, self).__init__(parent) self.setWindowTitle("Select folder") filter_input = PlaceholderLineEdit(self) @@ -99,7 +99,7 @@ class AssetsDialog(QtWidgets.QDialog): def showEvent(self, event): """Refresh folders widget on show.""" - super(AssetsDialog, self).showEvent(event) + super(FoldersDialog, self).showEvent(event) if self._first_show: self._first_show = False self._on_first_show() diff --git a/client/ayon_core/tools/publisher/widgets/widgets.py b/client/ayon_core/tools/publisher/widgets/widgets.py index fb387dba85..35b4ff46d0 100644 --- a/client/ayon_core/tools/publisher/widgets/widgets.py +++ b/client/ayon_core/tools/publisher/widgets/widgets.py @@ -26,7 +26,7 @@ from ayon_core.pipeline.create import ( TaskNotSetError, ) from .thumbnail_widget import ThumbnailWidget -from .assets_dialog import AssetsDialog +from .folders_dialog import FoldersDialog from .tasks_widget import TasksModel from .icons import ( get_pixmap, @@ -422,20 +422,20 @@ class ClickableLineEdit(QtWidgets.QLineEdit): event.accept() -class AssetsField(BaseClickableFrame): - """Field where asset name of selected instance/s is showed. +class FoldersFields(BaseClickableFrame): + """Field where folder path of selected instance/s is showed. - Click on the field will trigger `AssetsDialog`. + Click on the field will trigger `FoldersDialog`. """ value_changed = QtCore.Signal() def __init__(self, controller, parent): - super(AssetsField, self).__init__(parent) + super(FoldersFields, self).__init__(parent) self.setObjectName("AssetNameInputWidget") # Don't use 'self' for parent! # - this widget has specific styles - dialog = AssetsDialog(controller, parent) + dialog = FoldersDialog(controller, parent) name_input = ClickableLineEdit(self) name_input.setObjectName("AssetNameInput") @@ -481,15 +481,15 @@ class AssetsField(BaseClickableFrame): if not result: return - asset_name = self._dialog.get_selected_folder_path() - if asset_name is None: + folder_path = self._dialog.get_selected_folder_path() + if folder_path is None: return - self._selected_items = [asset_name] + self._selected_items = [folder_path] self._has_value_changed = ( self._origin_value != self._selected_items ) - self.set_text(asset_name) + self.set_text(folder_path) self._set_is_valid(True) self.value_changed.emit() @@ -499,10 +499,10 @@ class AssetsField(BaseClickableFrame): self._dialog.open() def set_multiselection_text(self, text): - """Change text for multiselection of different assets. + """Change text for multiselection of different folders. When there are selected multiple instances at once and they don't have - same asset in context. + same folder in context. """ self._multiselection_text = text @@ -521,30 +521,30 @@ class AssetsField(BaseClickableFrame): set_style_property(self._icon_btn, "state", state) def is_valid(self): - """Is asset valid.""" + """Is folder valid.""" return self._is_valid def has_value_changed(self): - """Value of asset has changed.""" + """Value of folder has changed.""" return self._has_value_changed def get_selected_items(self): - """Selected asset names.""" + """Selected folder paths.""" return list(self._selected_items) def set_text(self, text): """Set text in text field. - Does not change selected items (assets). + Does not change selected items (folders). """ self._name_input.setText(text) self._name_input.end(False) def set_selected_items(self, folder_paths=None): - """Set asset names for selection of instances. + """Set folder paths for selection of instances. - Passed asset names are validated and if there are 2 or more different - asset names then multiselection text is shown. + Passed folder paths are validated and if there are 2 or more different + folder paths then multiselection text is shown. Args: folder_paths (list, tuple, set, NoneType): List of folder paths. @@ -572,7 +572,7 @@ class AssetsField(BaseClickableFrame): self._set_is_valid(is_valid) def reset_to_origin(self): - """Change to asset names set with last `set_selected_items` call.""" + """Change to folder paths set with last `set_selected_items` call.""" self.set_selected_items(self._origin_value) def confirm_value(self): @@ -606,9 +606,9 @@ class TasksCombobox(QtWidgets.QComboBox): """Combobox to show tasks for selected instances. Combobox gives ability to select only from intersection of task names for - asset names in selected instances. + folder paths in selected instances. - If asset names in selected instances does not have same tasks then combobox + If folder paths in selected instances does not have same tasks then combobox will be empty. """ value_changed = QtCore.Signal() @@ -742,23 +742,23 @@ class TasksCombobox(QtWidgets.QComboBox): """ return list(self._selected_items) - def set_folder_paths(self, asset_names): - """Set asset names for which should show tasks.""" + def set_folder_paths(self, folder_paths): + """Set folder paths for which should show tasks.""" self._ignore_index_change = True - self._model.set_folder_paths(asset_names) + self._model.set_folder_paths(folder_paths) self._proxy_model.set_filter_empty(False) self._proxy_model.sort(0) self._ignore_index_change = False - # It is a bug if not exactly one asset got here - if len(asset_names) != 1: + # It is a bug if not exactly one folder got here + if len(folder_paths) != 1: self.set_selected_item("") self._set_is_valid(False) return - asset_name = tuple(asset_names)[0] + folder_path = tuple(folder_paths)[0] is_valid = False if self._selected_items: @@ -766,7 +766,7 @@ class TasksCombobox(QtWidgets.QComboBox): valid_task_names = [] for task_name in self._selected_items: - _is_valid = self._model.is_task_name_valid(asset_name, task_name) + _is_valid = self._model.is_task_name_valid(folder_path, task_name) if _is_valid: valid_task_names.append(task_name) else: @@ -787,42 +787,42 @@ class TasksCombobox(QtWidgets.QComboBox): self._set_is_valid(is_valid) - def confirm_value(self, asset_names): + def confirm_value(self, folder_paths): new_task_name = self._selected_items[0] self._origin_value = [ - (asset_name, new_task_name) - for asset_name in asset_names + (folder_path, new_task_name) + for folder_path in folder_paths ] self._origin_selection = copy.deepcopy(self._selected_items) self._has_value_changed = False - def set_selected_items(self, asset_task_combinations=None): + def set_selected_items(self, folder_task_combinations=None): """Set items for selected instances. Args: - asset_task_combinations (list): List of tuples. Each item in - the list contain asset name and task name. + folder_task_combinations (list): List of tuples. Each item in + the list contain folder path and task name. """ self._proxy_model.set_filter_empty(False) self._proxy_model.sort(0) - if asset_task_combinations is None: - asset_task_combinations = [] + if folder_task_combinations is None: + folder_task_combinations = [] task_names = set() - task_names_by_asset_name = collections.defaultdict(set) - for asset_name, task_name in asset_task_combinations: + task_names_by_folder_path = collections.defaultdict(set) + for folder_path, task_name in folder_task_combinations: task_names.add(task_name) - task_names_by_asset_name[asset_name].add(task_name) - asset_names = set(task_names_by_asset_name.keys()) + task_names_by_folder_path[folder_path].add(task_name) + folder_paths = set(task_names_by_folder_path.keys()) self._ignore_index_change = True - self._model.set_folder_paths(asset_names) + self._model.set_folder_paths(folder_paths) self._has_value_changed = False - self._origin_value = copy.deepcopy(asset_task_combinations) + self._origin_value = copy.deepcopy(folder_task_combinations) self._origin_selection = list(task_names) self._selected_items = list(task_names) @@ -836,9 +836,9 @@ class TasksCombobox(QtWidgets.QComboBox): task_name = tuple(task_names)[0] idx = self.findText(task_name) is_valid = not idx < 0 - if not is_valid and len(asset_names) > 1: - is_valid = self._validate_task_names_by_asset_names( - task_names_by_asset_name + if not is_valid and len(folder_paths) > 1: + is_valid = self._validate_task_names_by_folder_paths( + task_names_by_folder_path ) self.set_selected_item(task_name) @@ -849,9 +849,9 @@ class TasksCombobox(QtWidgets.QComboBox): if not is_valid: break - if not is_valid and len(asset_names) > 1: - is_valid = self._validate_task_names_by_asset_names( - task_names_by_asset_name + if not is_valid and len(folder_paths) > 1: + is_valid = self._validate_task_names_by_folder_paths( + task_names_by_folder_path ) multiselection_text = self._multiselection_text if multiselection_text is None: @@ -864,10 +864,10 @@ class TasksCombobox(QtWidgets.QComboBox): self.value_changed.emit() - def _validate_task_names_by_asset_names(self, task_names_by_asset_name): - for asset_name, task_names in task_names_by_asset_name.items(): + def _validate_task_names_by_folder_paths(self, task_names_by_folder_path): + for folder_path, task_names in task_names_by_folder_path.items(): for task_name in task_names: - if not self._model.is_task_name_valid(asset_name, task_name): + if not self._model.is_task_name_valid(folder_path, task_name): return False return True @@ -1102,17 +1102,17 @@ class GlobalAttrsWidget(QtWidgets.QWidget): self._current_instances = [] variant_input = VariantInputWidget(self) - asset_value_widget = AssetsField(controller, self) + folder_value_widget = FoldersFields(controller, self) task_value_widget = TasksCombobox(controller, self) product_type_value_widget = MultipleItemWidget(self) product_value_widget = MultipleItemWidget(self) variant_input.set_multiselection_text(self.multiselection_text) - asset_value_widget.set_multiselection_text(self.multiselection_text) + folder_value_widget.set_multiselection_text(self.multiselection_text) task_value_widget.set_multiselection_text(self.multiselection_text) variant_input.set_value() - asset_value_widget.set_selected_items() + folder_value_widget.set_selected_items() task_value_widget.set_selected_items() product_type_value_widget.set_value() product_value_widget.set_value() @@ -1133,20 +1133,20 @@ class GlobalAttrsWidget(QtWidgets.QWidget): main_layout.setHorizontalSpacing(INPUTS_LAYOUT_HSPACING) main_layout.setVerticalSpacing(INPUTS_LAYOUT_VSPACING) main_layout.addRow("Variant", variant_input) - main_layout.addRow("Folder", asset_value_widget) + main_layout.addRow("Folder", folder_value_widget) main_layout.addRow("Task", task_value_widget) main_layout.addRow("Product type", product_type_value_widget) main_layout.addRow("Product name", product_value_widget) main_layout.addRow(btns_layout) variant_input.value_changed.connect(self._on_variant_change) - asset_value_widget.value_changed.connect(self._on_asset_change) + folder_value_widget.value_changed.connect(self._on_folder_change) task_value_widget.value_changed.connect(self._on_task_change) submit_btn.clicked.connect(self._on_submit) cancel_btn.clicked.connect(self._on_cancel) self.variant_input = variant_input - self.asset_value_widget = asset_value_widget + self.folder_value_widget = folder_value_widget self.task_value_widget = task_value_widget self.product_type_value_widget = product_type_value_widget self.product_value_widget = product_value_widget @@ -1157,40 +1157,40 @@ class GlobalAttrsWidget(QtWidgets.QWidget): """Commit changes for selected instances.""" variant_value = None - asset_name = None + folder_path = None task_name = None if self.variant_input.has_value_changed(): variant_value = self.variant_input.get_value()[0] - if self.asset_value_widget.has_value_changed(): - asset_name = self.asset_value_widget.get_selected_items()[0] + if self.folder_value_widget.has_value_changed(): + folder_path = self.folder_value_widget.get_selected_items()[0] if self.task_value_widget.has_value_changed(): task_name = self.task_value_widget.get_selected_items()[0] product_names = set() invalid_tasks = False - asset_names = [] + folder_paths = [] for instance in self._current_instances: new_variant_value = instance.get("variant") - new_asset_name = instance.get("folderPath") + new_folder_path = instance.get("folderPath") 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 folder_path is not None: + new_folder_path = folder_path if task_name is not None: new_task_name = task_name - asset_names.append(new_asset_name) + folder_paths.append(new_folder_path) try: new_product_name = self._controller.get_product_name( instance.creator_identifier, new_variant_value, new_task_name, - new_asset_name, + new_folder_path, instance.id, ) @@ -1204,8 +1204,8 @@ class GlobalAttrsWidget(QtWidgets.QWidget): if variant_value is not None: instance["variant"] = variant_value - if asset_name is not None: - instance["folderPath"] = asset_name + if folder_path is not None: + instance["folderPath"] = folder_path instance.set_asset_invalid(False) if task_name is not None: @@ -1225,11 +1225,11 @@ class GlobalAttrsWidget(QtWidgets.QWidget): if variant_value is not None: self.variant_input.confirm_value() - if asset_name is not None: - self.asset_value_widget.confirm_value() + if folder_path is not None: + self.folder_value_widget.confirm_value() if task_name is not None: - self.task_value_widget.confirm_value(asset_names) + self.task_value_widget.confirm_value(folder_paths) self.instance_context_changed.emit() @@ -1237,19 +1237,19 @@ class GlobalAttrsWidget(QtWidgets.QWidget): """Cancel changes and set back to their irigin value.""" self.variant_input.reset_to_origin() - self.asset_value_widget.reset_to_origin() + self.folder_value_widget.reset_to_origin() self.task_value_widget.reset_to_origin() self._set_btns_enabled(False) def _on_value_change(self): any_invalid = ( not self.variant_input.is_valid() - or not self.asset_value_widget.is_valid() + or not self.folder_value_widget.is_valid() or not self.task_value_widget.is_valid() ) any_changed = ( self.variant_input.has_value_changed() - or self.asset_value_widget.has_value_changed() + or self.folder_value_widget.has_value_changed() or self.task_value_widget.has_value_changed() ) self._set_btns_visible(any_changed or any_invalid) @@ -1259,9 +1259,9 @@ class GlobalAttrsWidget(QtWidgets.QWidget): def _on_variant_change(self): self._on_value_change() - def _on_asset_change(self): - asset_names = self.asset_value_widget.get_selected_items() - self.task_value_widget.set_folder_paths(asset_names) + def _on_folder_change(self): + folder_paths = self.folder_value_widget.get_selected_items() + self.task_value_widget.set_folder_paths(folder_paths) self._on_value_change() def _on_task_change(self): @@ -1286,7 +1286,7 @@ class GlobalAttrsWidget(QtWidgets.QWidget): self._current_instances = instances - asset_names = set() + folder_paths = set() variants = set() product_types = set() product_names = set() @@ -1295,7 +1295,7 @@ class GlobalAttrsWidget(QtWidgets.QWidget): if len(instances) == 0: editable = False - asset_task_combinations = [] + folder_task_combinations = [] for instance in instances: # NOTE I'm not sure how this can even happen? if instance.creator_identifier is None: @@ -1303,23 +1303,23 @@ class GlobalAttrsWidget(QtWidgets.QWidget): variants.add(instance.get("variant") or self.unknown_value) product_types.add(instance.get("productType") or self.unknown_value) - asset_name = instance.get("folderPath") or self.unknown_value + folder_path = instance.get("folderPath") or self.unknown_value task_name = instance.get("task") or "" - asset_names.add(asset_name) - asset_task_combinations.append((asset_name, task_name)) + folder_paths.add(folder_path) + folder_task_combinations.append((folder_path, task_name)) product_names.add(instance.get("productName") or self.unknown_value) self.variant_input.set_value(variants) - # Set context of asset widget - self.asset_value_widget.set_selected_items(asset_names) + # Set context of folder widget + self.folder_value_widget.set_selected_items(folder_paths) # Set context of task widget - self.task_value_widget.set_selected_items(asset_task_combinations) + self.task_value_widget.set_selected_items(folder_task_combinations) self.product_type_value_widget.set_value(product_types) self.product_value_widget.set_value(product_names) self.variant_input.setEnabled(editable) - self.asset_value_widget.setEnabled(editable) + self.folder_value_widget.setEnabled(editable) self.task_value_widget.setEnabled(editable) From 9d4b98be45c094767784e40bc3cb419d4003591e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Feb 2024 16:16:38 +0100 Subject: [PATCH 360/573] moved tasks model around --- .../{tasks_widget.py => tasks_model.py} | 28 ++++++------------- .../tools/publisher/widgets/widgets.py | 2 +- 2 files changed, 10 insertions(+), 20 deletions(-) rename client/ayon_core/tools/publisher/widgets/{tasks_widget.py => tasks_model.py} (84%) diff --git a/client/ayon_core/tools/publisher/widgets/tasks_widget.py b/client/ayon_core/tools/publisher/widgets/tasks_model.py similarity index 84% rename from client/ayon_core/tools/publisher/widgets/tasks_widget.py rename to client/ayon_core/tools/publisher/widgets/tasks_model.py index 179475a3ad..8f00dc37a2 100644 --- a/client/ayon_core/tools/publisher/widgets/tasks_widget.py +++ b/client/ayon_core/tools/publisher/widgets/tasks_model.py @@ -31,7 +31,7 @@ class TasksModel(QtGui.QStandardItemModel): self._task_names_by_folder_path = {} def set_folder_paths(self, folder_paths): - """Set assets context.""" + """Set folders context.""" self._folder_paths = folder_paths self.reset() @@ -43,8 +43,8 @@ class TasksModel(QtGui.QStandardItemModel): ``` # Passed `task_names_by_folder_path` { - "asset_1": ["compositing", "animation"], - "asset_2": ["compositing", "editorial"] + "/folder_1": ["compositing", "animation"], + "/folder_2": ["compositing", "editorial"] } ``` Result: @@ -70,8 +70,11 @@ class TasksModel(QtGui.QStandardItemModel): def is_task_name_valid(self, folder_path, task_name): """Is task name available for folder. + Todos: + Move this method to PublisherController. + Args: - folder_path (str): Name of asset where should look for task. + folder_path (str): Fodler path where should look for task. task_name (str): Name of task which should be available in folder tasks. """ @@ -91,7 +94,8 @@ class TasksModel(QtGui.QStandardItemModel): if not self._folder_paths: self._items_by_name = {} self._task_names_by_folder_path = {} - self.clear() + root_item = self.invisibleRootItem() + root_item.removeRows(0, self.rowCount()) return task_names_by_folder_path = ( @@ -131,17 +135,3 @@ class TasksModel(QtGui.QStandardItemModel): if new_items: root_item.appendRows(new_items) - - def headerData(self, section, orientation, role=None): - if role is None: - role = QtCore.Qt.EditRole - # Show nice labels in the header - if section == 0: - if ( - role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole) - and orientation == QtCore.Qt.Horizontal - ): - return "Tasks" - - return super(TasksModel, self).headerData(section, orientation, role) - diff --git a/client/ayon_core/tools/publisher/widgets/widgets.py b/client/ayon_core/tools/publisher/widgets/widgets.py index 35b4ff46d0..efc47555d5 100644 --- a/client/ayon_core/tools/publisher/widgets/widgets.py +++ b/client/ayon_core/tools/publisher/widgets/widgets.py @@ -27,7 +27,7 @@ from ayon_core.pipeline.create import ( ) from .thumbnail_widget import ThumbnailWidget from .folders_dialog import FoldersDialog -from .tasks_widget import TasksModel +from .tasks_model import TasksModel from .icons import ( get_pixmap, get_icon_path From 9b0dcce5a82d9155b5b735b17431edce63d9f88a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Feb 2024 16:21:51 +0100 Subject: [PATCH 361/573] rename 'AssetNameInputWidget' to 'FolderPathInputWidget' --- client/ayon_core/style/style.css | 12 ++++++------ client/ayon_core/tools/publisher/widgets/widgets.py | 2 +- .../sceneinventory/switch_dialog/folders_input.py | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/style/style.css b/client/ayon_core/style/style.css index ce467663fc..e6d427a136 100644 --- a/client/ayon_core/style/style.css +++ b/client/ayon_core/style/style.css @@ -1138,13 +1138,13 @@ ValidationArtistMessage QLabel { font-size: 13pt; } -#AssetNameInputWidget { +#FolderPathInputWidget { background: {color:bg-inputs}; border: 1px solid {color:border}; border-radius: 0.2em; } -#AssetNameInputWidget QWidget { +#FolderPathInputWidget QWidget { background: transparent; } @@ -1165,17 +1165,17 @@ ValidationArtistMessage QLabel { border: none; } -#AssetNameInputWidget:hover { +#FolderPathInputWidget:hover { border-color: {color:border-hover}; } -#AssetNameInputWidget:focus{ +#FolderPathInputWidget:focus{ border-color: {color:border-focus}; } -#AssetNameInputWidget:disabled { +#FolderPathInputWidget:disabled { background: {color:bg-inputs-disabled}; } -#TasksCombobox[state="invalid"], #AssetNameInputWidget[state="invalid"], #AssetNameInputButton[state="invalid"] { +#TasksCombobox[state="invalid"], #FolderPathInputWidget[state="invalid"], #AssetNameInputButton[state="invalid"] { border-color: {color:publisher:error}; } diff --git a/client/ayon_core/tools/publisher/widgets/widgets.py b/client/ayon_core/tools/publisher/widgets/widgets.py index efc47555d5..dda204fbfd 100644 --- a/client/ayon_core/tools/publisher/widgets/widgets.py +++ b/client/ayon_core/tools/publisher/widgets/widgets.py @@ -431,7 +431,7 @@ class FoldersFields(BaseClickableFrame): def __init__(self, controller, parent): super(FoldersFields, self).__init__(parent) - self.setObjectName("AssetNameInputWidget") + self.setObjectName("FolderPathInputWidget") # Don't use 'self' for parent! # - this widget has specific styles diff --git a/client/ayon_core/tools/sceneinventory/switch_dialog/folders_input.py b/client/ayon_core/tools/sceneinventory/switch_dialog/folders_input.py index 2358a82a7f..8f6a12e8e3 100644 --- a/client/ayon_core/tools/sceneinventory/switch_dialog/folders_input.py +++ b/client/ayon_core/tools/sceneinventory/switch_dialog/folders_input.py @@ -196,7 +196,7 @@ class FoldersField(BaseClickableFrame): def __init__(self, controller, parent): super(FoldersField, self).__init__(parent) - self.setObjectName("AssetNameInputWidget") + self.setObjectName("FolderPathInputWidget") # Don't use 'self' for parent! # - this widget has specific styles From 284d395b01f8de84591e86dbcd750eedce1a566e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Feb 2024 16:22:47 +0100 Subject: [PATCH 362/573] rename 'AssetNameInput' and 'AssetNameInputButton' --- client/ayon_core/style/style.css | 6 +++--- client/ayon_core/tools/publisher/widgets/widgets.py | 4 ++-- .../tools/sceneinventory/switch_dialog/folders_input.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/style/style.css b/client/ayon_core/style/style.css index e6d427a136..fcc76b0bff 100644 --- a/client/ayon_core/style/style.css +++ b/client/ayon_core/style/style.css @@ -1148,7 +1148,7 @@ ValidationArtistMessage QLabel { background: transparent; } -#AssetNameInputButton { +#FolderPathInputButton { border-bottom-left-radius: 0px; border-top-left-radius: 0px; padding: 0px; @@ -1159,7 +1159,7 @@ ValidationArtistMessage QLabel { border-bottom: none; } -#AssetNameInput { +#FolderPathInput { border-bottom-right-radius: 0px; border-top-right-radius: 0px; border: none; @@ -1175,7 +1175,7 @@ ValidationArtistMessage QLabel { background: {color:bg-inputs-disabled}; } -#TasksCombobox[state="invalid"], #FolderPathInputWidget[state="invalid"], #AssetNameInputButton[state="invalid"] { +#TasksCombobox[state="invalid"], #FolderPathInputWidget[state="invalid"], #FolderPathInputButton[state="invalid"] { border-color: {color:publisher:error}; } diff --git a/client/ayon_core/tools/publisher/widgets/widgets.py b/client/ayon_core/tools/publisher/widgets/widgets.py index dda204fbfd..4005cf2c84 100644 --- a/client/ayon_core/tools/publisher/widgets/widgets.py +++ b/client/ayon_core/tools/publisher/widgets/widgets.py @@ -438,13 +438,13 @@ class FoldersFields(BaseClickableFrame): dialog = FoldersDialog(controller, parent) name_input = ClickableLineEdit(self) - name_input.setObjectName("AssetNameInput") + name_input.setObjectName("FolderPathInput") icon_name = "fa.window-maximize" icon = qtawesome.icon(icon_name, color="white") icon_btn = QtWidgets.QPushButton(self) icon_btn.setIcon(icon) - icon_btn.setObjectName("AssetNameInputButton") + icon_btn.setObjectName("FolderPathInputButton") layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) diff --git a/client/ayon_core/tools/sceneinventory/switch_dialog/folders_input.py b/client/ayon_core/tools/sceneinventory/switch_dialog/folders_input.py index 8f6a12e8e3..e46c28474f 100644 --- a/client/ayon_core/tools/sceneinventory/switch_dialog/folders_input.py +++ b/client/ayon_core/tools/sceneinventory/switch_dialog/folders_input.py @@ -203,12 +203,12 @@ class FoldersField(BaseClickableFrame): dialog = FoldersDialog(controller, parent) name_input = ClickableLineEdit(self) - name_input.setObjectName("AssetNameInput") + name_input.setObjectName("FolderPathInput") icon = qtawesome.icon("fa.window-maximize", color="white") icon_btn = QtWidgets.QPushButton(self) icon_btn.setIcon(icon) - icon_btn.setObjectName("AssetNameInputButton") + icon_btn.setObjectName("FolderPathInputButton") layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) From 93ad1ad9a3b5cb19c43c0a13209202d95df7f26e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Feb 2024 16:23:22 +0100 Subject: [PATCH 363/573] more folder naming --- client/ayon_core/tools/publisher/control.py | 10 +++++----- client/ayon_core/tools/publisher/control_qt.py | 9 +++------ .../publisher/widgets/create_context_widgets.py | 2 +- .../tools/publisher/widgets/create_widget.py | 14 +++++++------- 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/client/ayon_core/tools/publisher/control.py b/client/ayon_core/tools/publisher/control.py index f59ffa9588..6408056c04 100644 --- a/client/ayon_core/tools/publisher/control.py +++ b/client/ayon_core/tools/publisher/control.py @@ -1062,7 +1062,7 @@ class AbstractPublisherController(object): creator_identifier, variant, task_name, - asset_name, + folder_path, instance_id=None ): """Get product name based on passed data. @@ -1072,7 +1072,7 @@ class AbstractPublisherController(object): responsible for product name creation. variant (str): Variant value from user's input. task_name (str): Name of task for which is instance created. - asset_name (str): Name of asset for which is instance created. + folder_path (str): Folder path for which is instance created. instance_id (Union[str, None]): Existing instance id when product name is updated. """ @@ -1091,7 +1091,7 @@ class AbstractPublisherController(object): creator_identifier (str): Identifier of Creator plugin. product_name (str): Calculated product name. instance_data (Dict[str, Any]): Base instance data with variant, - asset name and task name. + folder path and task name. options (Dict[str, Any]): Data from pre-create attributes. """ @@ -1667,10 +1667,10 @@ class PublisherController(BasePublisherController): @property def current_folder_path(self): - """Current context asset name defined by host. + """Current context folder path defined by host. Returns: - Union[str, None]: Asset name or None if asset is not set. + Union[str, None]: Folder path or None if folder is not set. """ return self._create_context.get_current_asset_name() diff --git a/client/ayon_core/tools/publisher/control_qt.py b/client/ayon_core/tools/publisher/control_qt.py index 46b1228dc9..ee08899cac 100644 --- a/client/ayon_core/tools/publisher/control_qt.py +++ b/client/ayon_core/tools/publisher/control_qt.py @@ -251,9 +251,6 @@ class QtRemotePublishController(BasePublisherController): pass - def get_asset_hierarchy(self): - pass - def get_existing_product_names(self, folder_path): pass @@ -299,7 +296,7 @@ class QtRemotePublishController(BasePublisherController): creator_identifier, variant, task_name, - asset_name, + folder_path, instance_id=None ): """Get product name based on passed data. @@ -309,7 +306,7 @@ class QtRemotePublishController(BasePublisherController): responsible for product name creation. variant (str): Variant value from user's input. task_name (str): Name of task for which is instance created. - asset_name (str): Name of asset for which is instance created. + folder_path (str): Folder path for which is instance created. instance_id (Union[str, None]): Existing instance id when product name is updated. """ @@ -328,7 +325,7 @@ class QtRemotePublishController(BasePublisherController): creator_identifier (str): Identifier of Creator plugin. product_name (str): Calculated product name. instance_data (Dict[str, Any]): Base instance data with variant, - asset name and task name. + folder path and task name. options (Dict[str, Any]): Data from pre-create attributes. """ diff --git a/client/ayon_core/tools/publisher/widgets/create_context_widgets.py b/client/ayon_core/tools/publisher/widgets/create_context_widgets.py index 7107f786c3..dc80f47631 100644 --- a/client/ayon_core/tools/publisher/widgets/create_context_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/create_context_widgets.py @@ -211,7 +211,7 @@ class CreateContextWidget(QtWidgets.QWidget): return self._tasks_widget.get_selected_task_type() def update_current_context_btn(self): - # Hide set current asset if there is no one + # Hide set current folder if there is no one folder_path = self._controller.current_folder_path self._current_context_btn.setVisible(bool(folder_path)) diff --git a/client/ayon_core/tools/publisher/widgets/create_widget.py b/client/ayon_core/tools/publisher/widgets/create_widget.py index 728f1937d8..2e4ca34138 100644 --- a/client/ayon_core/tools/publisher/widgets/create_widget.py +++ b/client/ayon_core/tools/publisher/widgets/create_widget.py @@ -342,7 +342,7 @@ class CreateWidget(QtWidgets.QWidget): def _get_task_name(self): task_name = None if self._context_change_is_enabled(): - # Don't use selection of task if asset is not set + # Don't use selection of task if folder is not set folder_path = self._context_widget.get_selected_folder_path() if folder_path: task_name = self._context_widget.get_selected_task_name() @@ -367,7 +367,7 @@ class CreateWidget(QtWidgets.QWidget): current_folder_path = self._controller.current_folder_path current_task_name = self._controller.current_task_name - # Get context before refresh to keep selection of asset and + # Get context before refresh to keep selection of folder and # task widgets folder_path = self._get_folder_path() task_name = self._get_task_name() @@ -392,8 +392,8 @@ class CreateWidget(QtWidgets.QWidget): self._prereq_available = False - # Disable context widget so refresh of asset will use context asset - # name + # Disable context widget so refresh of folder will use context folder + # path self._set_context_enabled(False) # Refresh data before update of creators @@ -429,7 +429,7 @@ class CreateWidget(QtWidgets.QWidget): self._context_change_is_enabled() and self._get_folder_path() is None ): - # QUESTION how to handle invalid asset? + # QUESTION how to handle invalid folder? prereq_available = False creator_btn_tooltips.append("Context is not selected") @@ -451,7 +451,7 @@ class CreateWidget(QtWidgets.QWidget): def _refresh_product_name(self): folder_path = self._get_folder_path() - # Skip if asset did not change + # Skip if folder did not change if self._folder_path and self._folder_path == folder_path: return @@ -686,7 +686,7 @@ class CreateWidget(QtWidgets.QWidget): self._validate_product_name(product_name, variant_value) def _validate_product_name(self, product_name, variant_value): - # Get all products of the current asset + # Get all products of the current folder if self._product_names: existing_product_names = set(self._product_names) else: From 4b901af4fc6e0022163250808132feaba32216e9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Feb 2024 16:28:50 +0100 Subject: [PATCH 364/573] folders widget are deselectable --- .../ayon_core/tools/publisher/widgets/create_context_widgets.py | 2 ++ client/ayon_core/tools/publisher/widgets/folders_dialog.py | 1 + 2 files changed, 3 insertions(+) diff --git a/client/ayon_core/tools/publisher/widgets/create_context_widgets.py b/client/ayon_core/tools/publisher/widgets/create_context_widgets.py index dc80f47631..e17d3d1603 100644 --- a/client/ayon_core/tools/publisher/widgets/create_context_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/create_context_widgets.py @@ -176,6 +176,8 @@ class CreateContextWidget(QtWidgets.QWidget): folders_widget = FoldersWidget( hierarchy_controller, self, handle_expected_selection=True ) + folders_widget.set_deselectable(True) + tasks_widget = TasksWidget( hierarchy_controller, self, handle_expected_selection=True ) diff --git a/client/ayon_core/tools/publisher/widgets/folders_dialog.py b/client/ayon_core/tools/publisher/widgets/folders_dialog.py index bccbb600a8..8f93264b2e 100644 --- a/client/ayon_core/tools/publisher/widgets/folders_dialog.py +++ b/client/ayon_core/tools/publisher/widgets/folders_dialog.py @@ -43,6 +43,7 @@ class FoldersDialog(QtWidgets.QDialog): folders_controller = FoldersDialogController(controller) folders_widget = FoldersWidget(folders_controller, self) + folders_widget.set_deselectable(True) ok_btn = QtWidgets.QPushButton("OK", self) cancel_btn = QtWidgets.QPushButton("Cancel", self) From ac33a8dd41c1bf636c31b39144ece52fe6c24c68 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Feb 2024 16:34:12 +0100 Subject: [PATCH 365/573] use hierarchy model to get task names --- client/ayon_core/tools/publisher/control.py | 24 ++++++++------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/client/ayon_core/tools/publisher/control.py b/client/ayon_core/tools/publisher/control.py index 6408056c04..6407a25c37 100644 --- a/client/ayon_core/tools/publisher/control.py +++ b/client/ayon_core/tools/publisher/control.py @@ -1738,30 +1738,24 @@ class PublisherController(BasePublisherController): return None def get_task_names_by_folder_paths(self, folder_paths): - # TODO implement model and cache values if not folder_paths: return {} folder_items = self._hierarchy_model.get_folder_items_by_paths( self.project_name, folder_paths ) - folder_paths_by_id = { - folder_item.entity_id: folder_item.path - for folder_item in folder_items.values() - if folder_item - } - tasks = ayon_api.get_tasks( - self.project_name, - folder_ids=set(folder_paths_by_id), - fields=["name", "folderId"] - ) output = { folder_path: set() for folder_path in folder_paths } - for task in tasks: - folder_path = folder_paths_by_id.get(task["folderId"]) - if folder_path: - output[folder_path].add(task["name"]) + project_name = self.project_name + for folder_item in folder_items.values(): + task_items = self._hierarchy_model.get_task_items( + project_name, folder_item.entity_id, None + ) + output[folder_item.path] = { + task_item.name + for task_item in task_items + } return output From 46846e055147948a6854606ac2cda0acaaf7dedd Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Feb 2024 16:37:03 +0100 Subject: [PATCH 366/573] reset asset docs cache too --- client/ayon_core/tools/traypublisher/window.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/tools/traypublisher/window.py b/client/ayon_core/tools/traypublisher/window.py index 07ec826db7..78517b4185 100644 --- a/client/ayon_core/tools/traypublisher/window.py +++ b/client/ayon_core/tools/traypublisher/window.py @@ -32,6 +32,7 @@ class TrayPublisherController(QtPublisherController): def reset_hierarchy_cache(self): self._hierarchy_model.reset() + self._asset_docs_cache.reset() class TrayPublisherRegistry(JSONSettingRegistry): From bcda4aa985fd92cea6235e700af95bdefa5934d4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Feb 2024 16:38:47 +0100 Subject: [PATCH 367/573] reset hierarchy model and asset docs cache --- client/ayon_core/tools/publisher/control.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ayon_core/tools/publisher/control.py b/client/ayon_core/tools/publisher/control.py index 6407a25c37..712142f662 100644 --- a/client/ayon_core/tools/publisher/control.py +++ b/client/ayon_core/tools/publisher/control.py @@ -1819,6 +1819,9 @@ class PublisherController(BasePublisherController): # Reset avalon context self._create_context.reset_current_context() + self._hierarchy_model.reset() + self._asset_docs_cache.reset() + self._reset_plugins() # Publish part must be reset after plugins self._reset_publish() From 9464b3739a3e2500b3489cf3a283a5b89ebb0acd Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Feb 2024 18:02:22 +0100 Subject: [PATCH 368/573] fix collect comment --- client/ayon_core/plugins/publish/collect_comment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/collect_comment.py b/client/ayon_core/plugins/publish/collect_comment.py index 458c0a5658..980097ac0d 100644 --- a/client/ayon_core/plugins/publish/collect_comment.py +++ b/client/ayon_core/plugins/publish/collect_comment.py @@ -42,7 +42,7 @@ class CollectInstanceCommentDef( pass @classmethod - def apply_settings(cls, project_setting, _): + def apply_settings(cls, project_setting): plugin_settings = project_setting["core"]["publish"].get( "collect_comment_per_instance" ) From ffc4bf19e77c21fa0371c1dfba48f0c0adc1a25e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Feb 2024 18:36:04 +0100 Subject: [PATCH 369/573] fix docstring of 'run_detached_process' --- client/ayon_core/lib/execute.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/lib/execute.py b/client/ayon_core/lib/execute.py index 4e3257c3a0..e89c8f22ee 100644 --- a/client/ayon_core/lib/execute.py +++ b/client/ayon_core/lib/execute.py @@ -240,7 +240,7 @@ def run_detached_process(args, **kwargs): Args: - *args (tuple): AYON cli arguments. + args (Iterable[str]): AYON cli arguments. **kwargs (dict): Keyword arguments for subprocess.Popen. Returns: From dbab27483382264ef106729cc098b17c106ef6a4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 28 Feb 2024 23:11:00 +0100 Subject: [PATCH 370/573] use 'GoToCurrentButton' --- .../tools/publisher/widgets/create_context_widgets.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/create_context_widgets.py b/client/ayon_core/tools/publisher/widgets/create_context_widgets.py index e17d3d1603..cd3d1103e3 100644 --- a/client/ayon_core/tools/publisher/widgets/create_context_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/create_context_widgets.py @@ -1,7 +1,7 @@ from qtpy import QtWidgets, QtCore, QtGui from ayon_core.lib.events import QueuedEventSystem -from ayon_core.tools.utils import PlaceholderLineEdit +from ayon_core.tools.utils import PlaceholderLineEdit, GoToCurrentButton from ayon_core.tools.ayon_utils.models import HierarchyExpectedSelection from ayon_core.tools.ayon_utils.widgets import FoldersWidget, TasksWidget @@ -160,9 +160,7 @@ class CreateContextWidget(QtWidgets.QWidget): folder_filter_input = PlaceholderLineEdit(headers_widget) folder_filter_input.setPlaceholderText("Filter folders..") - current_context_btn = QtWidgets.QPushButton( - "Go to current context", headers_widget - ) + current_context_btn = GoToCurrentButton(headers_widget) current_context_btn.setToolTip("Go to current context") current_context_btn.setVisible(False) From 9ad258997b38968eb126d752ec588f955f6446b1 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 29 Feb 2024 13:40:08 +0800 Subject: [PATCH 371/573] check instance data against context data rather than check user sertup properties against context data --- .../publish/validate_instance_in_context.py | 55 +++++++------------ 1 file changed, 21 insertions(+), 34 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py b/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py index 93f977c15c..dcd30da459 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py @@ -33,32 +33,18 @@ class ValidateInstanceInContext(pyblish.api.InstancePlugin, if not self.is_active(instance.data): return - instance_node = rt.getNodeByName(instance.data.get( - "instance_node", "")) - if not instance_node: - return - asset = rt.getUserProp(instance_node, "folderPath") - context_asset = instance.context.data["folderPath"] - task = rt.getUserProp(instance_node, "task") - context_task = instance.context.data["task"] - invalid = [] - if asset != context_asset: - invalid.append( - "Instance '{}' publishes to different asset than current " - "context: {}. Current context: {}".format( - instance.name, asset, context_asset + folderPath = instance.data.get("folderPath") + task = instance.data.get("task") + context = self.get_context(instance) + if (folderPath, task) != context: + context_label = "{} > {}".format(*context) + instance_label = "{} > {}".format(folderPath, task) + message = ( + "Instance '{}' publishes to different folderPath than current" + "context: {}. Current context:{}".format( + instance.name, instance_label, context_label ) ) - if task != context_task: - invalid.append( - "Instance '{}' publishes to different task than current " - "context: {}. Current context: {}".format( - instance.name, task, context_task - ) - ) - - if invalid: - message = "\n".join(invalid) raise PublishValidationError( message=message, description=( @@ -75,16 +61,11 @@ class ValidateInstanceInContext(pyblish.api.InstancePlugin, @classmethod def get_invalid(cls, instance): invalid = [] - node = rt.getNodeByName(instance.data["instance_node"]) - asset = rt.getUserProp(node, "folderPath") - context_asset = instance.context.data["folderPath"] - if asset != context_asset: - invalid.append(node) - task = rt.getUserProp(node, "task") - context_task = instance.context.data["task"] - if task != context_task: - invalid.append(node) - + folderPath = instance.data.get("folderPath") + task = instance.data.get("task") + context = cls.get_context(instance) + if (folderPath, task) != context: + invalid.append(rt.getNodeByName(instance.name)) return invalid @classmethod @@ -97,3 +78,9 @@ class ValidateInstanceInContext(pyblish.api.InstancePlugin, return rt.SetUserProp(instance_node, "folderPath", context_asset) rt.SetUserProp(instance_node, "task", context_task) + + @staticmethod + def get_context(instance): + """Return asset, task from publishing context data""" + context = instance.context + return context.data["folderPath"], context.data["task"] From 56f4f04d1c1b94b8afc41da66eaff99ba8c49bd6 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 29 Feb 2024 15:08:25 +0800 Subject: [PATCH 372/573] edit the code would pass only when the object is mesh type --- .../plugins/publish/validate_mesh_has_uv.py | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py b/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py index 5594a6a789..4d1490ac8f 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py @@ -30,20 +30,17 @@ class ValidateMeshHasUVs(pyblish.api.InstancePlugin, actions = [SelectInvalidAction] optional = True - @classmethod def get_invalid(cls, instance): invalid_mesh_type = [member for member in instance.data["members"] - if not rt.isProperty(member, "mesh")] + if rt.isProperty(member, "mesh")] if invalid_mesh_type: - cls.log.error("Non-mesh type objects detected") - return invalid_mesh_type + invalid_uv = [member for member in instance.data["members"] + if not member.mesh.numTVerts > 0] + if invalid_uv: + cls.log.error("Meshes detected with invalid UVs") - invalid_uv = [member for member in instance.data["members"] - if not member.mesh.numTVerts > 0] - if invalid_uv: - cls.log.error("Meshes detected with invalid UVs") - return invalid_uv + return invalid_uv def process(self, instance): invalid = self.get_invalid(instance) @@ -53,14 +50,13 @@ class ValidateMeshHasUVs(pyblish.api.InstancePlugin, in invalid ) report = ( - "Non-mesh objects found or mesh objects" - " do not have UVs.\n\n" + "Model meshes are required to have UVs.\n\n" "Meshes detected with invalid or missing UVs:\n" f"{bullet_point_invalid_statement}\n" ) raise PublishValidationError( report, description=( - "Non-mesh objects detected or the meshes do not have any UVs.\n\n" + "Model meshes are required to have UVs.\n\n" "Meshes detected with no texture vertice or missing UVs"), title="Non-mesh objects found or mesh has missing UVs") From 123d2b851c206db8fa8a18394bfd7a371fbf2864 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 29 Feb 2024 11:53:12 +0100 Subject: [PATCH 373/573] added some docstrings --- .../tools/ayon_utils/widgets/tasks_widget.py | 14 ++++++++++++++ .../publisher/widgets/create_context_widgets.py | 12 ++++++++++++ 2 files changed, 26 insertions(+) diff --git a/client/ayon_core/tools/ayon_utils/widgets/tasks_widget.py b/client/ayon_core/tools/ayon_utils/widgets/tasks_widget.py index 437eef2180..b273d83fa6 100644 --- a/client/ayon_core/tools/ayon_utils/widgets/tasks_widget.py +++ b/client/ayon_core/tools/ayon_utils/widgets/tasks_widget.py @@ -360,6 +360,20 @@ class TasksWidget(QtWidgets.QWidget): self._tasks_model.refresh() def get_selected_task_info(self): + """Get selected task info. + + Example output:: + + { + "task_id": "5e7e3e3e3e3e3e3e3e3e3e3e", + "task_name": "modeling", + "task_type": "Modeling" + } + + Returns: + dict[str, Union[str, None]]: Task info. + + """ _, task_id, task_name, task_type = self._get_selected_item_ids() return { "task_id": task_id, diff --git a/client/ayon_core/tools/publisher/widgets/create_context_widgets.py b/client/ayon_core/tools/publisher/widgets/create_context_widgets.py index cd3d1103e3..d65a2ace8d 100644 --- a/client/ayon_core/tools/publisher/widgets/create_context_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/create_context_widgets.py @@ -82,6 +82,18 @@ class CreateSelectionModel(object): class CreateHierarchyController: + """Controller for hierarchy widgets. + + Helper for handling hierarchy widgets in create tab. It handles selection + of folder and task to properly propagate it to other widgets. + + At the same time handles expected selection so can pre-select folder and + task based on current context. + + Args: + controller (PublisherController): Publisher controller. + + """ def __init__(self, controller): self._event_system = QueuedEventSystem() self._controller = controller From 8ca15394a340e6f7c6b8ca0b5b874bea4a840688 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 29 Feb 2024 11:59:10 +0100 Subject: [PATCH 374/573] implemented 'get_index_by_project_name' --- .../tools/ayon_utils/widgets/projects_widget.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/client/ayon_core/tools/ayon_utils/widgets/projects_widget.py b/client/ayon_core/tools/ayon_utils/widgets/projects_widget.py index d3bebecfd6..79ffc77640 100644 --- a/client/ayon_core/tools/ayon_utils/widgets/projects_widget.py +++ b/client/ayon_core/tools/ayon_utils/widgets/projects_widget.py @@ -47,6 +47,22 @@ class ProjectsQtModel(QtGui.QStandardItemModel): def has_content(self): return len(self._project_items) > 0 + def get_index_by_project_name(self, project_name): + """Get index of project by name. + + Args: + project_name (str): Project name. + + Returns: + QtCore.QModelIndex: Index of project item. Index is not valid + if project is not found. + + """ + item = self._project_items.get(project_name) + if item is None: + return QtCore.QModelIndex() + return self.indexFromItem(item) + def set_select_item_visible(self, visible): if self._select_item_visible is visible: return From 3b060d8abf6cb1a12e847e0562e488bedc5d5c20 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 29 Feb 2024 11:59:21 +0100 Subject: [PATCH 375/573] added project roles to init --- client/ayon_core/tools/ayon_utils/widgets/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/client/ayon_core/tools/ayon_utils/widgets/__init__.py b/client/ayon_core/tools/ayon_utils/widgets/__init__.py index b1b7dd7527..a62bab6751 100644 --- a/client/ayon_core/tools/ayon_utils/widgets/__init__.py +++ b/client/ayon_core/tools/ayon_utils/widgets/__init__.py @@ -3,6 +3,10 @@ from .projects_widget import ( ProjectsCombobox, ProjectsQtModel, ProjectSortFilterProxy, + PROJECT_NAME_ROLE, + PROJECT_IS_CURRENT_ROLE, + PROJECT_IS_ACTIVE_ROLE, + PROJECT_IS_LIBRARY_ROLE, ) from .folders_widget import ( @@ -28,6 +32,10 @@ __all__ = ( "ProjectsCombobox", "ProjectsQtModel", "ProjectSortFilterProxy", + "PROJECT_NAME_ROLE", + "PROJECT_IS_CURRENT_ROLE", + "PROJECT_IS_ACTIVE_ROLE", + "PROJECT_IS_LIBRARY_ROLE", "FoldersWidget", "FoldersQtModel", From 28f1ab0fb5b6d95200f3aaccc3000df39b0f2b17 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 29 Feb 2024 12:00:46 +0100 Subject: [PATCH 376/573] use ayon projects model --- .../ayon_core/tools/traypublisher/window.py | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/client/ayon_core/tools/traypublisher/window.py b/client/ayon_core/tools/traypublisher/window.py index 78517b4185..f993b15d64 100644 --- a/client/ayon_core/tools/traypublisher/window.py +++ b/client/ayon_core/tools/traypublisher/window.py @@ -18,14 +18,19 @@ from ayon_core.hosts.traypublisher.api import TrayPublisherHost from ayon_core.tools.publisher.control_qt import QtPublisherController from ayon_core.tools.publisher.window import PublisherWindow from ayon_core.tools.utils import PlaceholderLineEdit, get_ayon_qt_app -from ayon_core.tools.utils.constants import PROJECT_NAME_ROLE -from ayon_core.tools.utils.models import ( - ProjectModel, - ProjectSortFilterProxy +from ayon_core.tools.ayon_utils.models import ProjectsModel +from ayon_core.tools.ayon_utils.widgets import ( + ProjectsQtModel, + ProjectSortFilterProxy, + PROJECT_NAME_ROLE, ) class TrayPublisherController(QtPublisherController): + def __init__(self, *args, **kwargs): + super(TrayPublisherController, self).__init__(*args, **kwargs) + self._projects_model = ProjectsModel(self) + @property def host(self): return self._host @@ -34,6 +39,9 @@ class TrayPublisherController(QtPublisherController): self._hierarchy_model.reset() self._asset_docs_cache.reset() + def get_project_items(self, sender=None): + return self._projects_model.get_project_items(sender) + class TrayPublisherRegistry(JSONSettingRegistry): """Class handling AYON general settings registry. @@ -55,7 +63,7 @@ class TrayPublisherRegistry(JSONSettingRegistry): class StandaloneOverlayWidget(QtWidgets.QFrame): project_selected = QtCore.Signal(str) - def __init__(self, publisher_window): + def __init__(self, controller, publisher_window): super(StandaloneOverlayWidget, self).__init__(publisher_window) self.setObjectName("OverlayFrame") @@ -67,7 +75,7 @@ class StandaloneOverlayWidget(QtWidgets.QFrame): header_label = QtWidgets.QLabel("Choose project", content_widget) header_label.setObjectName("ChooseProjectLabel") # Create project models and view - projects_model = ProjectModel() + projects_model = ProjectsQtModel(controller) projects_proxy = ProjectSortFilterProxy() projects_proxy.setSourceModel(projects_model) projects_proxy.setFilterKeyColumn(0) @@ -138,12 +146,11 @@ class StandaloneOverlayWidget(QtWidgets.QFrame): project_name = None if project_name: - index = None - src_index = self._projects_model.find_project(project_name) - if src_index is not None: - index = self._projects_proxy.mapFromSource(src_index) - - if index is not None: + src_index = self._projects_model.get_index_by_project_name( + project_name + ) + index = self._projects_proxy.mapFromSource(src_index) + if index.isValid(): selection_model = self._projects_view.selectionModel() selection_model.select( index, @@ -202,7 +209,7 @@ class TrayPublishWindow(PublisherWindow): self.setWindowFlags(flags) - overlay_widget = StandaloneOverlayWidget(self) + overlay_widget = StandaloneOverlayWidget(controller, self) btns_widget = self._header_extra_widget From 5e8119190492f9f7c058ef3d7c255bbf2a0af912 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 29 Feb 2024 12:01:13 +0100 Subject: [PATCH 377/573] use 'AYONSettingsRegistry' for traypublisher registry --- .../ayon_core/tools/traypublisher/window.py | 25 +++++-------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/client/ayon_core/tools/traypublisher/window.py b/client/ayon_core/tools/traypublisher/window.py index f993b15d64..210e77f0fa 100644 --- a/client/ayon_core/tools/traypublisher/window.py +++ b/client/ayon_core/tools/traypublisher/window.py @@ -10,9 +10,8 @@ import platform from qtpy import QtWidgets, QtCore import qtawesome -import appdirs -from ayon_core.lib import JSONSettingRegistry, is_running_from_build +from ayon_core.lib import AYONSettingsRegistry, is_running_from_build from ayon_core.pipeline import install_host from ayon_core.hosts.traypublisher.api import TrayPublisherHost from ayon_core.tools.publisher.control_qt import QtPublisherController @@ -26,6 +25,11 @@ from ayon_core.tools.ayon_utils.widgets import ( ) +class TrayPublisherRegistry(AYONSettingsRegistry): + def __init__(self): + super(TrayPublisherRegistry, self).__init__("traypublisher") + + class TrayPublisherController(QtPublisherController): def __init__(self, *args, **kwargs): super(TrayPublisherController, self).__init__(*args, **kwargs) @@ -43,23 +47,6 @@ class TrayPublisherController(QtPublisherController): return self._projects_model.get_project_items(sender) -class TrayPublisherRegistry(JSONSettingRegistry): - """Class handling AYON general settings registry. - - Attributes: - vendor (str): Name used for path construction. - product (str): Additional name used for path construction. - - """ - - def __init__(self): - self.vendor = "pypeclub" - self.product = "openpype" - name = "tray_publisher" - path = appdirs.user_data_dir(self.product, self.vendor) - super(TrayPublisherRegistry, self).__init__(name, path) - - class StandaloneOverlayWidget(QtWidgets.QFrame): project_selected = QtCore.Signal(str) From dff383bcedfa3793c4a5ce82ad33567165e26fd0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 29 Feb 2024 12:01:41 +0100 Subject: [PATCH 378/573] removed 'ProjectModel' and 'ProjectSortFilterProxy' from utils --- client/ayon_core/tools/utils/models.py | 157 ------------------------- 1 file changed, 157 deletions(-) diff --git a/client/ayon_core/tools/utils/models.py b/client/ayon_core/tools/utils/models.py index e60d85b4e4..a4b6ad7885 100644 --- a/client/ayon_core/tools/utils/models.py +++ b/client/ayon_core/tools/utils/models.py @@ -243,160 +243,3 @@ class RecursiveSortFilterProxyModel(QtCore.QSortFilterProxyModel): return super(RecursiveSortFilterProxyModel, self).filterAcceptsRow( row, parent_index ) - - -# TODO remove 'ProjectModel' and 'ProjectSortFilterProxy' classes -# - replace their usage with current 'ayon_utils' models -class ProjectModel(QtGui.QStandardItemModel): - def __init__( - self, only_active=True, add_default_project=False, *args, **kwargs - ): - super(ProjectModel, self).__init__(*args, **kwargs) - - self._only_active = only_active - self._add_default_project = add_default_project - - self._default_item = None - self._items_by_name = {} - self._refreshed = False - - def set_default_project_available(self, available=True): - if available is None: - available = not self._add_default_project - - if self._add_default_project == available: - return - - self._add_default_project = available - if not available and self._default_item is not None: - root_item = self.invisibleRootItem() - root_item.removeRow(self._default_item.row()) - self._default_item = None - - def set_only_active(self, only_active=True): - if only_active is None: - only_active = not self._only_active - - if self._only_active == only_active: - return - - self._only_active = only_active - - if self._refreshed: - self.refresh() - - def project_name_is_available(self, project_name): - """Check availability of project name in current items.""" - return project_name in self._items_by_name - - def refresh(self): - # Change '_refreshed' state - self._refreshed = True - new_items = [] - # Add default item to model if should - if self._add_default_project and self._default_item is None: - item = QtGui.QStandardItem(DEFAULT_PROJECT_LABEL) - item.setData(None, PROJECT_NAME_ROLE) - item.setData(True, PROJECT_IS_ACTIVE_ROLE) - new_items.append(item) - self._default_item = item - - project_names = set() - project_docs = get_projects( - inactive=not self._only_active, - fields=["name", "data.active"] - ) - for project_doc in project_docs: - project_name = project_doc["name"] - project_names.add(project_name) - if project_name in self._items_by_name: - item = self._items_by_name[project_name] - else: - item = QtGui.QStandardItem(project_name) - - self._items_by_name[project_name] = item - new_items.append(item) - - is_active = project_doc.get("data", {}).get("active", True) - item.setData(project_name, PROJECT_NAME_ROLE) - item.setData(is_active, PROJECT_IS_ACTIVE_ROLE) - - if not is_active: - font = item.font() - font.setItalic(True) - item.setFont(font) - - root_item = self.invisibleRootItem() - for project_name in tuple(self._items_by_name.keys()): - if project_name not in project_names: - item = self._items_by_name.pop(project_name) - root_item.removeRow(item.row()) - - if new_items: - root_item.appendRows(new_items) - - def find_project(self, project_name): - """ - Get index of 'project_name' value. - - Args: - project_name (str): - Returns: - (QModelIndex) - """ - val = self._items_by_name.get(project_name) - if val: - return self.indexFromItem(val) - - -class ProjectSortFilterProxy(QtCore.QSortFilterProxyModel): - def __init__(self, *args, **kwargs): - super(ProjectSortFilterProxy, self).__init__(*args, **kwargs) - self._filter_enabled = True - # Disable case sensitivity - self.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive) - - def lessThan(self, left_index, right_index): - if left_index.data(PROJECT_NAME_ROLE) is None: - return True - - if right_index.data(PROJECT_NAME_ROLE) is None: - return False - - left_is_active = left_index.data(PROJECT_IS_ACTIVE_ROLE) - right_is_active = right_index.data(PROJECT_IS_ACTIVE_ROLE) - if right_is_active == left_is_active: - return super(ProjectSortFilterProxy, self).lessThan( - left_index, right_index - ) - - if left_is_active: - return True - return False - - def filterAcceptsRow(self, source_row, source_parent): - index = self.sourceModel().index(source_row, 0, source_parent) - string_pattern = self.filterRegularExpression().pattern() - if self._filter_enabled: - result = self._custom_index_filter(index) - if result is not None: - project_name = index.data(PROJECT_NAME_ROLE) - if project_name is None: - return result - return string_pattern.lower() in project_name.lower() - - return super(ProjectSortFilterProxy, self).filterAcceptsRow( - source_row, source_parent - ) - - def _custom_index_filter(self, index): - is_active = bool(index.data(PROJECT_IS_ACTIVE_ROLE)) - - return is_active - - def is_filter_enabled(self): - return self._filter_enabled - - def set_filter_enabled(self, value): - self._filter_enabled = value - self.invalidateFilter() From c7ba4be69c9e46f02b05976ab8aec4514de5306c Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 29 Feb 2024 20:37:23 +0800 Subject: [PATCH 379/573] code clean up with bigroy's comment --- .../max/plugins/publish/validate_mesh_has_uv.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py b/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py index 4d1490ac8f..cecc16c3c3 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py @@ -32,15 +32,11 @@ class ValidateMeshHasUVs(pyblish.api.InstancePlugin, @classmethod def get_invalid(cls, instance): - invalid_mesh_type = [member for member in instance.data["members"] - if rt.isProperty(member, "mesh")] - if invalid_mesh_type: - invalid_uv = [member for member in instance.data["members"] - if not member.mesh.numTVerts > 0] - if invalid_uv: - cls.log.error("Meshes detected with invalid UVs") - - return invalid_uv + meshes = [member for member in instance.data["members"] + if rt.isProperty(member, "mesh")] + invalid = [member for member in meshes + if not member.mesh.numTVerts > 0] + return invalid def process(self, instance): invalid = self.get_invalid(instance) From 34cca056153b0ae6d6805c90d14822851a0f21e5 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 29 Feb 2024 20:42:32 +0800 Subject: [PATCH 380/573] code clean up --- .../hosts/max/plugins/publish/validate_instance_in_context.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py b/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py index dcd30da459..cf285bfab6 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- """Validate if instance asset is the same as context asset.""" -from __future__ import absolute_import - import pyblish.api from ayon_core.pipeline.publish import ( RepairAction, @@ -41,7 +39,7 @@ class ValidateInstanceInContext(pyblish.api.InstancePlugin, instance_label = "{} > {}".format(folderPath, task) message = ( "Instance '{}' publishes to different folderPath than current" - "context: {}. Current context:{}".format( + "context: {}. Current context: {}".format( instance.name, instance_label, context_label ) ) From 1bdac65841bc92d77c7df5353d7d96b5ab9f8b2d Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 29 Feb 2024 20:43:13 +0800 Subject: [PATCH 381/573] code clean up --- .../ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py b/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py index cecc16c3c3..109b7fe0b5 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py @@ -35,7 +35,7 @@ class ValidateMeshHasUVs(pyblish.api.InstancePlugin, meshes = [member for member in instance.data["members"] if rt.isProperty(member, "mesh")] invalid = [member for member in meshes - if not member.mesh.numTVerts > 0] + if member.mesh.numTVerts == 0] return invalid def process(self, instance): From b23500187d5d70f92843fd659df90fdaebc331f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Thu, 29 Feb 2024 14:07:00 +0100 Subject: [PATCH 382/573] :recycle: remove context asserts and support for multiple paths --- client/ayon_core/cli.py | 13 +++--- client/ayon_core/cli_commands.py | 29 +++++++------ .../plugins/publish/collect_rendered_files.py | 43 ++++++++----------- 3 files changed, 40 insertions(+), 45 deletions(-) diff --git a/client/ayon_core/cli.py b/client/ayon_core/cli.py index 88b574da76..0b59589ebc 100644 --- a/client/ayon_core/cli.py +++ b/client/ayon_core/cli.py @@ -102,19 +102,18 @@ def extractenvironments(output_json_path, project, asset, task, app, envgroup): @main_cli.command() -@click.argument("paths", nargs=-1) -@click.option("-t", "--targets", help="Targets module", default=None, +@click.argument("path", nargs=1) +@click.option("-t", "--targets", help="Targets", default=None, multiple=True) @click.option("-g", "--gui", is_flag=True, help="Show Publish UI", default=False) -def publish(paths, targets, gui): +def publish(path, targets, gui): """Start CLI publishing. - Publish collects json from paths provided as an argument. - More than one path is allowed. + Publish collects json from path provided as an argument. +S """ - - Commands.publish(list(paths), targets, gui) + Commands.publish(path, targets, gui) @main_cli.command(context_settings={"ignore_unknown_options": True}) diff --git a/client/ayon_core/cli_commands.py b/client/ayon_core/cli_commands.py index a24710aef2..dc11187990 100644 --- a/client/ayon_core/cli_commands.py +++ b/client/ayon_core/cli_commands.py @@ -3,6 +3,7 @@ import os import sys import json +import warnings class Commands: @@ -41,21 +42,21 @@ class Commands: return click_func @staticmethod - def publish(paths, targets=None, gui=False): + def publish(path: str, targets: list=None, gui:bool=False) -> None: """Start headless publishing. - Publish use json from passed paths argument. + Publish use json from passed path argument. Args: - paths (list): Paths to jsons. - targets (string): What module should be targeted - (to choose validator for example) + path (str): Path to JSON. + targets (list of str): List of pyblish targets. gui (bool): Show publish UI. Raises: RuntimeError: When there is no path to process. - """ + RuntimeError: When executed with list of JSON paths. + """ from ayon_core.lib import Logger from ayon_core.lib.applications import ( get_app_environments_for_context, @@ -73,6 +74,11 @@ class Commands: import pyblish.api import pyblish.util + if not isinstance(path, str): + warnings.warn( + "Passing list of paths is deprecated.", + DeprecationWarning) + # Fix older jobs for src_key, dst_key in ( ("AVALON_PROJECT", "AYON_PROJECT_NAME"), @@ -95,11 +101,8 @@ class Commands: publish_paths = manager.collect_plugin_paths()["publish"] - for path in publish_paths: - pyblish.api.register_plugin_path(path) - - if not any(paths): - raise RuntimeError("No publish paths specified") + for plugin_path in publish_paths: + pyblish.api.register_plugin_path(plugin_path) app_full_name = os.getenv("AYON_APP_NAME") if app_full_name: @@ -111,7 +114,7 @@ class Commands: app_full_name, launch_type=LaunchTypes.farm_publish, ) - os.environ.update(env) + os.environ |= env pyblish.api.register_host("shell") @@ -122,7 +125,7 @@ class Commands: else: pyblish.api.register_target("farm") - os.environ["AYON_PUBLISH_DATA"] = os.pathsep.join(paths) + os.environ["AYON_PUBLISH_DATA"] = os.pathsep.join(path) os.environ["HEADLESS_PUBLISH"] = 'true' # to use in app lib log.info("Running publish ...") diff --git a/client/ayon_core/plugins/publish/collect_rendered_files.py b/client/ayon_core/plugins/publish/collect_rendered_files.py index ca88a7aa82..152771da6f 100644 --- a/client/ayon_core/plugins/publish/collect_rendered_files.py +++ b/client/ayon_core/plugins/publish/collect_rendered_files.py @@ -36,18 +36,18 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin): def _load_json(self, path): path = path.strip('\"') - assert os.path.isfile(path), ( - "Path to json file doesn't exist. \"{}\"".format(path) - ) + + if not os.path.isfile(path): + raise FileNotFoundError( + f"Path to json file doesn't exist. \"{path}\"") + data = None with open(path, "r") as json_file: try: data = json.load(json_file) except Exception as exc: self.log.error( - "Error loading json: " - "{} - Exception: {}".format(path, exc) - ) + "Error loading json: %s - Exception: %s", path, exc) return data def _fill_staging_dir(self, data_object, anatomy): @@ -73,30 +73,23 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin): data_err = "invalid json file - missing data" required = ["user", "comment", "job", "instances", "version"] - assert all(elem in data.keys() for elem in required), data_err + + if any(elem not in data.keys() for elem in required): + raise ValueError(data_err) + if "folderPath" not in data and "asset" not in data: - raise AssertionError(data_err) + raise ValueError(data_err) if "folderPath" not in data: data["folderPath"] = data.pop("asset") - # set context by first json file - ctx = self._context.data - - ctx["folderPath"] = ctx.get("folderPath") or data.get("folderPath") - ctx["intent"] = ctx.get("intent") or data.get("intent") - ctx["comment"] = ctx.get("comment") or data.get("comment") - ctx["user"] = ctx.get("user") or data.get("user") - ctx["version"] = ctx.get("version") or data.get("version") - - # basic sanity check to see if we are working in same context - # if some other json file has different context, bail out. - ctx_err = "inconsistent contexts in json files - %s" - assert ctx.get("folderPath") == data.get("folderPath"), ctx_err % "folderPath" - assert ctx.get("intent") == data.get("intent"), ctx_err % "intent" - assert ctx.get("comment") == data.get("comment"), ctx_err % "comment" - assert ctx.get("user") == data.get("user"), ctx_err % "user" - assert ctx.get("version") == data.get("version"), ctx_err % "version" + # ftrack credentials are passed as environment variables by Deadline + # to publish job, but Muster doesn't pass them. + if data.get("ftrack") and not os.environ.get("FTRACK_API_USER"): + ftrack = data.get("ftrack") + os.environ["FTRACK_API_USER"] = ftrack["FTRACK_API_USER"] + os.environ["FTRACK_API_KEY"] = ftrack["FTRACK_API_KEY"] + os.environ["FTRACK_SERVER"] = ftrack["FTRACK_SERVER"] # now we can just add instances from json file and we are done any_staging_dir_persistent = False From 2450f179395bad2816f077fe8f923b5bdeae997c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 29 Feb 2024 15:49:28 +0100 Subject: [PATCH 383/573] removed project functions from 'ayon_core.client' --- client/ayon_core/client/__init__.py | 8 - client/ayon_core/client/conversion_utils.py | 225 -------------------- client/ayon_core/client/entities.py | 33 --- 3 files changed, 266 deletions(-) diff --git a/client/ayon_core/client/__init__.py b/client/ayon_core/client/__init__.py index 00f4d9863f..c2ed774392 100644 --- a/client/ayon_core/client/__init__.py +++ b/client/ayon_core/client/__init__.py @@ -1,10 +1,6 @@ from .utils import get_ayon_server_api_connection from .entities import ( - get_projects, - get_project, - get_whole_project, - get_asset_by_id, get_asset_by_name, get_assets, @@ -59,10 +55,6 @@ from .operations import ( __all__ = ( "get_ayon_server_api_connection", - "get_projects", - "get_project", - "get_whole_project", - "get_asset_by_id", "get_asset_by_name", "get_assets", diff --git a/client/ayon_core/client/conversion_utils.py b/client/ayon_core/client/conversion_utils.py index 192eb194db..f9c50b32be 100644 --- a/client/ayon_core/client/conversion_utils.py +++ b/client/ayon_core/client/conversion_utils.py @@ -18,16 +18,6 @@ from .constants import ( ) from .utils import create_entity_id, prepare_entity_changes -# --- Project entity --- -PROJECT_FIELDS_MAPPING_V3_V4 = { - "_id": {"name"}, - "name": {"name"}, - "data": {"data", "code"}, - "data.library_project": {"library"}, - "data.code": {"code"}, - "data.active": {"active"}, -} - # TODO this should not be hardcoded but received from server!!! # --- Folder entity --- FOLDER_FIELDS_MAPPING_V3_V4 = { @@ -69,221 +59,6 @@ REPRESENTATION_FIELDS_MAPPING_V3_V4 = { } -def project_fields_v3_to_v4(fields, con): - """Convert project fields from v3 to v4 structure. - - Args: - fields (Union[Iterable(str), None]): fields to be converted. - - Returns: - Union[Set(str), None]: Converted fields to v4 fields. - """ - - # TODO config fields - # - config.apps - # - config.groups - if not fields: - return None - - project_attribs = con.get_attributes_for_type("project") - output = set() - for field in fields: - # If config is needed the rest api call must be used - if field.startswith("config"): - return None - - if field in PROJECT_FIELDS_MAPPING_V3_V4: - output |= PROJECT_FIELDS_MAPPING_V3_V4[field] - if field == "data": - output |= { - "attrib.{}".format(attr) - for attr in project_attribs - } - - elif field.startswith("data"): - field_parts = field.split(".") - field_parts.pop(0) - data_key = ".".join(field_parts) - if data_key in project_attribs: - output.add("attrib.{}".format(data_key)) - else: - output.add("data") - print("Requested specific key from data {}".format(data_key)) - - else: - raise ValueError("Unknown field mapping for {}".format(field)) - - if "name" not in output: - output.add("name") - return output - - -def _get_default_template_name(templates): - default_template = None - for name, template in templates.items(): - if name == "default": - return "default" - - if default_template is None: - default_template = name - - return default_template - - -def _template_replacements_to_v3(template): - return ( - template - .replace("{product[name]}", "{subset}") - .replace("{product[type]}", "{family}") - ) - - -def _convert_template_item(template_item): - for key, value in tuple(template_item.items()): - template_item[key] = _template_replacements_to_v3(value) - - # Change 'directory' to 'folder' - if "directory" in template_item: - template_item["folder"] = template_item.pop("directory") - - if ( - "path" not in template_item - and "file" in template_item - and "folder" in template_item - ): - template_item["path"] = "/".join( - (template_item["folder"], template_item["file"]) - ) - - -def _fill_template_category(templates, cat_templates, cat_key): - default_template_name = _get_default_template_name(cat_templates) - for template_name, cat_template in cat_templates.items(): - _convert_template_item(cat_template) - if template_name == default_template_name: - templates[cat_key] = cat_template - else: - new_name = "{}_{}".format(cat_key, template_name) - templates["others"][new_name] = cat_template - - -def convert_v4_project_to_v3(project): - """Convert Project entity data from v4 structure to v3 structure. - - Args: - project (Dict[str, Any]): Project entity queried from v4 server. - - Returns: - Dict[str, Any]: Project converted to v3 structure. - """ - - if not project: - return project - - project_name = project["name"] - output = { - "_id": project_name, - "name": project_name, - "schema": CURRENT_PROJECT_SCHEMA, - "type": "project" - } - - data = project.get("data") or {} - attribs = project.get("attrib") or {} - apps_attr = attribs.pop("applications", None) or [] - applications = [ - {"name": app_name} - for app_name in apps_attr - ] - data.update(attribs) - if "tools" in data: - data["tools_env"] = data.pop("tools") - - data["entityType"] = "Project" - - config = {} - project_config = project.get("config") - - if project_config: - config["apps"] = applications - config["roots"] = project_config["roots"] - - templates = project_config["templates"] - templates["defaults"] = templates.pop("common", None) or {} - - others_templates = templates.pop("others", None) or {} - new_others_templates = {} - templates["others"] = new_others_templates - for name, template in others_templates.items(): - _convert_template_item(template) - new_others_templates[name] = template - - staging_templates = templates.pop("staging", None) - # Key 'staging_directories' is legacy key that changed - # to 'staging_dir' - _legacy_staging_templates = templates.pop("staging_directories", None) - if staging_templates is None: - staging_templates = _legacy_staging_templates - - if staging_templates is None: - staging_templates = {} - - # Prefix all staging template names with 'staging_' prefix - # and add them to 'others' - for name, template in staging_templates.items(): - _convert_template_item(template) - new_name = "staging_{}".format(name) - new_others_templates[new_name] = template - - for key in ( - "work", - "publish", - "hero", - ): - cat_templates = templates.pop(key) - _fill_template_category(templates, cat_templates, key) - - delivery_templates = templates.pop("delivery", None) or {} - new_delivery_templates = {} - for name, delivery_template in delivery_templates.items(): - new_delivery_templates[name] = "/".join( - (delivery_template["directory"], delivery_template["file"]) - ) - templates["delivery"] = new_delivery_templates - - config["templates"] = templates - - if "taskTypes" in project: - task_types = project["taskTypes"] - new_task_types = {} - for task_type in task_types: - name = task_type.pop("name") - # Change 'shortName' to 'short_name' - task_type["short_name"] = task_type.pop("shortName", None) - new_task_types[name] = task_type - - config["tasks"] = new_task_types - - if config: - output["config"] = config - - for data_key, key in ( - ("library_project", "library"), - ("code", "code"), - ("active", "active") - ): - if key in project: - data[data_key] = project[key] - - if "attrib" in project: - for key, value in project["attrib"].items(): - data[key] = value - - if data: - output["data"] = data - return output - - def folder_fields_v3_to_v4(fields, con): """Convert folder fields from v3 to v4 structure. diff --git a/client/ayon_core/client/entities.py b/client/ayon_core/client/entities.py index 5ef2571421..f9a32e15cd 100644 --- a/client/ayon_core/client/entities.py +++ b/client/ayon_core/client/entities.py @@ -4,9 +4,6 @@ from .constants import CURRENT_THUMBNAIL_SCHEMA from .utils import get_ayon_server_api_connection from .openpype_comp import get_folders_with_tasks from .conversion_utils import ( - project_fields_v3_to_v4, - convert_v4_project_to_v3, - folder_fields_v3_to_v4, convert_v4_folder_to_v3, @@ -41,36 +38,6 @@ def get_asset_name_identifier(asset_doc): return "/" + "/".join(parents) -def get_projects(active=True, inactive=False, library=None, fields=None): - if not active and not inactive: - return - - if active and inactive: - active = None - elif active: - active = True - elif inactive: - active = False - - con = get_ayon_server_api_connection() - fields = project_fields_v3_to_v4(fields, con) - for project in con.get_projects(active, library, fields=fields): - yield convert_v4_project_to_v3(project) - - -def get_project(project_name, active=True, inactive=False, fields=None): - # Skip if both are disabled - con = get_ayon_server_api_connection() - fields = project_fields_v3_to_v4(fields, con) - return convert_v4_project_to_v3( - con.get_project(project_name, fields=fields) - ) - - -def get_whole_project(*args, **kwargs): - raise NotImplementedError("'get_whole_project' not implemented") - - def _get_subsets( project_name, subset_ids=None, From b227c6936cd505a41a02b32409ab1a05c6d5a097 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 29 Feb 2024 16:00:28 +0100 Subject: [PATCH 384/573] use unchanged ayon project entity in codebase --- .../hooks/pre_copy_template_workfile.py | 6 +- .../ayon_core/hooks/pre_global_host_data.py | 22 ++-- .../hosts/flame/hooks/pre_flame_setup.py | 24 ++-- .../plugins/publish/integrate_batch_group.py | 6 +- client/ayon_core/hosts/fusion/api/lib.py | 13 +- client/ayon_core/hosts/hiero/api/lib.py | 18 +-- client/ayon_core/hosts/hiero/api/tags.py | 18 +-- .../hosts/hiero/plugins/load/load_effects.py | 4 +- client/ayon_core/hosts/houdini/api/lib.py | 9 +- client/ayon_core/hosts/max/api/lib.py | 22 ++-- .../publish/validate_resolution_setting.py | 4 +- client/ayon_core/hosts/maya/api/commands.py | 26 ++-- client/ayon_core/hosts/maya/api/lib.py | 19 +-- client/ayon_core/hosts/maya/api/setdress.py | 2 +- .../hosts/maya/hooks/pre_copy_mel.py | 6 +- .../plugins/publish/validate_resolution.py | 4 +- client/ayon_core/hosts/nuke/api/lib.py | 14 +-- .../hosts/traypublisher/api/editorial.py | 47 ++++--- .../plugins/create/create_editorial.py | 15 ++- .../unreal/hooks/pre_workfile_preparation.py | 4 +- client/ayon_core/lib/applications.py | 14 +-- .../clockify/launcher_actions/ClockifySync.py | 21 ++-- .../publish/submit_publish_cache_job.py | 4 +- .../plugins/publish/submit_publish_job.py | 4 +- client/ayon_core/pipeline/anatomy.py | 118 ++++++++++++++---- client/ayon_core/pipeline/context_tools.py | 10 +- client/ayon_core/pipeline/load/utils.py | 27 ++-- client/ayon_core/pipeline/template_data.py | 46 +++---- client/ayon_core/pipeline/usdlib.py | 51 ++++---- .../pipeline/workfile/path_resolving.py | 49 +++++--- .../plugins/actions/open_file_explorer.py | 15 ++- .../ayon_core/plugins/load/push_to_library.py | 4 +- .../publish/collect_anatomy_instance_data.py | 35 +++--- .../publish/collect_context_entities.py | 5 +- .../plugins/publish/collect_hierarchy.py | 2 +- .../publish/extract_hierarchy_to_ayon.py | 6 +- .../ayon_core/tools/loader/models/actions.py | 27 ++-- .../tools/push_to_project/models/integrate.py | 46 ++++--- client/ayon_core/tools/texture_copy/app.py | 48 +++---- client/ayon_core/tools/utils/assets_widget.py | 10 +- client/ayon_core/tools/utils/lib.py | 9 +- client/ayon_core/tools/utils/models.py | 6 - .../tools/workfiles/models/workfiles.py | 5 +- 43 files changed, 470 insertions(+), 375 deletions(-) diff --git a/client/ayon_core/hooks/pre_copy_template_workfile.py b/client/ayon_core/hooks/pre_copy_template_workfile.py index df2a0386b2..13f2f64565 100644 --- a/client/ayon_core/hooks/pre_copy_template_workfile.py +++ b/client/ayon_core/hooks/pre_copy_template_workfile.py @@ -60,13 +60,13 @@ class CopyTemplateWorkfile(PreLaunchHook): project_settings = get_project_settings(project_name) - project_doc = self.data.get("project_doc") + project_entity = self.data.get("project_entity") asset_doc = self.data.get("asset_doc") anatomy = self.data.get("anatomy") - if project_doc and asset_doc: + if project_entity and asset_doc: self.log.debug("Started filtering of custom template paths.") template_path = get_custom_workfile_template( - project_doc, + project_entity, asset_doc, task_name, host_name, diff --git a/client/ayon_core/hooks/pre_global_host_data.py b/client/ayon_core/hooks/pre_global_host_data.py index de6d4acc8b..b6cfe2d5d4 100644 --- a/client/ayon_core/hooks/pre_global_host_data.py +++ b/client/ayon_core/hooks/pre_global_host_data.py @@ -1,4 +1,5 @@ -from ayon_core.client import get_project, get_asset_by_name +from ayon_api import get_project, get_folder_by_path + from ayon_core.lib.applications import ( PreLaunchHook, EnvironmentPrepData, @@ -27,7 +28,7 @@ class GlobalHostDataHook(PreLaunchHook): "app": app, - "project_doc": self.data["project_doc"], + "project_entity": self.data["project_entity"], "asset_doc": self.data["asset_doc"], "anatomy": self.data["anatomy"], @@ -59,12 +60,15 @@ class GlobalHostDataHook(PreLaunchHook): return self.log.debug("Project name is set to \"{}\"".format(project_name)) - # Anatomy - self.data["anatomy"] = Anatomy(project_name) - # Project document - project_doc = get_project(project_name) - self.data["project_doc"] = project_doc + # Project Entity + project_entity = get_project(project_name) + self.data["project_entity"] = project_entity + + # Anatomy + self.data["anatomy"] = Anatomy( + project_name, project_entity=project_entity + ) asset_name = self.data.get("folder_path") if not asset_name: @@ -73,5 +77,5 @@ class GlobalHostDataHook(PreLaunchHook): ) return - asset_doc = get_asset_by_name(project_name, asset_name) - self.data["asset_doc"] = asset_doc + folder_entity = get_folder_by_path(project_name, asset_name) + self.data["folder_entity"] = folder_entity diff --git a/client/ayon_core/hosts/flame/hooks/pre_flame_setup.py b/client/ayon_core/hosts/flame/hooks/pre_flame_setup.py index 391332d368..b7fc431352 100644 --- a/client/ayon_core/hosts/flame/hooks/pre_flame_setup.py +++ b/client/ayon_core/hosts/flame/hooks/pre_flame_setup.py @@ -36,8 +36,8 @@ class FlamePrelaunch(PreLaunchHook): self.flame_pythonpath = _env["AYON_FLAME_PYTHONPATH"] """Hook entry method.""" - project_doc = self.data["project_doc"] - project_name = project_doc["name"] + project_entity = self.data["project_entity"] + project_name = project_entity["name"] volume_name = _env.get("FLAME_WIRETAP_VOLUME") # get image io @@ -63,20 +63,22 @@ class FlamePrelaunch(PreLaunchHook): hostname = socket.gethostname() # not returning wiretap host name self.log.debug("Collected user \"{}\"".format(user_name)) - self.log.info(pformat(project_doc)) - _db_p_data = project_doc["data"] - width = _db_p_data["resolutionWidth"] - height = _db_p_data["resolutionHeight"] - fps = float(_db_p_data["fps"]) + self.log.info(pformat(project_entity)) + project_attribs = project_entity["attrib"] + width = project_attribs["resolutionWidth"] + height = project_attribs["resolutionHeight"] + fps = float(project_attribs["fps"]) project_data = { - "Name": project_doc["name"], - "Nickname": _db_p_data["code"], + "Name": project_entity["name"], + "Nickname": project_entity["code"], "Description": "Created by OpenPype", - "SetupDir": project_doc["name"], + "SetupDir": project_entity["name"], "FrameWidth": int(width), "FrameHeight": int(height), - "AspectRatio": float((width / height) * _db_p_data["pixelAspect"]), + "AspectRatio": float( + (width / height) * project_attribs["pixelAspect"] + ), "FrameRate": self._get_flame_fps(fps) } diff --git a/client/ayon_core/hosts/flame/plugins/publish/integrate_batch_group.py b/client/ayon_core/hosts/flame/plugins/publish/integrate_batch_group.py index a66ee9f2c0..66b2af7d2b 100644 --- a/client/ayon_core/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/client/ayon_core/hosts/flame/plugins/publish/integrate_batch_group.py @@ -219,7 +219,7 @@ class IntegrateBatchGroup(pyblish.api.InstancePlugin): # update task data in anatomy data project_task_types = anatomy_obj["tasks"] - task_code = project_task_types.get(task_type, {}).get("short_name") + task_code = project_task_types.get(task_type, {}).get("shortName") anatomy_data.update({ "task": { "name": task_name, @@ -321,13 +321,13 @@ class IntegrateBatchGroup(pyblish.api.InstancePlugin): )) def _get_shot_task_dir_path(self, instance, task_data): - project_doc = instance.data["projectEntity"] + project_entity = instance.data["projectEntity"] asset_entity = instance.data["assetEntity"] anatomy = instance.context.data["anatomy"] project_settings = instance.context.data["project_settings"] return get_workdir( - project_doc, + project_entity, asset_entity, task_data["name"], "flame", diff --git a/client/ayon_core/hosts/fusion/api/lib.py b/client/ayon_core/hosts/fusion/api/lib.py index b31f812c1b..47cfb284f7 100644 --- a/client/ayon_core/hosts/fusion/api/lib.py +++ b/client/ayon_core/hosts/fusion/api/lib.py @@ -4,18 +4,7 @@ import re import contextlib from ayon_core.lib import Logger -from ayon_core.client import ( - get_asset_by_name, - get_subset_by_name, - get_last_version_by_subset_id, - get_representation_by_id, - get_representation_by_name, - get_representation_parents, -) -from ayon_core.pipeline import ( - switch_container, - get_current_project_name, -) + from ayon_core.pipeline.context_tools import get_current_project_asset self = sys.modules[__name__] diff --git a/client/ayon_core/hosts/hiero/api/lib.py b/client/ayon_core/hosts/hiero/api/lib.py index ef0f9edca9..abf3690f9e 100644 --- a/client/ayon_core/hosts/hiero/api/lib.py +++ b/client/ayon_core/hosts/hiero/api/lib.py @@ -15,12 +15,12 @@ import shutil import hiero from qtpy import QtWidgets, QtCore +import ayon_api try: from PySide import QtXml except ImportError: from PySide2 import QtXml -from ayon_core.client import get_project from ayon_core.settings import get_project_settings from ayon_core.pipeline import ( Anatomy, @@ -654,17 +654,17 @@ def sync_avalon_data_to_workfile(): project.setProjectRoot(active_project_root) # get project data from avalon db - project_doc = get_project(project_name) - project_data = project_doc["data"] + project_entity = ayon_api.get_project(project_name) + project_attribs = project_entity["attrib"] - log.debug("project_data: {}".format(project_data)) + log.debug("project attributes: {}".format(project_attribs)) # get format and fps property from avalon db on project - width = project_data["resolutionWidth"] - height = project_data["resolutionHeight"] - pixel_aspect = project_data["pixelAspect"] - fps = project_data['fps'] - format_name = project_data['code'] + width = project_attribs["resolutionWidth"] + height = project_attribs["resolutionHeight"] + pixel_aspect = project_attribs["pixelAspect"] + fps = project_attribs["fps"] + format_name = project_entity["code"] # create new format in hiero project format = hiero.core.Format(width, height, pixel_aspect, format_name) diff --git a/client/ayon_core/hosts/hiero/api/tags.py b/client/ayon_core/hosts/hiero/api/tags.py index 6491b1f384..5d13dd98c3 100644 --- a/client/ayon_core/hosts/hiero/api/tags.py +++ b/client/ayon_core/hosts/hiero/api/tags.py @@ -3,7 +3,8 @@ import re import os import hiero -from ayon_core.client import get_project, get_assets +import ayon_api +from ayon_core.client import get_assets from ayon_core.lib import Logger from ayon_core.pipeline import get_current_project_name @@ -143,18 +144,19 @@ def add_tags_to_workfile(): # Get project task types. project_name = get_current_project_name() - project_doc = get_project(project_name) - tasks = project_doc["config"]["tasks"] + project_entity = ayon_api.get_project(project_name) + task_types = project_entity["taskType"] nks_pres_tags["[Tasks]"] = {} - log.debug("__ tasks: {}".format(tasks)) - for task_type in tasks.keys(): - nks_pres_tags["[Tasks]"][task_type.lower()] = { + log.debug("__ tasks: {}".format(task_types)) + for task_type in task_types: + task_type_name = task_type["name"] + nks_pres_tags["[Tasks]"][task_type_name.lower()] = { "editable": "1", - "note": task_type, + "note": task_type_name, "icon": "icons:TagGood.png", "metadata": { "productType": "task", - "type": task_type + "type": task_type_name } } diff --git a/client/ayon_core/hosts/hiero/plugins/load/load_effects.py b/client/ayon_core/hosts/hiero/plugins/load/load_effects.py index 809080e87e..39c355ebe0 100644 --- a/client/ayon_core/hosts/hiero/plugins/load/load_effects.py +++ b/client/ayon_core/hosts/hiero/plugins/load/load_effects.py @@ -2,9 +2,7 @@ import json from collections import OrderedDict import six -from ayon_core.client import ( - get_version_by_id -) +from ayon_core.client import get_version_by_id from ayon_core.pipeline import ( AVALON_CONTAINER_ID, diff --git a/client/ayon_core/hosts/houdini/api/lib.py b/client/ayon_core/hosts/houdini/api/lib.py index 9db055779d..ea62a1f0a6 100644 --- a/client/ayon_core/hosts/houdini/api/lib.py +++ b/client/ayon_core/hosts/houdini/api/lib.py @@ -9,9 +9,10 @@ import json from contextlib import contextmanager import six +import ayon_api from ayon_core.lib import StringTemplate -from ayon_core.client import get_project, get_asset_by_name +from ayon_core.client import get_asset_by_name from ayon_core.settings import get_current_project_settings from ayon_core.pipeline import ( Anatomy, @@ -839,8 +840,8 @@ def get_current_context_template_data_with_asset_data(): task_name = context["task_name"] host_name = get_current_host_name() - anatomy = Anatomy(project_name) - project_doc = get_project(project_name) + project_entity = ayon_api.get_project(project_name) + anatomy = Anatomy(project_name, project_entity=project_entity) asset_doc = get_asset_by_name(project_name, asset_name) # get context specific vars @@ -858,7 +859,7 @@ def get_current_context_template_data_with_asset_data(): asset_data["frameEndHandle"] = frame_end + handle_end template_data = get_template_data( - project_doc, asset_doc, task_name, host_name + project_entity, asset_doc, task_name, host_name ) template_data["root"] = anatomy.roots template_data["assetData"] = asset_data diff --git a/client/ayon_core/hosts/max/api/lib.py b/client/ayon_core/hosts/max/api/lib.py index 05c3364e4a..5c8395a2ff 100644 --- a/client/ayon_core/hosts/max/api/lib.py +++ b/client/ayon_core/hosts/max/api/lib.py @@ -9,7 +9,9 @@ import six from ayon_core.pipeline import get_current_project_name, colorspace from ayon_core.settings import get_project_settings from ayon_core.pipeline.context_tools import ( - get_current_project, get_current_project_asset) + get_current_project_entity, + get_current_project_asset, +) from ayon_core.style import load_stylesheet from pymxs import runtime as rt @@ -220,16 +222,16 @@ def reset_scene_resolution(): Returns: None """ - data = ["data.resolutionWidth", "data.resolutionHeight"] - project_resolution = get_current_project(fields=data) - project_resolution_data = project_resolution["data"] - asset_resolution = get_current_project_asset(fields=data) - asset_resolution_data = asset_resolution["data"] + fields = ["attrib.resolutionWidth", "attrib.resolutionHeight"] + project_entity = get_current_project_entity(fields=fields) + project_attribs = project_entity["attrib"] + asset_doc = get_current_project_asset(fields=fields) + asset_data = asset_doc["data"] # Set project resolution - project_width = int(project_resolution_data.get("resolutionWidth", 1920)) - project_height = int(project_resolution_data.get("resolutionHeight", 1080)) - width = int(asset_resolution_data.get("resolutionWidth", project_width)) - height = int(asset_resolution_data.get("resolutionHeight", project_height)) + project_width = int(project_attribs.get("resolutionWidth", 1920)) + project_height = int(project_attribs.get("resolutionHeight", 1080)) + width = int(asset_data.get("resolutionWidth", project_width)) + height = int(asset_data.get("resolutionHeight", project_height)) set_scene_resolution(width, height) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_resolution_setting.py b/client/ayon_core/hosts/max/plugins/publish/validate_resolution_setting.py index 0058d3b262..1913e77210 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_resolution_setting.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_resolution_setting.py @@ -43,8 +43,8 @@ class ValidateResolutionSetting(pyblish.api.InstancePlugin, def get_db_resolution(self, instance): asset_doc = instance.data["assetEntity"] - project_doc = instance.context.data["projectEntity"] - for data in [asset_doc["data"], project_doc["data"]]: + project_entity = instance.context.data["projectEntity"] + for data in [asset_doc["data"], project_entity["attrib"]]: if "resolutionWidth" in data and "resolutionHeight" in data: width = data["resolutionWidth"] height = data["resolutionHeight"] diff --git a/client/ayon_core/hosts/maya/api/commands.py b/client/ayon_core/hosts/maya/api/commands.py index f69dca97a8..ad669980df 100644 --- a/client/ayon_core/hosts/maya/api/commands.py +++ b/client/ayon_core/hosts/maya/api/commands.py @@ -2,7 +2,9 @@ """OpenPype script commands to be used directly in Maya.""" from maya import cmds -from ayon_core.client import get_asset_by_name, get_project +from ayon_api import get_project + +from ayon_core.client import get_asset_by_name from ayon_core.pipeline import get_current_project_name, get_current_asset_name @@ -39,16 +41,22 @@ class ToolWindows: def _resolution_from_document(doc): - if not doc or "data" not in doc: - print("Entered document is not valid. \"{}\"".format(str(doc))) + if not doc: + print("Entered document is not valid. \"{}\"".format( + str(doc) + )) return None - resolution_width = doc["data"].get("resolutionWidth") - resolution_height = doc["data"].get("resolutionHeight") + attributes = doc.get("attrib") + if attributes is None: + attributes = doc.get("data", {}) + + resolution_width = attributes.get("resolutionWidth") + resolution_height = attributes.get("resolutionHeight") # Backwards compatibility if resolution_width is None or resolution_height is None: - resolution_width = doc["data"].get("resolution_width") - resolution_height = doc["data"].get("resolution_height") + resolution_width = attributes.get("resolution_width") + resolution_height = attributes.get("resolution_height") # Make sure both width and height are set if resolution_width is None or resolution_height is None: @@ -77,8 +85,8 @@ def reset_resolution(): "Asset \"{}\" does not have set resolution." " Trying to get resolution from project" ).format(asset_name)) - project_doc = get_project(project_name) - resolution = _resolution_from_document(project_doc) + project_entity = get_project(project_name) + resolution = _resolution_from_document(project_entity) if resolution is None: msg = "Using default resolution {}x{}" diff --git a/client/ayon_core/hosts/maya/api/lib.py b/client/ayon_core/hosts/maya/api/lib.py index 1aa2244111..dc242ebd15 100644 --- a/client/ayon_core/hosts/maya/api/lib.py +++ b/client/ayon_core/hosts/maya/api/lib.py @@ -19,8 +19,9 @@ from six import string_types from maya import cmds, mel from maya.api import OpenMaya +import ayon_api + from ayon_core.client import ( - get_project, get_asset_by_name, get_subsets, get_last_versions, @@ -2504,8 +2505,10 @@ def get_fps_for_current_context(): ) or {} fps = asset_doc.get("data", {}).get("fps") if not fps: - project_doc = get_project(project_name, fields=["data.fps"]) or {} - fps = project_doc.get("data", {}).get("fps") + project_entity = ayon_api.get_project( + project_name, fields=["attrib.fps"] + ) or {} + fps = project_entity.get("attrib", {}).get("fps") if not fps: fps = 25 @@ -2624,8 +2627,8 @@ def reset_scene_resolution(): """ project_name = get_current_project_name() - project_doc = get_project(project_name) - project_data = project_doc["data"] + project_entity = ayon_api.get_project(project_name) + project_attribs = project_entity["attrib"] asset_data = get_current_project_asset()["data"] # Set project resolution @@ -2633,10 +2636,10 @@ def reset_scene_resolution(): height_key = "resolutionHeight" pixelAspect_key = "pixelAspect" - width = asset_data.get(width_key, project_data.get(width_key, 1920)) - height = asset_data.get(height_key, project_data.get(height_key, 1080)) + width = asset_data.get(width_key, project_attribs.get(width_key, 1920)) + height = asset_data.get(height_key, project_attribs.get(height_key, 1080)) pixelAspect = asset_data.get(pixelAspect_key, - project_data.get(pixelAspect_key, 1)) + project_attribs.get(pixelAspect_key, 1)) set_scene_resolution(width, height, pixelAspect) diff --git a/client/ayon_core/hosts/maya/api/setdress.py b/client/ayon_core/hosts/maya/api/setdress.py index 8d09716bf6..ab2da93d64 100644 --- a/client/ayon_core/hosts/maya/api/setdress.py +++ b/client/ayon_core/hosts/maya/api/setdress.py @@ -296,7 +296,7 @@ def update_package_version(container, version): assert current_representation is not None, "This is a bug" - version_doc, subset_doc, asset_doc, project_doc = ( + version_doc, subset_doc, asset_doc, project_entity = ( get_representation_parents(project_name, current_representation) ) diff --git a/client/ayon_core/hosts/maya/hooks/pre_copy_mel.py b/client/ayon_core/hosts/maya/hooks/pre_copy_mel.py index 03ca8661bd..3fd81ceff4 100644 --- a/client/ayon_core/hosts/maya/hooks/pre_copy_mel.py +++ b/client/ayon_core/hosts/maya/hooks/pre_copy_mel.py @@ -11,11 +11,13 @@ class PreCopyMel(PreLaunchHook): launch_types = {LaunchTypes.local} def execute(self): - project_doc = self.data["project_doc"] + project_entity = self.data["project_entity"] workdir = self.launch_context.env.get("AYON_WORKDIR") if not workdir: self.log.warning("BUG: Workdir is not filled.") return project_settings = self.data["project_settings"] - create_workspace_mel(workdir, project_doc["name"], project_settings) + create_workspace_mel( + workdir, project_entity["name"], project_settings + ) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_resolution.py b/client/ayon_core/hosts/maya/plugins/publish/validate_resolution.py index ff552f566d..1a35f1b764 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_resolution.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_resolution.py @@ -85,8 +85,8 @@ class ValidateResolution(pyblish.api.InstancePlugin, @classmethod def get_db_resolution(cls, instance): asset_doc = instance.data["assetEntity"] - project_doc = instance.context.data["projectEntity"] - for data in [asset_doc["data"], project_doc["data"]]: + project_entity = instance.context.data["projectEntity"] + for data in [asset_doc["data"], project_entity["attrib"]]: if ( "resolutionWidth" in data and "resolutionHeight" in data and diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index e304b33dc7..f745d4ff4b 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -12,9 +12,9 @@ from collections import OrderedDict import nuke from qtpy import QtCore, QtWidgets +import ayon_api from ayon_core.client import ( - get_project, get_asset_by_name, get_versions, get_last_versions, @@ -128,7 +128,7 @@ class Context: workfiles_tool_timer = None # Seems unused - _project_doc = None + _project_entity = None def get_main_window(): @@ -1453,14 +1453,14 @@ class WorkfileSettings(object): """ def __init__(self, root_node=None, nodes=None, **kwargs): - project_doc = kwargs.get("project") - if project_doc is None: + project_entity = kwargs.get("project") + if project_entity is None: project_name = get_current_project_name() - project_doc = get_project(project_name) + project_entity = ayon_api.get_project(project_name) else: - project_name = project_doc["name"] + project_name = project_entity["name"] - Context._project_doc = project_doc + Context._project_entity = project_entity self._project_name = project_name self._asset = get_current_asset_name() self._asset_entity = get_asset_by_name(project_name, self._asset) diff --git a/client/ayon_core/hosts/traypublisher/api/editorial.py b/client/ayon_core/hosts/traypublisher/api/editorial.py index 6153bc5752..7cdfb0b699 100644 --- a/client/ayon_core/hosts/traypublisher/api/editorial.py +++ b/client/ayon_core/hosts/traypublisher/api/editorial.py @@ -184,7 +184,7 @@ class ShotMetadataSolver: # in case first parent is project then start parents from start if ( _index == 0 - and parent_token_type == "Project" + and parent_token_type == "project" ): project_parent = parents[0] parents = [project_parent] @@ -209,14 +209,14 @@ class ShotMetadataSolver: return "/".join( [ p["entity_name"] for p in parents - if p["entity_type"] != "Project" + if p["entity_type"] != "project" ] ) if parents else "" def _get_parents_from_selected_asset( self, asset_doc, - project_doc + project_entity ): """Returning parents from context on selected asset. @@ -224,12 +224,12 @@ class ShotMetadataSolver: Args: asset_doc (db obj): selected asset doc - project_doc (db obj): actual project doc + project_entity (dict[str, Any]): Project entity. Returns: list: list of dict parent components """ - project_name = project_doc["name"] + project_name = project_entity["name"] visual_hierarchy = [asset_doc] current_doc = asset_doc @@ -242,25 +242,28 @@ class ShotMetadataSolver: visual_parent = get_asset_by_id(project_name, visual_parent_id) if not visual_parent: - visual_hierarchy.append(project_doc) break visual_hierarchy.append(visual_parent) current_doc = visual_parent # add current selection context hierarchy - return [ - { + output = [] + for entity in reversed(visual_hierarchy): + output.append({ "entity_type": entity["data"]["entityType"], "entity_name": entity["name"] - } - for entity in reversed(visual_hierarchy) - ] + }) + output.append({ + "entity_type": "project", + "entity_name": project_name, + }) + return output - def _generate_tasks_from_settings(self, project_doc): + def _generate_tasks_from_settings(self, project_entity): """Convert settings inputs to task data. Args: - project_doc (db obj): actual project doc + project_entity (dict): Project entity. Raises: KeyError: Missing task type in project doc @@ -270,19 +273,23 @@ class ShotMetadataSolver: """ tasks_to_add = {} - project_task_types = project_doc["config"]["tasks"] + project_task_types = project_entity["taskTypes"] + task_type_names = { + task_type["name"] + for task_type in project_task_types + } for task_item in self.shot_add_tasks: task_name = task_item["name"] task_type = task_item["task_type"] # check if task type in project task types - if task_type not in project_task_types.keys(): + if task_type not in task_type_names: raise KeyError( "Missing task type `{}` for `{}` is not" " existing in `{}``".format( task_type, task_name, - list(project_task_types.keys()) + list(task_type_names) ) ) tasks_to_add[task_name] = {"type": task_type} @@ -304,7 +311,7 @@ class ShotMetadataSolver: tasks = {} asset_doc = source_data["selected_asset_doc"] - project_doc = source_data["project_doc"] + project_entity = source_data["project_entity"] # match clip to shot name at start shot_name = clip_name @@ -313,7 +320,9 @@ class ShotMetadataSolver: formatting_data = self._generate_tokens(shot_name, source_data) # generate parents from selected asset - parents = self._get_parents_from_selected_asset(asset_doc, project_doc) + parents = self._get_parents_from_selected_asset( + asset_doc, project_entity + ) if self.shot_rename["enabled"]: shot_name = self._rename_template(formatting_data) @@ -325,7 +334,7 @@ class ShotMetadataSolver: if self.shot_add_tasks: tasks = self._generate_tasks_from_settings( - project_doc) + project_entity) # generate hierarchy path from parents hierarchy_path = self._create_hierarchy_path(parents) diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py index a7abd3e6db..8ef96ed302 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py @@ -1,11 +1,10 @@ import os from copy import deepcopy -import opentimelineio as otio -from ayon_core.client import ( - get_asset_by_name, - get_project -) +import opentimelineio as otio +import ayon_api + +from ayon_core.client import get_asset_by_name from ayon_core.hosts.traypublisher.api.plugin import ( TrayPublishCreator, HiddenTrayPublishCreator @@ -628,7 +627,7 @@ or updating already created. Publishing will create OTIO file. # basic unique asset name clip_name = os.path.splitext(otio_clip.name)[0] - project_doc = get_project(self.project_name) + project_entity = ayon_api.get_project(self.project_name) shot_name, shot_metadata = self._shot_metadata_solver.generate_data( clip_name, @@ -636,14 +635,14 @@ or updating already created. Publishing will create OTIO file. "anatomy_data": { "project": { "name": self.project_name, - "code": project_doc["data"]["code"] + "code": project_entity["code"] }, "parent": parent_asset_name, "app": self.host_name }, "selected_asset_doc": get_asset_by_name( self.project_name, parent_asset_name), - "project_doc": project_doc + "project_entity": project_entity } ) diff --git a/client/ayon_core/hosts/unreal/hooks/pre_workfile_preparation.py b/client/ayon_core/hosts/unreal/hooks/pre_workfile_preparation.py index 0eaa1adb84..a8feaa31d8 100644 --- a/client/ayon_core/hosts/unreal/hooks/pre_workfile_preparation.py +++ b/client/ayon_core/hosts/unreal/hooks/pre_workfile_preparation.py @@ -49,7 +49,7 @@ class UnrealPrelaunchHook(PreLaunchHook): # Prepare data for fill data and for getting workfile template key anatomy = self.data["anatomy"] - project_doc = self.data["project_doc"] + project_entity = self.data["project_entity"] # Use already prepared workdir data workdir_data = copy.deepcopy(self.data["workdir_data"]) @@ -63,7 +63,7 @@ class UnrealPrelaunchHook(PreLaunchHook): workfile_template_key = get_workfile_template_key( task_type, self.host_name, - project_name=project_doc["name"] + project_name=project_entity["name"] ) # Fill templates template_obj = anatomy.templates_obj[workfile_template_key]["file"] diff --git a/client/ayon_core/lib/applications.py b/client/ayon_core/lib/applications.py index 8f1a1d10ea..866a616518 100644 --- a/client/ayon_core/lib/applications.py +++ b/client/ayon_core/lib/applications.py @@ -1381,7 +1381,7 @@ class EnvironmentPrepData(dict): data (dict): Data must contain required keys. """ required_keys = ( - "project_doc", "asset_doc", "task_name", "app", "anatomy" + "project_entity", "asset_doc", "task_name", "app", "anatomy" ) def __init__(self, data): @@ -1395,7 +1395,7 @@ class EnvironmentPrepData(dict): if data.get("env") is None: data["env"] = os.environ.copy() - project_name = data["project_doc"]["name"] + project_name = data["project_entity"]["name"] if "project_settings" not in data: data["project_settings"] = get_project_settings(project_name) @@ -1674,10 +1674,10 @@ def prepare_context_environments(data, env_group=None, addons_manager=None): # Context environments log = data["log"] - project_doc = data["project_doc"] + project_entity = data["project_entity"] asset_doc = data["asset_doc"] task_name = data["task_name"] - if not project_doc: + if not project_entity: log.info( "Skipping context environments preparation." " Launch context does not contain required data." @@ -1685,13 +1685,13 @@ def prepare_context_environments(data, env_group=None, addons_manager=None): return # Load project specific environments - project_name = project_doc["name"] + project_name = project_entity["name"] project_settings = get_project_settings(project_name) data["project_settings"] = project_settings app = data["app"] context_env = { - "AYON_PROJECT_NAME": project_doc["name"], + "AYON_PROJECT_NAME": project_entity["name"], "AYON_APP_NAME": app.full_name } if asset_doc: @@ -1727,7 +1727,7 @@ def prepare_context_environments(data, env_group=None, addons_manager=None): ) workdir_data = get_template_data( - project_doc, asset_doc, task_name, app.host_name, project_settings + project_entity, asset_doc, task_name, app.host_name, project_settings ) data["workdir_data"] = workdir_data diff --git a/client/ayon_core/modules/clockify/launcher_actions/ClockifySync.py b/client/ayon_core/modules/clockify/launcher_actions/ClockifySync.py index 5ef9033ffe..72187c6d28 100644 --- a/client/ayon_core/modules/clockify/launcher_actions/ClockifySync.py +++ b/client/ayon_core/modules/clockify/launcher_actions/ClockifySync.py @@ -1,4 +1,5 @@ -from ayon_core.client import get_projects, get_project +import ayon_api + from openpype_modules.clockify.clockify_api import ClockifyAPI from ayon_core.pipeline import LauncherAction @@ -21,7 +22,7 @@ class ClockifySync(LauncherAction): def is_compatible(self, session): """Check if there's some projects to sync""" try: - next(get_projects()) + next(ayon_api.get_projects()) return True except StopIteration: return False @@ -38,16 +39,18 @@ class ClockifySync(LauncherAction): ) project_name = session.get("AYON_PROJECT_NAME") or "" - projects_to_sync = [] if project_name.strip(): - projects_to_sync = [get_project(project_name)] + projects_to_sync = [ayon_api.get_project(project_name)] else: - projects_to_sync = get_projects() + projects_to_sync = ayon_api.get_projects() - projects_info = {} - for project in projects_to_sync: - task_types = project["config"]["tasks"].keys() - projects_info[project["name"]] = task_types + projects_info = { + project["name"]: { + task_type["name"] + for task_type in project["taskTypes"] + } + for project in projects_to_sync + } clockify_projects = self.clockify_api.get_projects(workspace_id) for project_name, task_types in projects_info.items(): diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py index 3e95049e56..ef146b5d79 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py @@ -8,9 +8,7 @@ import requests import pyblish.api -from ayon_core.client import ( - get_last_version_by_subset_name, -) +from ayon_core.client import get_last_version_by_subset_name from ayon_core.pipeline import publish from ayon_core.lib import EnumDef, is_in_tests from ayon_core.pipeline.version_start import get_versioning_start diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py index 7bc13ae4b6..c512dc570c 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py @@ -9,9 +9,7 @@ import clique import pyblish.api -from ayon_core.client import ( - get_last_version_by_subset_name, -) +from ayon_core.client import get_last_version_by_subset_name from ayon_core.pipeline import publish from ayon_core.lib import EnumDef, is_in_tests from ayon_core.pipeline.version_start import get_versioning_start diff --git a/client/ayon_core/pipeline/anatomy.py b/client/ayon_core/pipeline/anatomy.py index e7833a9a15..6ba5175a2a 100644 --- a/client/ayon_core/pipeline/anatomy.py +++ b/client/ayon_core/pipeline/anatomy.py @@ -4,11 +4,11 @@ import copy import platform import collections import numbers - -import six import time -from ayon_core.client import get_project, get_ayon_server_api_connection +import six +import ayon_api + from ayon_core.lib import Logger, get_local_site_id from ayon_core.lib.path_templates import ( TemplateUnsolved, @@ -50,13 +50,13 @@ class BaseAnatomy(object): root_key_regex = re.compile(r"{(root?[^}]+)}") root_name_regex = re.compile(r"root\[([^]]+)\]") - def __init__(self, project_doc, root_overrides=None): - project_name = project_doc["name"] + def __init__(self, project_entity, root_overrides=None): + project_name = project_entity["name"] self.project_name = project_name - self.project_code = project_doc["data"]["code"] + self.project_code = project_entity["code"] self._data = self._prepare_anatomy_data( - project_doc, root_overrides + project_entity, root_overrides ) self._templates_obj = AnatomyTemplates(self) self._roots_obj = Roots(self) @@ -78,13 +78,13 @@ class BaseAnatomy(object): def items(self): return copy.deepcopy(self._data).items() - def _prepare_anatomy_data(self, project_doc, root_overrides): + def _prepare_anatomy_data(self, project_entity, root_overrides): """Prepare anatomy data for further processing. Method added to replace `{task}` with `{task[name]}` in templates. """ - anatomy_data = self._project_doc_to_anatomy_data(project_doc) + anatomy_data = self._project_entity_to_anatomy_data(project_entity) self._apply_local_settings_on_anatomy_data( anatomy_data, @@ -311,14 +311,21 @@ class BaseAnatomy(object): data = self.root_environmets_fill_data(template) return rootless_path.format(**data) - def _project_doc_to_anatomy_data(self, project_doc): + def _project_entity_to_anatomy_data(self, project_entity): """Convert project document to anatomy data. Probably should fill missing keys and values. """ - output = copy.deepcopy(project_doc["config"]) - output["attributes"] = copy.deepcopy(project_doc["data"]) + output = copy.deepcopy(project_entity["config"]) + # TODO remove AYON convertion + task_types = copy.deepcopy(project_entity["taskTypes"]) + new_task_types = {} + for task_type in task_types: + name = task_type["name"] + new_task_types[name] = task_type + output["tasks"] = new_task_types + output["attributes"] = copy.deepcopy(project_entity["attrib"]) return output @@ -418,7 +425,9 @@ class Anatomy(BaseAnatomy): lambda: collections.defaultdict(CacheItem) ) - def __init__(self, project_name=None, site_name=None): + def __init__( + self, project_name=None, site_name=None, project_entity=None + ): if not project_name: project_name = os.environ.get("AYON_PROJECT_NAME") @@ -428,16 +437,19 @@ class Anatomy(BaseAnatomy): " to load data for specific project." )) - project_doc = self.get_project_doc_from_cache(project_name) - root_overrides = self._get_site_root_overrides(project_name, site_name) + if not project_entity: + project_entity = self.get_project_entity_from_cache(project_name) + root_overrides = self._get_site_root_overrides( + project_name, site_name + ) - super(Anatomy, self).__init__(project_doc, root_overrides) + super(Anatomy, self).__init__(project_entity, root_overrides) @classmethod - def get_project_doc_from_cache(cls, project_name): + def get_project_entity_from_cache(cls, project_name): project_cache = cls._project_cache[project_name] if project_cache.is_outdated: - project_cache.update_data(get_project(project_name)) + project_cache.update_data(ayon_api.get_project(project_name)) return copy.deepcopy(project_cache.data) @classmethod @@ -468,8 +480,7 @@ class Anatomy(BaseAnatomy): """ if not project_name: return - con = get_ayon_server_api_connection() - return con.get_project_roots_for_site( + return ayon_api.get_project_roots_for_site( project_name, get_local_site_id() ) @@ -642,13 +653,76 @@ class AnatomyTemplates(TemplatesDict): return self._solve_dict(value, data) return super(AnatomyTemplates, self)._format_value(value, data) + @staticmethod + def _ayon_template_conversion(templates): + def _convert_template_item(template_item): + # Change 'directory' to 'folder' + if "directory" in template_item: + template_item["folder"] = template_item["directory"] + + if ( + "path" not in template_item + and "file" in template_item + and "folder" in template_item + ): + template_item["path"] = "/".join( + (template_item["folder"], template_item["file"]) + ) + + def _get_default_template_name(templates): + default_template = None + for name, template in templates.items(): + if name == "default": + return "default" + + if default_template is None: + default_template = name + + return default_template + + def _fill_template_category(templates, cat_templates, cat_key): + default_template_name = _get_default_template_name(cat_templates) + for template_name, cat_template in cat_templates.items(): + _convert_template_item(cat_template) + if template_name == default_template_name: + templates[cat_key] = cat_template + else: + new_name = "{}_{}".format(cat_key, template_name) + templates["others"][new_name] = cat_template + + others_templates = templates.pop("others", None) or {} + new_others_templates = {} + templates["others"] = new_others_templates + for name, template in others_templates.items(): + _convert_template_item(template) + new_others_templates[name] = template + + for key in ( + "work", + "publish", + "hero", + ): + cat_templates = templates.pop(key) + _fill_template_category(templates, cat_templates, key) + + delivery_templates = templates.pop("delivery", None) or {} + new_delivery_templates = {} + for name, delivery_template in delivery_templates.items(): + new_delivery_templates[name] = "/".join( + (delivery_template["directory"], delivery_template["file"]) + ) + templates["delivery"] = new_delivery_templates + def set_templates(self, templates): if not templates: self.reset() return - self._raw_templates = copy.deepcopy(templates) templates = copy.deepcopy(templates) + # TODO remove AYON convertion + self._ayon_template_conversion(templates) + + self._raw_templates = copy.deepcopy(templates) v_queue = collections.deque() v_queue.append(templates) while v_queue: @@ -834,7 +908,7 @@ class AnatomyTemplates(TemplatesDict): key_2: "value_2" key_4: "value_3/value_2" """ - default_key_values = templates.pop("defaults", {}) + default_key_values = templates.pop("common", {}) for key, value in tuple(templates.items()): if isinstance(value, dict): continue diff --git a/client/ayon_core/pipeline/context_tools.py b/client/ayon_core/pipeline/context_tools.py index 86b3d770b4..7fa3def33a 100644 --- a/client/ayon_core/pipeline/context_tools.py +++ b/client/ayon_core/pipeline/context_tools.py @@ -6,13 +6,13 @@ import logging import platform import uuid +import ayon_api import pyblish.api from pyblish.lib import MessageHandler from ayon_core import AYON_CORE_ROOT from ayon_core.host import HostBase from ayon_core.client import ( - get_project, get_asset_by_id, get_asset_by_name, version_is_latest, @@ -399,7 +399,7 @@ def get_current_task_name(): return get_global_context()["task_name"] -def get_current_project(fields=None): +def get_current_project_entity(fields=None): """Helper function to get project document based on global Session. This function should be called only in process where host is installed. @@ -410,7 +410,7 @@ def get_current_project(fields=None): """ project_name = get_current_project_name() - return get_project(project_name, fields=fields) + return ayon_api.get_project(project_name, fields=fields) def get_current_project_asset(asset_name=None, asset_id=None, fields=None): @@ -605,10 +605,10 @@ def change_current_context(asset_doc, task_name, template_key=None): project_name = get_current_project_name() workdir = None if asset_doc: - project_doc = get_project(project_name) + project_entity = ayon_api.get_project(project_name) host_name = get_current_host_name() workdir = get_workdir( - project_doc, + project_entity, asset_doc, task_name, host_name, diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index 056836d712..443dc6df8e 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -1,15 +1,15 @@ import os import platform import copy -import getpass import logging import inspect import collections import numbers +import ayon_api + from ayon_core.host import ILoadHost from ayon_core.client import ( - get_project, get_assets, get_subsets, get_versions, @@ -165,17 +165,14 @@ def get_contexts_for_repre_docs(project_name, repre_docs): for asset_doc in asset_docs } - project_doc = get_project(project_name) + project_entity = ayon_api.get_project(project_name) 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") - }, + "project": project_entity, "asset": asset_doc, "subset": subset_doc, "version": version_doc, @@ -218,15 +215,12 @@ def get_subset_contexts(subset_ids, project_name=None): for asset_doc in asset_docs } - project_doc = get_project(project_name) + project_entity = ayon_api.get_project(project_name) 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") - }, + "project": project_entity, "asset": asset_doc, "subset": subset_doc } @@ -557,10 +551,13 @@ def get_representation_path_from_context(context): from ayon_core.pipeline import get_current_project_name representation = context["representation"] - project_doc = context.get("project") + project_entity = context.get("project") root = None - if project_doc and project_doc["name"] != get_current_project_name(): - anatomy = Anatomy(project_doc["name"]) + if ( + project_entity + and project_entity["name"] != get_current_project_name() + ): + anatomy = Anatomy(project_entity["name"]) root = anatomy.roots return get_representation_path(representation, root) diff --git a/client/ayon_core/pipeline/template_data.py b/client/ayon_core/pipeline/template_data.py index e9c57521d4..8ecba828a6 100644 --- a/client/ayon_core/pipeline/template_data.py +++ b/client/ayon_core/pipeline/template_data.py @@ -1,4 +1,5 @@ -from ayon_core.client import get_project, get_asset_by_name +import ayon_api +from ayon_core.client import get_asset_by_name from ayon_core.settings import get_studio_settings from ayon_core.lib.local_settings import get_ayon_username @@ -27,13 +28,13 @@ def get_general_template_data(settings=None): } -def get_project_template_data(project_doc=None, project_name=None): +def get_project_template_data(project_entity=None, project_name=None): """Extract data from project document that are used in templates. Project document must have 'name' and (at this moment) optional key 'data.code'. - One of 'project_name' or 'project_doc' must be passed. With prepared + One of 'project_name' or 'project_entity' must be passed. With prepared project document is function much faster because don't have to query. Output contains formatting keys: @@ -41,7 +42,7 @@ def get_project_template_data(project_doc=None, project_name=None): - 'project[code]' - Project code Args: - project_doc (Dict[str, Any]): Queried project document. + project_entity (Dict[str, Any]): Queried project entity. project_name (str): Name of project. Returns: @@ -49,12 +50,12 @@ def get_project_template_data(project_doc=None, project_name=None): """ if not project_name: - project_name = project_doc["name"] + project_name = project_entity["name"] - if not project_doc: - project_doc = get_project(project_name, fields=["data.code"]) + if not project_entity: + project_entity = ayon_api.get_project(project_name, fields=["code"]) - project_code = project_doc.get("data", {}).get("code") + project_code = project_entity["code"] return { "project": { "name": project_name, @@ -120,15 +121,15 @@ def get_task_type(asset_doc, task_name): return asset_tasks_info.get(task_name, {}).get("type") -def get_task_template_data(project_doc, asset_doc, task_name): +def get_task_template_data(project_entity, asset_doc, task_name): """"Extract task specific data from project and asset documents. Required document fields: - Project: 'config.tasks' + Project: 'tasksTypes' Asset: 'data.tasks'. Args: - project_doc (Dict[str, Any]): Queried project document. + project_entity (Dict[str, Any]): Queried project entity. asset_doc (Dict[str, Any]): Queried asset document. task_name (str): Name of task for which data should be returned. @@ -136,9 +137,10 @@ def get_task_template_data(project_doc, asset_doc, task_name): Dict[str, Dict[str, str]]: Template data """ - project_task_types = project_doc["config"]["tasks"] + project_task_types = project_entity["taskTypes"] + task_types_by_name = {task["name"]: task for task in project_task_types} task_type = get_task_type(asset_doc, task_name) - task_code = project_task_types.get(task_type, {}).get("short_name") + task_code = task_types_by_name.get(task_type, {}).get("shortName") return { "task": { @@ -150,7 +152,7 @@ def get_task_template_data(project_doc, asset_doc, task_name): def get_template_data( - project_doc, + project_entity, asset_doc=None, task_name=None, host_name=None, @@ -166,11 +168,11 @@ def get_template_data( and their values won't be added to template data if are not passed. Required document fields: - Project: 'name', 'data.code', 'config.tasks' + Project: 'name', 'code', 'taskTypes.name' Asset: 'name', 'data.parents', 'data.tasks' Args: - project_doc (Dict[str, Any]): Mongo document of project from MongoDB. + project_entity (Dict[str, Any]): Project entity. asset_doc (Dict[str, Any]): Mongo document of asset from MongoDB. task_name (Union[str, None]): Task name under passed asset. host_name (Union[str, None]): Used to fill '{app}' key. @@ -182,14 +184,14 @@ def get_template_data( """ template_data = get_general_template_data(settings) - template_data.update(get_project_template_data(project_doc)) + template_data.update(get_project_template_data(project_entity)) if asset_doc: template_data.update(get_asset_template_data( - asset_doc, project_doc["name"] + asset_doc, project_entity["name"] )) if task_name: template_data.update(get_task_template_data( - project_doc, asset_doc, task_name + project_entity, asset_doc, task_name )) if host_name: @@ -225,9 +227,7 @@ def get_template_data_with_names( Dict[str, Any]: Data prepared for filling workdir template. """ - project_doc = get_project( - project_name, fields=["name", "data.code", "config.tasks"] - ) + project_entity = ayon_api.get_project(project_name) asset_doc = None if asset_name: asset_doc = get_asset_by_name( @@ -236,5 +236,5 @@ def get_template_data_with_names( fields=["name", "data.parents", "data.tasks"] ) return get_template_data( - project_doc, asset_doc, task_name, host_name, settings + project_entity, asset_doc, task_name, host_name, settings ) diff --git a/client/ayon_core/pipeline/usdlib.py b/client/ayon_core/pipeline/usdlib.py index 2a5a317d72..971caa7b87 100644 --- a/client/ayon_core/pipeline/usdlib.py +++ b/client/ayon_core/pipeline/usdlib.py @@ -2,14 +2,16 @@ import os import re import logging +import ayon_api try: from pxr import Usd, UsdGeom, Sdf, Kind except ImportError: # Allow to fall back on Multiverse 6.3.0+ pxr usd library from mvpxr import Usd, UsdGeom, Sdf, Kind -from ayon_core.client import get_project, get_asset_by_name +from ayon_core.client import get_asset_by_name from ayon_core.pipeline import Anatomy, get_current_project_name +from ayon_core.pipeline.template_data import get_template_data log = logging.getLogger(__name__) @@ -142,7 +144,7 @@ def create_model(filename, asset, variant_subsets): ) path = get_usd_master_path( - asset=asset_doc, subset=subset, representation="usd" + asset=asset_doc, product_name=subset, representation="usd" ) variants.append((variant, path)) @@ -195,7 +197,7 @@ def create_shade(filename, asset, variant_subsets): shade_subset = re.sub("^usdModel", "usdShade", subset) path = get_usd_master_path( - asset=asset_doc, subset=shade_subset, representation="usd" + asset=asset_doc, product_name=shade_subset, representation="usd" ) variants.append((variant, path)) @@ -223,7 +225,7 @@ def create_shade_variation(filename, asset, model_variant, shade_variants): model=model_variant, shade=variant ) path = get_usd_master_path( - asset=asset_doc, subset=subset, representation="usd" + asset=asset_doc, product_name=subset, representation="usd" ) variants.append((variant, path)) @@ -306,20 +308,21 @@ def _create_variants_file( return stage -def get_usd_master_path(asset, subset, representation): +def get_usd_master_path(asset, product_name, representation): """Get the filepath for a .usd file of a subset. This will return the path to an unversioned master file generated by `usd_master_file.py`. + Args: + asset (Union[str, dict]): Folder path or asset document. + product_name (str): Product name. + representation (str): Representation name. """ project_name = get_current_project_name() - anatomy = Anatomy(project_name) - project_doc = get_project( - project_name, - fields=["name", "data.code"] - ) + project_entity = ayon_api.get_project(project_name) + anatomy = Anatomy(project_name, project_entity=project_entity) if isinstance(asset, dict) and "name" in asset: # Allow explicitly passing asset document @@ -327,27 +330,23 @@ def get_usd_master_path(asset, subset, representation): else: asset_doc = get_asset_by_name(project_name, asset, fields=["name"]) + template_data = get_template_data(project_entity, asset_doc) + template_data.update({ + "product": { + "name": product_name + }, + "subset": product_name, + "representation": representation, + "version": 0, # stub version zero + }) + template_obj = anatomy.templates_obj["publish"]["path"] - path = template_obj.format_strict( - { - "project": { - "name": project_name, - "code": project_doc.get("data", {}).get("code") - }, - "folder": { - "name": asset_doc["name"], - }, - "asset": asset_doc["name"], - "subset": subset, - "representation": representation, - "version": 0, # stub version zero - } - ) + path = template_obj.format_strict(template_data) # Remove the version folder subset_folder = os.path.dirname(os.path.dirname(path)) master_folder = os.path.join(subset_folder, "master") - fname = "{0}.{1}".format(subset, representation) + fname = "{0}.{1}".format(product_name, representation) return os.path.join(master_folder, fname).replace("\\", "/") diff --git a/client/ayon_core/pipeline/workfile/path_resolving.py b/client/ayon_core/pipeline/workfile/path_resolving.py index 7718e32317..fc0de60b36 100644 --- a/client/ayon_core/pipeline/workfile/path_resolving.py +++ b/client/ayon_core/pipeline/workfile/path_resolving.py @@ -3,7 +3,9 @@ import re import copy import platform -from ayon_core.client import get_project, get_asset_by_name +import ayon_api + +from ayon_core.client import get_asset_by_name from ayon_core.settings import get_project_settings from ayon_core.lib import ( filter_profiles, @@ -141,7 +143,7 @@ def get_workdir_with_workdir_data( def get_workdir( - project_doc, + project_entity, asset_doc, task_name, host_name, @@ -152,14 +154,14 @@ def get_workdir( """Fill workdir path from entered data and project's anatomy. Args: - project_doc (Dict[str, Any]): Mongo document of project from MongoDB. + project_entity (Dict[str, Any]): Project entity. asset_doc (Dict[str, Any]): Mongo document of asset from MongoDB. task_name (str): Task name for which are workdir data preapred. host_name (str): Host which is used to workdir. This is required because workdir template may contain `{app}` key. In `Session` is stored under `AYON_HOST_NAME` key. anatomy (Anatomy): Optional argument. Anatomy object is created using - project name from `project_doc`. It is preferred to pass this + project name from `project_entity`. It is preferred to pass this argument as initialization of a new Anatomy object may be time consuming. template_key (str): Key of work templates in anatomy templates. Default @@ -173,10 +175,12 @@ def get_workdir( """ if not anatomy: - anatomy = Anatomy(project_doc["name"]) + anatomy = Anatomy( + project_entity["name"], project_entity=project_entity + ) workdir_data = get_template_data( - project_doc, asset_doc, task_name, host_name + project_entity, asset_doc, task_name, host_name ) # Output is TemplateResult object which contain useful data return get_workdir_with_workdir_data( @@ -336,7 +340,7 @@ def get_last_workfile( def get_custom_workfile_template( - project_doc, + project_entity, asset_doc, task_name, host_name, @@ -360,7 +364,7 @@ def get_custom_workfile_template( project and asset as parents of processing task name. Args: - project_doc (Dict[str, Any]): Project document from MongoDB. + project_entity (Dict[str, Any]): Project entity. asset_doc (Dict[str, Any]): Asset document from MongoDB. task_name (str): Name of task for which templates are filtered. host_name (str): Name of host. @@ -376,7 +380,7 @@ def get_custom_workfile_template( log = Logger.get_logger("CustomWorkfileResolve") - project_name = project_doc["name"] + project_name = project_entity["name"] if project_settings is None: project_settings = get_project_settings(project_name) @@ -413,7 +417,7 @@ def get_custom_workfile_template( # get project, asset, task anatomy context data anatomy_context_data = get_template_data( - project_doc, asset_doc, task_name, host_name + project_entity, asset_doc, task_name, host_name ) # add root dict anatomy_context_data["root"] = anatomy.roots @@ -457,25 +461,30 @@ def get_custom_workfile_template_by_string_context( `get_custom_workfile_template` for rest of logic. Args: - project_name(str): Project name. - asset_name(str): Asset name. - task_name(str): Task name. + project_name (str): Project name. + asset_name (str): Asset name. + task_name (str): Task name. host_name (str): Name of host. - anatomy(Anatomy): Optionally prepared anatomy object for passed + anatomy (Anatomy): Optionally prepared anatomy object for passed project. - project_settings(Dict[str, Any]): Preloaded project settings. + project_settings (Dict[str, Any]): Preloaded project settings. Returns: - str: Path to template or None if none of profiles match current - context. (Existence of formatted path is not validated.) - None: If no profile is matching context. + Union[str, None]: Path to template or None if none of profiles match + current context. (Existence of formatted path is not validated.) + """ - project_doc = get_project(project_name) + project_entity = ayon_api.get_project(project_name) asset_doc = get_asset_by_name(project_name, asset_name) return get_custom_workfile_template( - project_doc, asset_doc, task_name, host_name, anatomy, project_settings + project_entity, + asset_doc, + task_name, + host_name, + anatomy, + project_settings ) diff --git a/client/ayon_core/plugins/actions/open_file_explorer.py b/client/ayon_core/plugins/actions/open_file_explorer.py index fba3c231a5..c5c34cc4f0 100644 --- a/client/ayon_core/plugins/actions/open_file_explorer.py +++ b/client/ayon_core/plugins/actions/open_file_explorer.py @@ -1,12 +1,11 @@ import os import platform import subprocess - from string import Formatter -from ayon_core.client import ( - get_project, - get_asset_by_name, -) + +import ayon_api + +from ayon_core.client import get_asset_by_name from ayon_core.pipeline import ( Anatomy, LauncherAction, @@ -63,10 +62,10 @@ class OpenTaskPath(LauncherAction): return path def _get_workdir(self, project_name, asset_name, task_name): - project = get_project(project_name) - asset = get_asset_by_name(project_name, asset_name) + project_entity = ayon_api.get_project(project_name) + asset_doc = get_asset_by_name(project_name, asset_name) - data = get_template_data(project, asset, task_name) + data = get_template_data(project_entity, asset_doc, task_name) anatomy = Anatomy(project_name) workdir = anatomy.templates_obj["work"]["folder"].format(data) diff --git a/client/ayon_core/plugins/load/push_to_library.py b/client/ayon_core/plugins/load/push_to_library.py index 39f95d134c..067c652cd9 100644 --- a/client/ayon_core/plugins/load/push_to_library.py +++ b/client/ayon_core/plugins/load/push_to_library.py @@ -40,9 +40,9 @@ class PushToLibraryProject(load.SubsetLoaderPlugin): "main.py" ) - project_doc = context["project"] + project_entity = context["project"] version_doc = context["version"] - project_name = project_doc["name"] + project_name = project_entity["name"] version_id = str(version_doc["_id"]) args = get_ayon_launcher_args( diff --git a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py index b62935dd6a..8ad8a013c2 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py @@ -186,8 +186,11 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): def fill_anatomy_data(self, context): self.log.debug("Storing anatomy data to instance data.") - project_doc = context.data["projectEntity"] - project_task_types = project_doc["config"]["tasks"] + project_entity = context.data["projectEntity"] + task_types_by_name = { + task_type["name"]: task_type + for task_type in project_entity["taskTypes"] + } for instance in context: anatomy_data = copy.deepcopy(context.data["anatomyData"]) @@ -202,8 +205,8 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): } }) - self._fill_asset_data(instance, project_doc, anatomy_data) - self._fill_task_data(instance, project_task_types, anatomy_data) + self._fill_asset_data(instance, project_entity, anatomy_data) + self._fill_task_data(instance, task_types_by_name, anatomy_data) # Define version version_number = None @@ -258,7 +261,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): anatomy_data["fps"] = float("{:0.2f}".format(float(fps))) # Store anatomy data - instance.data["projectEntity"] = project_doc + instance.data["projectEntity"] = project_entity instance.data["anatomyData"] = anatomy_data instance.data["version"] = version_number @@ -272,14 +275,14 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): json.dumps(anatomy_data, indent=4) )) - def _fill_asset_data(self, instance, project_doc, anatomy_data): + def _fill_asset_data(self, instance, project_entity, anatomy_data): # QUESTION should we make sure that all asset data are poped if asset # data cannot be found? # - 'asset', 'hierarchy', 'parent', 'folder' asset_doc = instance.data.get("assetEntity") if asset_doc: parents = asset_doc["data"].get("parents") or list() - parent_name = project_doc["name"] + parent_name = project_entity["name"] if parents: parent_name = parents[-1] @@ -298,7 +301,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): hierarchy = instance.data["hierarchy"] anatomy_data["hierarchy"] = hierarchy - parent_name = project_doc["name"] + parent_name = project_entity["name"] if hierarchy: parent_name = hierarchy.split("/")[-1] @@ -312,7 +315,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): }, }) - def _fill_task_data(self, instance, project_task_types, anatomy_data): + def _fill_task_data(self, instance, task_types_by_name, anatomy_data): # QUESTION should we make sure that all task data are poped if task # data cannot be resolved? # - 'task' @@ -325,7 +328,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): # Find task data based on asset entity asset_doc = instance.data.get("assetEntity") task_data = self._get_task_data_from_asset( - asset_doc, task_name, project_task_types + asset_doc, task_name, task_types_by_name ) if task_data: # Fill task data @@ -362,9 +365,9 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): task_info = tasks_info.get(task_name, {}) task_type = task_info.get("type") task_code = ( - project_task_types + task_types_by_name .get(task_type, {}) - .get("short_name") + .get("shortName") ) anatomy_data["task"] = { "name": task_name, @@ -373,14 +376,14 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): } def _get_task_data_from_asset( - self, asset_doc, task_name, project_task_types + self, asset_doc, task_name, task_types_by_name ): """ Args: asset_doc (Union[dict[str, Any], None]): Asset document. task_name (Union[str, None]): Task name. - project_task_types (dict[str, dict[str, Any]]): Project task + task_types_by_name (dict[str, dict[str, Any]]): Project task types. Returns: @@ -393,9 +396,9 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): asset_tasks = asset_doc["data"]["tasks"] task_type = asset_tasks.get(task_name, {}).get("type") task_code = ( - project_task_types + task_types_by_name .get(task_type, {}) - .get("short_name") + .get("shortName") ) return { "name": task_name, diff --git a/client/ayon_core/plugins/publish/collect_context_entities.py b/client/ayon_core/plugins/publish/collect_context_entities.py index 64ef73e2d9..b5646576d6 100644 --- a/client/ayon_core/plugins/publish/collect_context_entities.py +++ b/client/ayon_core/plugins/publish/collect_context_entities.py @@ -12,8 +12,9 @@ Provides: """ import pyblish.api +import ayon_api -from ayon_core.client import get_project, get_asset_by_name +from ayon_core.client import get_asset_by_name from ayon_core.pipeline import KnownPublishError @@ -28,7 +29,7 @@ class CollectContextEntities(pyblish.api.ContextPlugin): asset_name = context.data["folderPath"] task_name = context.data["task"] - project_entity = get_project(project_name) + project_entity = ayon_api.get_project(project_name) if not project_entity: raise KnownPublishError( "Project '{0}' was not found.".format(project_name) diff --git a/client/ayon_core/plugins/publish/collect_hierarchy.py b/client/ayon_core/plugins/publish/collect_hierarchy.py index 8ba83d582f..3c1d19e827 100644 --- a/client/ayon_core/plugins/publish/collect_hierarchy.py +++ b/client/ayon_core/plugins/publish/collect_hierarchy.py @@ -21,7 +21,7 @@ class CollectHierarchy(pyblish.api.ContextPlugin): project_name = context.data["projectName"] final_context = {} final_context[project_name] = {} - final_context[project_name]['entity_type'] = 'Project' + final_context[project_name]["entity_type"] = "project" for instance in context: self.log.debug("Processing instance: `{}` ...".format(instance)) diff --git a/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py b/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py index 7ceaf7d2ad..ef8a8b8dfc 100644 --- a/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py +++ b/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py @@ -48,7 +48,7 @@ class ExtractHierarchyToAYON(pyblish.api.ContextPlugin): instance_asset_name = instance.data.get("folderPath") instances_by_asset_name[instance_asset_name].append(instance) - project_doc = context.data["projectEntity"] + project_entity = context.data["projectEntity"] asset_docs = get_assets( project_name, asset_names=instances_by_asset_name.keys() ) @@ -62,7 +62,7 @@ class ExtractHierarchyToAYON(pyblish.api.ContextPlugin): for instance in instances: task_name = instance.data.get("task") template_data = get_task_template_data( - project_doc, asset_doc, task_name) + project_entity, asset_doc, task_name) template_data.update(copy.deepcopy(asset_data)) instance.data["anatomyData"].update(template_data) @@ -157,7 +157,7 @@ class ExtractHierarchyToAYON(pyblish.api.ContextPlugin): Output example: { "name": "MyProject", - "entity_type": "Project", + "entity_type": "project", "attributes": {}, "tasks": [], "children": [ diff --git a/client/ayon_core/tools/loader/models/actions.py b/client/ayon_core/tools/loader/models/actions.py index dff15ea16c..70577f6e6c 100644 --- a/client/ayon_core/tools/loader/models/actions.py +++ b/client/ayon_core/tools/loader/models/actions.py @@ -5,8 +5,9 @@ import copy import collections import uuid +import ayon_api + from ayon_core.client import ( - get_project, get_assets, get_subsets, get_versions, @@ -443,8 +444,7 @@ class LoaderActionsModel: _folder_docs = get_assets(project_name, asset_ids=_folder_ids) folder_docs_by_id = {f["_id"]: f for f in _folder_docs} - project_doc = get_project(project_name) - project_doc["code"] = project_doc["data"]["code"] + project_entity = ayon_api.get_project(project_name) for version_doc in version_docs: version_id = version_doc["_id"] @@ -453,7 +453,7 @@ class LoaderActionsModel: folder_id = product_doc["parent"] folder_doc = folder_docs_by_id[folder_id] version_context_by_id[version_id] = { - "project": project_doc, + "project": project_entity, "asset": folder_doc, "subset": product_doc, "version": version_doc, @@ -470,7 +470,7 @@ class LoaderActionsModel: folder_doc = folder_docs_by_id[folder_id] repre_context_by_id[repre_doc["_id"]] = { - "project": project_doc, + "project": project_entity, "asset": folder_doc, "subset": product_doc, "version": version_doc, @@ -524,14 +524,13 @@ class LoaderActionsModel: f["_id"]: f for f in folder_docs } - project_doc = get_project(project_name) - project_doc["code"] = project_doc["data"]["code"] + project_entity = ayon_api.get_project(project_name) for product_id, product_doc in product_docs_by_id.items(): folder_id = product_doc["parent"] folder_doc = folder_docs_by_id[folder_id] product_context_by_id[product_id] = { - "project": project_doc, + "project": project_entity, "asset": folder_doc, "subset": product_doc, } @@ -545,7 +544,7 @@ class LoaderActionsModel: folder_doc = folder_docs_by_id[folder_id] repre_context_by_id[repre_doc["_id"]] = { - "project": project_doc, + "project": project_entity, "asset": folder_doc, "subset": product_doc, "version": version_doc, @@ -660,8 +659,7 @@ class LoaderActionsModel: version_ids (Iterable[str]): Version ids. """ - project_doc = get_project(project_name) - project_doc["code"] = project_doc["data"]["code"] + project_entity = ayon_api.get_project(project_name) version_docs = self._get_version_docs(project_name, version_ids) product_ids = {v["parent"] for v in version_docs} @@ -677,7 +675,7 @@ class LoaderActionsModel: folder_id = product_doc["parent"] folder_doc = folder_docs_by_id[folder_id] product_contexts.append({ - "project": project_doc, + "project": project_entity, "asset": folder_doc, "subset": product_doc, "version": version_doc, @@ -707,8 +705,7 @@ class LoaderActionsModel: representation_ids (Iterable[str]): Representation ids. """ - project_doc = get_project(project_name) - project_doc["code"] = project_doc["data"]["code"] + project_entity = ayon_api.get_project(project_name) repre_docs = list(get_representations( project_name, representation_ids=representation_ids )) @@ -730,7 +727,7 @@ class LoaderActionsModel: folder_id = product_doc["parent"] folder_doc = folder_docs_by_id[folder_id] repre_contexts.append({ - "project": project_doc, + "project": project_entity, "asset": folder_doc, "subset": product_doc, "version": version_doc, diff --git a/client/ayon_core/tools/push_to_project/models/integrate.py b/client/ayon_core/tools/push_to_project/models/integrate.py index b427f3d226..d0b26d1c03 100644 --- a/client/ayon_core/tools/push_to_project/models/integrate.py +++ b/client/ayon_core/tools/push_to_project/models/integrate.py @@ -8,8 +8,9 @@ import sys import traceback import uuid +import ayon_api + from ayon_core.client import ( - get_project, get_assets, get_asset_by_id, get_subset_by_id, @@ -453,7 +454,7 @@ class ProjectPushItemProcess: self._src_version_doc = None self._src_repre_items = None - self._project_doc = None + self._project_entity = None self._anatomy = None self._asset_doc = None self._created_asset_doc = None @@ -562,8 +563,8 @@ class ProjectPushItemProcess: src_project_name = self._item.src_project_name src_version_id = self._item.src_version_id - project_doc = get_project(src_project_name) - if not project_doc: + project_entity = ayon_api.get_project(src_project_name) + if not project_entity: self._status.set_failed( f"Source project \"{src_project_name}\" was not found" ) @@ -632,8 +633,8 @@ class ProjectPushItemProcess: # --- Destination entities --- dst_project_name = self._item.dst_project_name # Validate project existence - dst_project_doc = get_project(dst_project_name) - if not dst_project_doc: + dst_project_entity = ayon_api.get_project(dst_project_name) + if not dst_project_entity: self._status.set_failed( f"Destination project '{dst_project_name}' was not found" ) @@ -642,8 +643,11 @@ class ProjectPushItemProcess: self._log_debug( f"Destination project '{dst_project_name}' found" ) - self._project_doc = dst_project_doc - self._anatomy = Anatomy(dst_project_name) + self._project_entity = dst_project_entity + self._anatomy = Anatomy( + dst_project_name, + project_entity=dst_project_entity + ) self._project_settings = get_project_settings( self._item.dst_project_name ) @@ -651,7 +655,7 @@ class ProjectPushItemProcess: def _create_asset( self, src_asset_doc, - project_doc, + project_entity, parent_asset_doc, asset_name ): @@ -668,7 +672,8 @@ class ProjectPushItemProcess: asset_name_low = asset_name.lower() other_asset_docs = get_assets( - project_doc["name"], fields=["_id", "name", "data.visualParent"] + project_entity["name"], + fields=["_id", "name", "data.visualParent"] ) for other_asset_doc in other_asset_docs: other_name = other_asset_doc["name"] @@ -687,7 +692,9 @@ class ProjectPushItemProcess: f"Found already existing asset with name \"{other_name}\"" f" which match requested name \"{asset_name}\"" )) - return get_asset_by_id(project_doc["name"], other_asset_doc["_id"]) + return get_asset_by_id( + project_entity["name"], other_asset_doc["_id"] + ) data_keys = ( "clipIn", @@ -714,13 +721,13 @@ class ProjectPushItemProcess: asset_doc = new_asset_document( asset_name, - project_doc["_id"], + project_entity["name"], parent_id, parents, data=asset_data ) self._operations.create_entity( - project_doc["name"], + project_entity["name"], asset_doc["type"], asset_doc ) @@ -759,7 +766,7 @@ class ProjectPushItemProcess: else: asset_doc = self._create_asset( self._src_asset_doc, - self._project_doc, + self._project_entity, parent_asset_doc, new_folder_name ) @@ -785,9 +792,12 @@ class ProjectPushItemProcess: task_info = copy.deepcopy(task_info) task_info["name"] = dst_task_name # Fill rest of task information based on task type - task_type = task_info["type"] - task_type_info = self._project_doc["config"]["tasks"].get( - task_type, {}) + task_type_name = task_info["type"] + task_types_by_name = { + task_type["name"]: task_type + for task_type in self._project_entity["taskTypes"] + } + task_type_info = task_types_by_name.get(task_type_name, {}) task_info.update(task_type_info) self._task_info = task_info @@ -950,7 +960,7 @@ class ProjectPushItemProcess: template_name = self._template_name anatomy = self._anatomy formatting_data = get_template_data( - self._project_doc, + self._project_entity, self._asset_doc, self._task_info.get("name"), self.host_name diff --git a/client/ayon_core/tools/texture_copy/app.py b/client/ayon_core/tools/texture_copy/app.py index 120051060b..2736354cf2 100644 --- a/client/ayon_core/tools/texture_copy/app.py +++ b/client/ayon_core/tools/texture_copy/app.py @@ -1,12 +1,14 @@ import os import re + import click - import speedcopy +import ayon_api -from ayon_core.client import get_project, get_asset_by_name +from ayon_core.client import get_asset_by_name from ayon_core.lib import Terminal from ayon_core.pipeline import Anatomy +from ayon_core.pipeline.template_data import get_template_data t = Terminal() @@ -24,33 +26,21 @@ class TextureCopy: if os.path.splitext(x)[1].lower() in texture_extensions) return textures - def _get_destination_path(self, asset, project): - project_name = project["name"] - hierarchy = "" - parents = asset['data']['parents'] - if parents and len(parents) > 0: - hierarchy = os.path.join(*parents) + def _get_destination_path(self, asset_doc, project_entity): + project_name = project_entity["name"] product_name = "Main" product_type = "texture" - template_data = { - "project": { - "name": project_name, - "code": project['data']['code'] - }, - "asset": asset["name"], + template_data = get_template_data(project_entity, asset_doc) + template_data.update({ "family": product_type, "subset": product_name, - "folder": { - "name": asset["name"], - }, "product": { "name": product_name, "type": product_type, }, - "hierarchy": hierarchy - } - anatomy = Anatomy(project_name) + }) + anatomy = Anatomy(project_name, project_entity=project_entity) template_obj = anatomy.templates_obj["texture"]["path"] return template_obj.format_strict(template_data) @@ -92,20 +82,22 @@ class TextureCopy: else: t.echo(">>> Found {} textures ...".format(len(textures))) - project = get_project(project_name) - if not project: + project_entity = ayon_api.get_project(project_name) + if not project_entity: t.echo("!!! Project name [ {} ] not found.".format(project_name)) exit(1) - asset = get_asset_by_name(project_name, asset_name) - if not asset: + asset_doc = get_asset_by_name(project_name, asset_name) + if not asset_doc: t.echo("!!! Asset [ {} ] not found in project".format(asset_name)) exit(1) - t.echo((">>> Project [ {} ] and " - "asset [ {} ] seems to be OK ...").format(project['name'], - asset['name'])) + t.echo( + ( + ">>> Project [ {} ] and folder [ {} ] seems to be OK ..." + ).format(project_entity['name'], asset_doc['name']) + ) - dst_path = self._get_destination_path(asset, project) + dst_path = self._get_destination_path(asset_doc, project_entity) t.echo("--- Using [ {} ] as destination path".format(dst_path)) if not os.path.exists(dst_path): try: diff --git a/client/ayon_core/tools/utils/assets_widget.py b/client/ayon_core/tools/utils/assets_widget.py index 7c3fd8d97c..2ac30b1ec1 100644 --- a/client/ayon_core/tools/utils/assets_widget.py +++ b/client/ayon_core/tools/utils/assets_widget.py @@ -3,11 +3,9 @@ import collections from qtpy import QtWidgets, QtCore, QtGui import qtawesome +import ayon_api -from ayon_core.client import ( - get_project, - get_assets, -) +from ayon_core.client import get_assets from ayon_core.style import ( get_default_tools_icon_color, get_default_entity_icon_color, @@ -407,8 +405,8 @@ class _AssetModel(QtGui.QStandardItemModel): if not project_name: return [] - project_doc = get_project(project_name, fields=["_id"]) - if not project_doc: + project_entity = ayon_api.get_project(project_name, fields=["name"]) + if not project_entity: return [] # Get all assets sorted by name diff --git a/client/ayon_core/tools/utils/lib.py b/client/ayon_core/tools/utils/lib.py index e785cec390..8c2eb517df 100644 --- a/client/ayon_core/tools/utils/lib.py +++ b/client/ayon_core/tools/utils/lib.py @@ -240,7 +240,7 @@ def get_default_task_icon(color=None): return get_qta_icon_by_name_and_color("fa.male", color) -def get_task_icon(project_doc, asset_doc, task_name): +def get_task_icon(project_entity, asset_doc, task_name): """Get icon for a task. Icon should be defined by task type which is stored on project. @@ -257,9 +257,12 @@ def get_task_icon(project_doc, asset_doc, task_name): return icon task_type = task_info.get("type") - task_types = project_doc["config"]["tasks"] + task_types_by_name = { + task_type["name"]: task_type + for task_type in project_entity["taskTypes"] + } - task_type_info = task_types.get(task_type) or {} + task_type_info = task_types_by_name.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) diff --git a/client/ayon_core/tools/utils/models.py b/client/ayon_core/tools/utils/models.py index a4b6ad7885..92bed16e98 100644 --- a/client/ayon_core/tools/utils/models.py +++ b/client/ayon_core/tools/utils/models.py @@ -3,12 +3,6 @@ import logging import qtpy from qtpy import QtCore, QtGui -from ayon_core.client import get_projects -from .constants import ( - PROJECT_IS_ACTIVE_ROLE, - PROJECT_NAME_ROLE, - DEFAULT_PROJECT_LABEL -) log = logging.getLogger(__name__) diff --git a/client/ayon_core/tools/workfiles/models/workfiles.py b/client/ayon_core/tools/workfiles/models/workfiles.py index 1e9491b3d7..a0d618c1ae 100644 --- a/client/ayon_core/tools/workfiles/models/workfiles.py +++ b/client/ayon_core/tools/workfiles/models/workfiles.py @@ -6,7 +6,6 @@ import arrow import ayon_api from ayon_api.operations import OperationsSession -from ayon_core.client import get_project from ayon_core.client.operations import ( prepare_workfile_info_update_data, ) @@ -140,7 +139,9 @@ class WorkareaModel: def _get_base_data(self): if self._base_data is None: - base_data = get_template_data(get_project(self.project_name)) + base_data = get_template_data( + ayon_api.get_project(self.project_name) + ) base_data["app"] = self._controller.get_host_name() self._base_data = base_data return copy.deepcopy(self._base_data) From 0664da2a5e324f0bbe88a8f01eeedd8943967eaa Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 29 Feb 2024 18:11:19 +0100 Subject: [PATCH 385/573] rename 'get_current_asset_name' to 'get_current_folder_path' --- client/ayon_core/host/host.py | 4 ++-- .../aftereffects/plugins/create/workfile_creator.py | 2 +- .../plugins/publish/validate_instance_asset.py | 6 +++--- client/ayon_core/hosts/blender/api/ops.py | 4 ++-- client/ayon_core/hosts/blender/api/pipeline.py | 4 ++-- .../hosts/blender/plugins/create/create_workfile.py | 2 +- client/ayon_core/hosts/fusion/api/menu.py | 4 ++-- .../hosts/fusion/plugins/create/create_workfile.py | 2 +- .../harmony/plugins/publish/validate_instances.py | 6 +++--- client/ayon_core/hosts/hiero/api/menu.py | 4 ++-- .../hosts/houdini/api/creator_node_shelves.py | 2 +- client/ayon_core/hosts/houdini/api/lib.py | 6 +++--- .../hosts/houdini/plugins/create/create_workfile.py | 2 +- .../ayon_core/hosts/houdini/startup/MainMenuCommon.xml | 4 ++-- .../hosts/max/plugins/create/create_workfile.py | 2 +- client/ayon_core/hosts/maya/api/commands.py | 4 ++-- client/ayon_core/hosts/maya/api/lib.py | 8 ++++---- client/ayon_core/hosts/maya/api/menu.py | 4 ++-- client/ayon_core/hosts/maya/api/plugin.py | 2 +- .../hosts/maya/api/workfile_template_builder.py | 4 ++-- .../maya/plugins/create/create_multishot_layout.py | 4 ++-- .../hosts/maya/plugins/create/create_workfile.py | 2 +- client/ayon_core/hosts/nuke/api/lib.py | 6 +++--- client/ayon_core/hosts/nuke/api/pipeline.py | 4 ++-- .../hosts/nuke/plugins/create/workfile_creator.py | 2 +- client/ayon_core/hosts/photoshop/lib.py | 2 +- .../photoshop/plugins/create/create_flatten_image.py | 2 +- .../plugins/publish/validate_instance_asset.py | 6 +++--- .../resolve/plugins/publish/precollect_workfile.py | 4 ++-- .../ayon_core/hosts/substancepainter/api/pipeline.py | 4 ++-- .../hosts/tvpaint/plugins/create/create_render.py | 4 ++-- client/ayon_core/pipeline/__init__.py | 4 ++-- client/ayon_core/pipeline/context_tools.py | 6 +++--- client/ayon_core/pipeline/create/context.py | 10 +++++----- client/ayon_core/pipeline/workfile/build_workfile.py | 4 ++-- .../pipeline/workfile/workfile_template_builder.py | 8 ++++---- .../plugins/publish/collect_from_create_context.py | 2 +- client/ayon_core/tools/creator/window.py | 4 ++-- client/ayon_core/tools/publisher/control.py | 2 +- client/ayon_core/tools/utils/assets_widget.py | 10 +++++----- client/ayon_core/tools/utils/host_tools.py | 7 ++----- 41 files changed, 85 insertions(+), 88 deletions(-) diff --git a/client/ayon_core/host/host.py b/client/ayon_core/host/host.py index f79c22824b..d9739603d7 100644 --- a/client/ayon_core/host/host.py +++ b/client/ayon_core/host/host.py @@ -108,7 +108,7 @@ class HostBase(object): return os.environ.get("AYON_PROJECT_NAME") - def get_current_asset_name(self): + def get_current_folder_path(self): """ Returns: Union[str, None]: Current asset name. @@ -139,7 +139,7 @@ class HostBase(object): return { "project_name": self.get_current_project_name(), - "folder_path": self.get_current_asset_name(), + "folder_path": self.get_current_folder_path(), "task_name": self.get_current_task_name() } diff --git a/client/ayon_core/hosts/aftereffects/plugins/create/workfile_creator.py b/client/ayon_core/hosts/aftereffects/plugins/create/workfile_creator.py index 282e06d0bf..0dae779496 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/create/workfile_creator.py +++ b/client/ayon_core/hosts/aftereffects/plugins/create/workfile_creator.py @@ -39,7 +39,7 @@ class AEWorkfileCreator(AutoCreator): context = self.create_context project_name = context.get_current_project_name() - asset_name = context.get_current_asset_name() + asset_name = context.get_current_folder_path() task_name = context.get_current_task_name() host_name = context.host_name diff --git a/client/ayon_core/hosts/aftereffects/plugins/publish/validate_instance_asset.py b/client/ayon_core/hosts/aftereffects/plugins/publish/validate_instance_asset.py index e8f2e29a2f..bf54ec2115 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/publish/validate_instance_asset.py +++ b/client/ayon_core/hosts/aftereffects/plugins/publish/validate_instance_asset.py @@ -1,6 +1,6 @@ import pyblish.api -from ayon_core.pipeline import get_current_asset_name +from ayon_core.pipeline import get_current_folder_path from ayon_core.pipeline.publish import ( ValidateContentsOrder, PublishXmlValidationError, @@ -30,7 +30,7 @@ class ValidateInstanceAssetRepair(pyblish.api.Action): for instance in instances: data = stub.read(instance[0]) - data["folderPath"] = get_current_asset_name() + data["folderPath"] = get_current_folder_path() stub.imprint(instance[0].instance_id, data) @@ -54,7 +54,7 @@ class ValidateInstanceAsset(pyblish.api.InstancePlugin): def process(self, instance): instance_asset = instance.data["folderPath"] - current_asset = get_current_asset_name() + current_asset = get_current_folder_path() msg = ( f"Instance asset {instance_asset} is not the same " f"as current context {current_asset}." diff --git a/client/ayon_core/hosts/blender/api/ops.py b/client/ayon_core/hosts/blender/api/ops.py index dcbc44bcad..91a79f9049 100644 --- a/client/ayon_core/hosts/blender/api/ops.py +++ b/client/ayon_core/hosts/blender/api/ops.py @@ -16,7 +16,7 @@ import bpy import bpy.utils.previews from ayon_core import style -from ayon_core.pipeline import get_current_asset_name, get_current_task_name +from ayon_core.pipeline import get_current_folder_path, get_current_task_name from ayon_core.tools.utils import host_tools from .workio import OpenFileCacher @@ -388,7 +388,7 @@ class TOPBAR_MT_avalon(bpy.types.Menu): else: pyblish_menu_icon_id = 0 - asset = get_current_asset_name() + asset = get_current_folder_path() task = get_current_task_name() context_label = f"{asset}, {task}" context_label_item = layout.row() diff --git a/client/ayon_core/hosts/blender/api/pipeline.py b/client/ayon_core/hosts/blender/api/pipeline.py index fcac285f74..3270564b09 100644 --- a/client/ayon_core/hosts/blender/api/pipeline.py +++ b/client/ayon_core/hosts/blender/api/pipeline.py @@ -20,7 +20,7 @@ from ayon_core.client import get_asset_by_name from ayon_core.pipeline import ( schema, get_current_project_name, - get_current_asset_name, + get_current_folder_path, register_loader_plugin_path, register_creator_plugin_path, deregister_loader_plugin_path, @@ -223,7 +223,7 @@ def message_window(title, message): def get_asset_data(): project_name = get_current_project_name() - asset_name = get_current_asset_name() + asset_name = get_current_folder_path() asset_doc = get_asset_by_name(project_name, asset_name) return asset_doc.get("data") diff --git a/client/ayon_core/hosts/blender/plugins/create/create_workfile.py b/client/ayon_core/hosts/blender/plugins/create/create_workfile.py index ead3ed7749..29a03cb369 100644 --- a/client/ayon_core/hosts/blender/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/blender/plugins/create/create_workfile.py @@ -33,7 +33,7 @@ class CreateWorkfile(BaseCreator, AutoCreator): ) project_name = self.project_name - asset_name = self.create_context.get_current_asset_name() + asset_name = self.create_context.get_current_folder_path() task_name = self.create_context.get_current_task_name() host_name = self.create_context.host_name diff --git a/client/ayon_core/hosts/fusion/api/menu.py b/client/ayon_core/hosts/fusion/api/menu.py index a2b0a7b628..2f6a1fa808 100644 --- a/client/ayon_core/hosts/fusion/api/menu.py +++ b/client/ayon_core/hosts/fusion/api/menu.py @@ -13,7 +13,7 @@ from ayon_core.hosts.fusion.api.lib import ( set_asset_framerange, set_asset_resolution, ) -from ayon_core.pipeline import get_current_asset_name +from ayon_core.pipeline import get_current_folder_path from ayon_core.resources import get_ayon_icon_filepath from ayon_core.tools.utils import get_qt_app @@ -131,7 +131,7 @@ class OpenPypeMenu(QtWidgets.QWidget): def on_task_changed(self): # Update current context label - label = get_current_asset_name() + label = get_current_folder_path() self.asset_label.setText(label) def register_callback(self, name, fn): diff --git a/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py b/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py index dfd9da3df1..53e2a0ff62 100644 --- a/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py @@ -67,7 +67,7 @@ class FusionWorkfileCreator(AutoCreator): break project_name = self.create_context.get_current_project_name() - asset_name = self.create_context.get_current_asset_name() + asset_name = self.create_context.get_current_folder_path() task_name = self.create_context.get_current_task_name() host_name = self.create_context.host_name diff --git a/client/ayon_core/hosts/harmony/plugins/publish/validate_instances.py b/client/ayon_core/hosts/harmony/plugins/publish/validate_instances.py index fdba834de6..fa3d2e22cb 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/validate_instances.py +++ b/client/ayon_core/hosts/harmony/plugins/publish/validate_instances.py @@ -1,7 +1,7 @@ import pyblish.api import ayon_core.hosts.harmony.api as harmony -from ayon_core.pipeline import get_current_asset_name +from ayon_core.pipeline import get_current_folder_path from ayon_core.pipeline.publish import ( ValidateContentsOrder, PublishXmlValidationError, @@ -27,7 +27,7 @@ class ValidateInstanceRepair(pyblish.api.Action): # Apply pyblish.logic to get the instances for the plug-in instances = pyblish.api.instances_by_plugin(failed, plugin) - folder_path = get_current_asset_name() + folder_path = get_current_folder_path() for instance in instances: data = harmony.read(instance.data["setMembers"][0]) data["folderPath"] = folder_path @@ -44,7 +44,7 @@ class ValidateInstance(pyblish.api.InstancePlugin): def process(self, instance): instance_asset = instance.data["folderPath"] - current_asset = get_current_asset_name() + current_asset = get_current_folder_path() msg = ( "Instance asset is not the same as current asset:" f"\nInstance: {instance_asset}\nCurrent: {current_asset}" diff --git a/client/ayon_core/hosts/hiero/api/menu.py b/client/ayon_core/hosts/hiero/api/menu.py index ba0cbdd120..632b11c7d3 100644 --- a/client/ayon_core/hosts/hiero/api/menu.py +++ b/client/ayon_core/hosts/hiero/api/menu.py @@ -11,7 +11,7 @@ from ayon_core.tools.utils import host_tools from ayon_core.settings import get_project_settings from ayon_core.pipeline import ( get_current_project_name, - get_current_asset_name, + get_current_folder_path, get_current_task_name ) @@ -25,7 +25,7 @@ self._change_context_menu = None def get_context_label(): return "{}, {}".format( - get_current_asset_name(), + get_current_folder_path(), get_current_task_name() ) diff --git a/client/ayon_core/hosts/houdini/api/creator_node_shelves.py b/client/ayon_core/hosts/houdini/api/creator_node_shelves.py index 57fdef753a..273d532d5a 100644 --- a/client/ayon_core/hosts/houdini/api/creator_node_shelves.py +++ b/client/ayon_core/hosts/houdini/api/creator_node_shelves.py @@ -94,7 +94,7 @@ def create_interactive(creator_identifier, **kwargs): project_name=context.get_current_project_name(), asset_doc=get_asset_by_name( project_name=context.get_current_project_name(), - asset_name=context.get_current_asset_name() + asset_name=context.get_current_folder_path() ), task_name=context.get_current_task_name(), variant=variant, diff --git a/client/ayon_core/hosts/houdini/api/lib.py b/client/ayon_core/hosts/houdini/api/lib.py index ea62a1f0a6..fc1808b225 100644 --- a/client/ayon_core/hosts/houdini/api/lib.py +++ b/client/ayon_core/hosts/houdini/api/lib.py @@ -17,7 +17,7 @@ from ayon_core.settings import get_current_project_settings from ayon_core.pipeline import ( Anatomy, get_current_project_name, - get_current_asset_name, + get_current_folder_path, registered_host, get_current_context, get_current_host_name, @@ -97,7 +97,7 @@ def generate_ids(nodes, asset_id=None): if asset_id is None: project_name = get_current_project_name() - asset_name = get_current_asset_name() + asset_name = get_current_folder_path() # Get the asset ID from the database for the asset of current context asset_doc = get_asset_by_name(project_name, asset_name, fields=["_id"]) @@ -531,7 +531,7 @@ def reset_framerange(): # Get asset data project_name = get_current_project_name() - asset_name = get_current_asset_name() + asset_name = get_current_folder_path() # Get the asset ID from the database for the asset of current context asset_doc = get_asset_by_name(project_name, asset_name) asset_data = asset_doc["data"] diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py b/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py index 631ef6ce77..898fe5e5f9 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py @@ -26,7 +26,7 @@ class CreateWorkfile(plugin.HoudiniCreatorBase, AutoCreator): ), None) project_name = self.project_name - asset_name = self.create_context.get_current_asset_name() + asset_name = self.create_context.get_current_folder_path() task_name = self.create_context.get_current_task_name() host_name = self.host_name diff --git a/client/ayon_core/hosts/houdini/startup/MainMenuCommon.xml b/client/ayon_core/hosts/houdini/startup/MainMenuCommon.xml index b2ea142cd5..25e8c9177b 100644 --- a/client/ayon_core/hosts/houdini/startup/MainMenuCommon.xml +++ b/client/ayon_core/hosts/houdini/startup/MainMenuCommon.xml @@ -8,8 +8,8 @@ return os.environ.get("AYON_MENU_LABEL") or "AYON" ]]> diff --git a/client/ayon_core/hosts/max/plugins/create/create_workfile.py b/client/ayon_core/hosts/max/plugins/create/create_workfile.py index 1552149413..d3ae3382bc 100644 --- a/client/ayon_core/hosts/max/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/max/plugins/create/create_workfile.py @@ -24,7 +24,7 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): if instance.creator_identifier == self.identifier ), None) project_name = self.project_name - asset_name = self.create_context.get_current_asset_name() + asset_name = self.create_context.get_current_folder_path() task_name = self.create_context.get_current_task_name() host_name = self.create_context.host_name diff --git a/client/ayon_core/hosts/maya/api/commands.py b/client/ayon_core/hosts/maya/api/commands.py index ad669980df..75931e301a 100644 --- a/client/ayon_core/hosts/maya/api/commands.py +++ b/client/ayon_core/hosts/maya/api/commands.py @@ -5,7 +5,7 @@ from maya import cmds from ayon_api import get_project from ayon_core.client import get_asset_by_name -from ayon_core.pipeline import get_current_project_name, get_current_asset_name +from ayon_core.pipeline import get_current_project_name, get_current_folder_path class ToolWindows: @@ -75,7 +75,7 @@ def reset_resolution(): # Get resolution from asset project_name = get_current_project_name() - asset_name = get_current_asset_name() + asset_name = get_current_folder_path() asset_doc = get_asset_by_name(project_name, asset_name) resolution = _resolution_from_document(asset_doc) # Try get resolution from project diff --git a/client/ayon_core/hosts/maya/api/lib.py b/client/ayon_core/hosts/maya/api/lib.py index dc242ebd15..fc084d9e23 100644 --- a/client/ayon_core/hosts/maya/api/lib.py +++ b/client/ayon_core/hosts/maya/api/lib.py @@ -31,7 +31,7 @@ from ayon_core.client import ( from ayon_core.settings import get_project_settings from ayon_core.pipeline import ( get_current_project_name, - get_current_asset_name, + get_current_folder_path, get_current_task_name, discover_loader_plugins, loaders_from_representation, @@ -1668,7 +1668,7 @@ def generate_ids(nodes, asset_id=None): if asset_id is None: # Get the asset ID from the database for the asset of current context project_name = get_current_project_name() - asset_name = get_current_asset_name() + asset_name = get_current_folder_path() asset_doc = get_asset_by_name(project_name, asset_name, fields=["_id"]) assert asset_doc, "No current asset found in Session" asset_id = asset_doc['_id'] @@ -2499,7 +2499,7 @@ def get_fps_for_current_context(): """ project_name = get_current_project_name() - asset_name = get_current_asset_name() + asset_name = get_current_folder_path() asset_doc = get_asset_by_name( project_name, asset_name, fields=["data.fps"] ) or {} @@ -2531,7 +2531,7 @@ def get_frame_range(include_animation_range=False): # Set frame start/end project_name = get_current_project_name() - asset_name = get_current_asset_name() + asset_name = get_current_folder_path() asset = get_asset_by_name(project_name, asset_name) frame_start = asset["data"].get("frameStart") diff --git a/client/ayon_core/hosts/maya/api/menu.py b/client/ayon_core/hosts/maya/api/menu.py index 70347e91b6..8de025bfe5 100644 --- a/client/ayon_core/hosts/maya/api/menu.py +++ b/client/ayon_core/hosts/maya/api/menu.py @@ -8,7 +8,7 @@ import maya.utils import maya.cmds as cmds from ayon_core.pipeline import ( - get_current_asset_name, + get_current_folder_path, get_current_task_name, registered_host ) @@ -43,7 +43,7 @@ def _get_menu(menu_name=None): def get_context_label(): return "{}, {}".format( - get_current_asset_name(), + get_current_folder_path(), get_current_task_name() ) diff --git a/client/ayon_core/hosts/maya/api/plugin.py b/client/ayon_core/hosts/maya/api/plugin.py index 1151b0e248..5d6ffe0384 100644 --- a/client/ayon_core/hosts/maya/api/plugin.py +++ b/client/ayon_core/hosts/maya/api/plugin.py @@ -454,7 +454,7 @@ class RenderlayerCreator(NewCreator, MayaCreatorBase): # this instance will not have the `instance_node` data yet # until it's been saved/persisted at least once. project_name = self.create_context.get_current_project_name() - asset_name = self.create_context.get_current_asset_name() + asset_name = self.create_context.get_current_folder_path() instance_data = { "folderPath": asset_name, "task": self.create_context.get_current_task_name(), diff --git a/client/ayon_core/hosts/maya/api/workfile_template_builder.py b/client/ayon_core/hosts/maya/api/workfile_template_builder.py index 6ae2a075e3..abe8f458dc 100644 --- a/client/ayon_core/hosts/maya/api/workfile_template_builder.py +++ b/client/ayon_core/hosts/maya/api/workfile_template_builder.py @@ -4,7 +4,7 @@ from maya import cmds from ayon_core.pipeline import ( registered_host, - get_current_asset_name, + get_current_folder_path, AYON_INSTANCE_ID, AVALON_INSTANCE_ID, ) @@ -74,7 +74,7 @@ class MayaTemplateBuilder(AbstractTemplateBuilder): return True # update imported sets information - asset_name = get_current_asset_name() + asset_name = get_current_folder_path() for node in imported_sets: if not cmds.attributeQuery("id", node=node, exists=True): continue diff --git a/client/ayon_core/hosts/maya/plugins/create/create_multishot_layout.py b/client/ayon_core/hosts/maya/plugins/create/create_multishot_layout.py index e7b903312f..c640c9f52a 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_multishot_layout.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_multishot_layout.py @@ -10,7 +10,7 @@ from ayon_core.hosts.maya.api import plugin from ayon_core.lib import BoolDef, EnumDef, TextDef from ayon_core.pipeline import ( Creator, - get_current_asset_name, + get_current_folder_path, get_current_project_name, ) from ayon_core.pipeline.create import CreatorError @@ -45,7 +45,7 @@ class CreateMultishotLayout(plugin.MayaCreator): """ project_name = get_current_project_name() - folder_path = get_current_asset_name() + folder_path = get_current_folder_path() if "/" in folder_path: current_folder = get_folder_by_path(project_name, folder_path) else: diff --git a/client/ayon_core/hosts/maya/plugins/create/create_workfile.py b/client/ayon_core/hosts/maya/plugins/create/create_workfile.py index 5eb32e1c90..1c6922f942 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_workfile.py @@ -25,7 +25,7 @@ class CreateWorkfile(plugin.MayaCreatorBase, AutoCreator): ), None) project_name = self.project_name - asset_name = self.create_context.get_current_asset_name() + asset_name = self.create_context.get_current_folder_path() task_name = self.create_context.get_current_task_name() host_name = self.create_context.host_name diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index f745d4ff4b..01ddf52dad 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -44,7 +44,7 @@ from ayon_core.pipeline import ( Anatomy, get_current_host_name, get_current_project_name, - get_current_asset_name, + get_current_folder_path, AYON_INSTANCE_ID, AVALON_INSTANCE_ID, ) @@ -1462,7 +1462,7 @@ class WorkfileSettings(object): Context._project_entity = project_entity self._project_name = project_name - self._asset = get_current_asset_name() + self._asset = get_current_folder_path() self._asset_entity = get_asset_by_name(project_name, self._asset) self._root_node = root_node or nuke.root() self._nodes = self.get_nodes(nodes=nodes) @@ -2108,7 +2108,7 @@ Reopening Nuke should synchronize these paths and resolve any discrepancies. from .utils import set_context_favorites work_dir = os.getenv("AYON_WORKDIR") - asset = get_current_asset_name() + asset = get_current_folder_path() favorite_items = OrderedDict() # project diff --git a/client/ayon_core/hosts/nuke/api/pipeline.py b/client/ayon_core/hosts/nuke/api/pipeline.py index 582df952d3..1189f79a07 100644 --- a/client/ayon_core/hosts/nuke/api/pipeline.py +++ b/client/ayon_core/hosts/nuke/api/pipeline.py @@ -21,7 +21,7 @@ from ayon_core.pipeline import ( AYON_INSTANCE_ID, AVALON_INSTANCE_ID, AVALON_CONTAINER_ID, - get_current_asset_name, + get_current_folder_path, get_current_task_name, registered_host, ) @@ -224,7 +224,7 @@ def _show_workfiles(): def get_context_label(): return "{0}, {1}".format( - get_current_asset_name(), + get_current_folder_path(), get_current_task_name() ) diff --git a/client/ayon_core/hosts/nuke/plugins/create/workfile_creator.py b/client/ayon_core/hosts/nuke/plugins/create/workfile_creator.py index 0a0467787a..de46e02b37 100644 --- a/client/ayon_core/hosts/nuke/plugins/create/workfile_creator.py +++ b/client/ayon_core/hosts/nuke/plugins/create/workfile_creator.py @@ -27,7 +27,7 @@ class WorkfileCreator(AutoCreator): ) project_name = self.create_context.get_current_project_name() - asset_name = self.create_context.get_current_asset_name() + asset_name = self.create_context.get_current_folder_path() task_name = self.create_context.get_current_task_name() host_name = self.create_context.host_name diff --git a/client/ayon_core/hosts/photoshop/lib.py b/client/ayon_core/hosts/photoshop/lib.py index 6d5be48bc2..de3bc2ea6e 100644 --- a/client/ayon_core/hosts/photoshop/lib.py +++ b/client/ayon_core/hosts/photoshop/lib.py @@ -40,7 +40,7 @@ class PSAutoCreator(AutoCreator): context = self.create_context project_name = context.get_current_project_name() - asset_name = context.get_current_asset_name() + asset_name = context.get_current_folder_path() task_name = context.get_current_task_name() host_name = context.host_name diff --git a/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py b/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py index 11bf92d5fb..36b5e1577d 100644 --- a/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py @@ -32,7 +32,7 @@ class AutoImageCreator(PSAutoCreator): context = self.create_context project_name = context.get_current_project_name() - asset_name = context.get_current_asset_name() + asset_name = context.get_current_folder_path() task_name = context.get_current_task_name() host_name = context.host_name asset_doc = get_asset_by_name(project_name, asset_name) diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/validate_instance_asset.py b/client/ayon_core/hosts/photoshop/plugins/publish/validate_instance_asset.py index 67a7303316..937d68eeff 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/validate_instance_asset.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/validate_instance_asset.py @@ -1,6 +1,6 @@ import pyblish.api -from ayon_core.pipeline import get_current_asset_name +from ayon_core.pipeline import get_current_folder_path from ayon_core.pipeline.publish import ( ValidateContentsOrder, PublishXmlValidationError, @@ -28,7 +28,7 @@ class ValidateInstanceAssetRepair(pyblish.api.Action): # Apply pyblish.logic to get the instances for the plug-in instances = pyblish.api.instances_by_plugin(failed, plugin) stub = photoshop.stub() - current_asset_name = get_current_asset_name() + current_asset_name = get_current_folder_path() for instance in instances: data = stub.read(instance[0]) data["folderPath"] = current_asset_name @@ -55,7 +55,7 @@ class ValidateInstanceAsset(OptionalPyblishPluginMixin, def process(self, instance): instance_asset = instance.data["folderPath"] - current_asset = get_current_asset_name() + current_asset = get_current_folder_path() if instance_asset != current_asset: msg = ( diff --git a/client/ayon_core/hosts/resolve/plugins/publish/precollect_workfile.py b/client/ayon_core/hosts/resolve/plugins/publish/precollect_workfile.py index a147c9a905..71c3aa4571 100644 --- a/client/ayon_core/hosts/resolve/plugins/publish/precollect_workfile.py +++ b/client/ayon_core/hosts/resolve/plugins/publish/precollect_workfile.py @@ -1,7 +1,7 @@ import pyblish.api from pprint import pformat -from ayon_core.pipeline import get_current_asset_name +from ayon_core.pipeline import get_current_folder_path from ayon_core.hosts.resolve import api as rapi from ayon_core.hosts.resolve.otio import davinci_export @@ -14,7 +14,7 @@ class PrecollectWorkfile(pyblish.api.ContextPlugin): order = pyblish.api.CollectorOrder - 0.5 def process(self, context): - current_asset_name = get_current_asset_name() + current_asset_name = get_current_folder_path() asset_name = current_asset_name.split("/")[-1] product_name = "workfileMain" diff --git a/client/ayon_core/hosts/substancepainter/api/pipeline.py b/client/ayon_core/hosts/substancepainter/api/pipeline.py index 843c120d8e..a14d398808 100644 --- a/client/ayon_core/hosts/substancepainter/api/pipeline.py +++ b/client/ayon_core/hosts/substancepainter/api/pipeline.py @@ -250,8 +250,8 @@ class SubstanceHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): has_formatting_entries = any("{" in item["value"] for item in shelves) if has_formatting_entries: project_name = self.get_current_project_name() - asset_name = self.get_current_asset_name() - task_name = self.get_current_asset_name() + asset_name = self.get_current_folder_path() + task_name = self.get_current_task_name() project_settings = get_project_settings(project_name) formatting_data = get_template_data_with_names( project_name, asset_name, task_name, project_settings diff --git a/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py b/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py index dc53ccb9ca..28d21d69e6 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py +++ b/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py @@ -1069,7 +1069,7 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): create_context = self.create_context host_name = create_context.host_name project_name = create_context.get_current_project_name() - asset_name = create_context.get_current_asset_name() + asset_name = create_context.get_current_folder_path() task_name = create_context.get_current_task_name() asset_doc = get_asset_by_name(project_name, asset_name) @@ -1118,7 +1118,7 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): create_context = self.create_context host_name = create_context.host_name project_name = create_context.get_current_project_name() - asset_name = create_context.get_current_asset_name() + asset_name = create_context.get_current_folder_path() task_name = create_context.get_current_task_name() existing_name = existing_instance.get("folderPath") diff --git a/client/ayon_core/pipeline/__init__.py b/client/ayon_core/pipeline/__init__.py index 679e9a195e..d5813286da 100644 --- a/client/ayon_core/pipeline/__init__.py +++ b/client/ayon_core/pipeline/__init__.py @@ -94,7 +94,7 @@ from .context_tools import ( get_current_context, get_current_host_name, get_current_project_name, - get_current_asset_name, + get_current_folder_path, get_current_task_name ) install = install_host @@ -195,7 +195,7 @@ __all__ = ( "get_current_context", "get_current_host_name", "get_current_project_name", - "get_current_asset_name", + "get_current_folder_path", "get_current_task_name", # Backwards compatible function names diff --git a/client/ayon_core/pipeline/context_tools.py b/client/ayon_core/pipeline/context_tools.py index 7fa3def33a..93fc329081 100644 --- a/client/ayon_core/pipeline/context_tools.py +++ b/client/ayon_core/pipeline/context_tools.py @@ -385,10 +385,10 @@ def get_current_project_name(): return get_global_context()["project_name"] -def get_current_asset_name(): +def get_current_folder_path(): host = registered_host() if isinstance(host, HostBase): - return host.get_current_asset_name() + return host.get_current_folder_path() return get_global_context()["folder_path"] @@ -439,7 +439,7 @@ def get_current_project_asset(asset_name=None, asset_id=None, fields=None): return get_asset_by_id(project_name, asset_id, fields=fields) if not asset_name: - asset_name = get_current_asset_name() + asset_name = get_current_folder_path() # Skip if is not set even on context if not asset_name: return None diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index 425de4305f..091ae902c6 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -1402,7 +1402,7 @@ class CreateContext: ).format(joined_methods)) self._current_project_name = None - self._current_asset_name = None + self._current_folder_path = None self._current_task_name = None self._current_workfile_path = None @@ -1557,14 +1557,14 @@ class CreateContext: return self._current_project_name - def get_current_asset_name(self): + def get_current_folder_path(self): """Asset name which was used as current context on context reset. Returns: Union[str, None]: Asset name. """ - return self._current_asset_name + return self._current_folder_path def get_current_task_name(self): """Task name which was used as current context on context reset. @@ -1612,7 +1612,7 @@ class CreateContext: ) return ( self._current_project_name != project_name - or self._current_asset_name != asset_name + or self._current_folder_path != asset_name or self._current_task_name != task_name or self._current_workfile_path != workfile_path ) @@ -1718,7 +1718,7 @@ class CreateContext: ) self._current_project_name = project_name - self._current_asset_name = asset_name + self._current_folder_path = asset_name self._current_task_name = task_name self._current_workfile_path = workfile_path diff --git a/client/ayon_core/pipeline/workfile/build_workfile.py b/client/ayon_core/pipeline/workfile/build_workfile.py index 34d8ef0c8f..1f07493f9a 100644 --- a/client/ayon_core/pipeline/workfile/build_workfile.py +++ b/client/ayon_core/pipeline/workfile/build_workfile.py @@ -102,7 +102,7 @@ class BuildWorkfile: from ayon_core.pipeline.context_tools import ( get_current_project_name, - get_current_asset_name, + get_current_folder_path, get_current_task_name, ) @@ -110,7 +110,7 @@ class BuildWorkfile: # Get current asset name and entity project_name = get_current_project_name() - current_folder_path = get_current_asset_name() + current_folder_path = get_current_folder_path() current_asset_doc = get_asset_by_name( project_name, current_folder_path ) diff --git a/client/ayon_core/pipeline/workfile/workfile_template_builder.py b/client/ayon_core/pipeline/workfile/workfile_template_builder.py index c889e0cafb..1b5a848ea2 100644 --- a/client/ayon_core/pipeline/workfile/workfile_template_builder.py +++ b/client/ayon_core/pipeline/workfile/workfile_template_builder.py @@ -128,9 +128,9 @@ class AbstractTemplateBuilder(object): return os.getenv("AYON_PROJECT_NAME") @property - def current_asset_name(self): + def current_folder_path(self): if isinstance(self._host, HostBase): - return self._host.get_current_asset_name() + return self._host.get_current_folder_path() return os.getenv("AYON_FOLDER_PATH") @property @@ -144,7 +144,7 @@ class AbstractTemplateBuilder(object): return self._host.get_current_context() return { "project_name": self.project_name, - "folder_path": self.current_asset_name, + "folder_path": self.current_folder_path, "task_name": self.current_task_name } @@ -158,7 +158,7 @@ class AbstractTemplateBuilder(object): def current_asset_doc(self): if self._current_asset_doc is None: self._current_asset_doc = get_asset_by_name( - self.project_name, self.current_asset_name + self.project_name, self.current_folder_path ) return self._current_asset_doc diff --git a/client/ayon_core/plugins/publish/collect_from_create_context.py b/client/ayon_core/plugins/publish/collect_from_create_context.py index 8218806c4c..36d44def41 100644 --- a/client/ayon_core/plugins/publish/collect_from_create_context.py +++ b/client/ayon_core/plugins/publish/collect_from_create_context.py @@ -53,7 +53,7 @@ class CollectFromCreateContext(pyblish.api.ContextPlugin): context.data.update(create_context.context_data_to_store()) context.data["newPublishing"] = True # Update context data - asset_name = create_context.get_current_asset_name() + asset_name = create_context.get_current_folder_path() task_name = create_context.get_current_task_name() for key, value in ( ("AYON_PROJECT_NAME", project_name), diff --git a/client/ayon_core/tools/creator/window.py b/client/ayon_core/tools/creator/window.py index 7bf65ea510..bda7ebb11e 100644 --- a/client/ayon_core/tools/creator/window.py +++ b/client/ayon_core/tools/creator/window.py @@ -10,7 +10,7 @@ from ayon_core.settings import get_current_project_settings from ayon_core.tools.utils.lib import qt_app_context from ayon_core.pipeline import ( get_current_project_name, - get_current_asset_name, + get_current_folder_path, get_current_task_name, ) from ayon_core.pipeline.create import ( @@ -372,7 +372,7 @@ class CreatorWindow(QtWidgets.QDialog): self.setStyleSheet(style.load_stylesheet()) def refresh(self): - self._folder_path_input.setText(get_current_asset_name()) + self._folder_path_input.setText(get_current_folder_path()) self._creators_model.reset() diff --git a/client/ayon_core/tools/publisher/control.py b/client/ayon_core/tools/publisher/control.py index 712142f662..f6153da47f 100644 --- a/client/ayon_core/tools/publisher/control.py +++ b/client/ayon_core/tools/publisher/control.py @@ -1673,7 +1673,7 @@ class PublisherController(BasePublisherController): Union[str, None]: Folder path or None if folder is not set. """ - return self._create_context.get_current_asset_name() + return self._create_context.get_current_folder_path() @property def current_task_name(self): diff --git a/client/ayon_core/tools/utils/assets_widget.py b/client/ayon_core/tools/utils/assets_widget.py index 2ac30b1ec1..e7036ff8e8 100644 --- a/client/ayon_core/tools/utils/assets_widget.py +++ b/client/ayon_core/tools/utils/assets_widget.py @@ -521,7 +521,7 @@ class _AssetsWidget(QtWidgets.QWidget): self._last_btns_height = None - self._current_asset_name = None + self._current_folder_path = None self.model_selection = {} @@ -536,7 +536,7 @@ class _AssetsWidget(QtWidgets.QWidget): self._model.set_project_name(project_name, refresh) def set_current_asset_name(self, asset_name): - self._current_asset_name = asset_name + self._current_folder_path = asset_name def _create_source_model(self): model = _AssetModel(parent=self) @@ -560,8 +560,8 @@ class _AssetsWidget(QtWidgets.QWidget): def stop_refresh(self): self._model.stop_refresh() - def _get_current_asset_name(self): - return self._current_asset_name + def _get_current_folder_path(self): + return self._current_folder_path def _on_current_asset_click(self): """Trigger change of asset to current context asset. @@ -572,7 +572,7 @@ class _AssetsWidget(QtWidgets.QWidget): self.select_current_asset() def select_current_asset(self): - asset_name = self._get_current_asset_name() + asset_name = self._get_current_folder_path() if asset_name: self.select_asset_by_name(asset_name) diff --git a/client/ayon_core/tools/utils/host_tools.py b/client/ayon_core/tools/utils/host_tools.py index 8841a377cf..1eff746b9e 100644 --- a/client/ayon_core/tools/utils/host_tools.py +++ b/client/ayon_core/tools/utils/host_tools.py @@ -7,12 +7,9 @@ import os import pyblish.api -from ayon_core.host import IWorkfileHost, ILoadHost +from ayon_core.host import ILoadHost from ayon_core.lib import Logger -from ayon_core.pipeline import ( - registered_host, - get_current_asset_name, -) +from ayon_core.pipeline import registered_host from .lib import qt_app_context From 48c64c9cafa5e09edbeb10ec7d47329975a7b07d Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 1 Mar 2024 17:44:01 +0800 Subject: [PATCH 386/573] cosmetic fix --- .../hosts/max/plugins/publish/validate_instance_in_context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py b/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py index cf285bfab6..8104bfb5e3 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py @@ -38,7 +38,7 @@ class ValidateInstanceInContext(pyblish.api.InstancePlugin, context_label = "{} > {}".format(*context) instance_label = "{} > {}".format(folderPath, task) message = ( - "Instance '{}' publishes to different folderPath than current" + "Instance '{}' publishes to different folderPath than current " "context: {}. Current context: {}".format( instance.name, instance_label, context_label ) From 3b455730892304edc299e10dd43eabe14bdbcdd7 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 1 Mar 2024 17:54:43 +0800 Subject: [PATCH 387/573] make the docstring and comment more concise and align with ayon --- .../max/plugins/publish/validate_instance_in_context.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py b/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py index 8104bfb5e3..f116ec5609 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""Validate if instance asset is the same as context asset.""" +"""Validate if instance context is the same as current context.""" import pyblish.api from ayon_core.pipeline.publish import ( RepairAction, @@ -13,10 +13,10 @@ from pymxs import runtime as rt class ValidateInstanceInContext(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): - """Validator to check if instance asset match context asset. + """Validator to check if instance context match current context . When working in per-shot style you always publish data in context of - current asset (shot). This validator checks if this is so. It is optional + current context (shot). This validator checks if this is so. It is optional so it can be disabled when needed. Action on this validator will select invalid instances. @@ -38,7 +38,7 @@ class ValidateInstanceInContext(pyblish.api.InstancePlugin, context_label = "{} > {}".format(*context) instance_label = "{} > {}".format(folderPath, task) message = ( - "Instance '{}' publishes to different folderPath than current " + "Instance '{}' publishes to different context than current " "context: {}. Current context: {}".format( instance.name, instance_label, context_label ) From 7955ab5e88e36d199fbace8b0a490b7c3474a871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Fri, 1 Mar 2024 11:20:41 +0100 Subject: [PATCH 388/573] :bug: path as string --- client/ayon_core/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/cli.py b/client/ayon_core/cli.py index 0b59589ebc..52eadccdd4 100644 --- a/client/ayon_core/cli.py +++ b/client/ayon_core/cli.py @@ -102,7 +102,7 @@ def extractenvironments(output_json_path, project, asset, task, app, envgroup): @main_cli.command() -@click.argument("path", nargs=1) +@click.argument("path", required=True) @click.option("-t", "--targets", help="Targets", default=None, multiple=True) @click.option("-g", "--gui", is_flag=True, From 7ec4f6cc92d910ac2dbb0b1eb0614dc709500ff8 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 1 Mar 2024 18:21:13 +0800 Subject: [PATCH 389/573] cosmetic --- .../hosts/max/plugins/publish/validate_instance_in_context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py b/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py index f116ec5609..6f44356d9f 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py @@ -13,7 +13,7 @@ from pymxs import runtime as rt class ValidateInstanceInContext(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): - """Validator to check if instance context match current context . + """Validator to check if instance context match current context. When working in per-shot style you always publish data in context of current context (shot). This validator checks if this is so. It is optional From 14385cf12aef8aff6f3d2d1ae1dd9a7868ac7591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Fri, 1 Mar 2024 11:26:34 +0100 Subject: [PATCH 390/573] :bug: raise exception instead of deprecation warning --- client/ayon_core/cli_commands.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/ayon_core/cli_commands.py b/client/ayon_core/cli_commands.py index dc11187990..08933a5501 100644 --- a/client/ayon_core/cli_commands.py +++ b/client/ayon_core/cli_commands.py @@ -75,9 +75,7 @@ class Commands: import pyblish.util if not isinstance(path, str): - warnings.warn( - "Passing list of paths is deprecated.", - DeprecationWarning) + raise RuntimeError("Path to JSON must be a string.") # Fix older jobs for src_key, dst_key in ( From 25573cebd5d27e8c915bdb5102b093b3345e4e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Fri, 1 Mar 2024 11:49:04 +0100 Subject: [PATCH 391/573] :recycle: small refactors --- client/ayon_core/cli_commands.py | 4 ++-- client/ayon_core/plugins/publish/collect_rendered_files.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/cli_commands.py b/client/ayon_core/cli_commands.py index 08933a5501..f50ad61622 100644 --- a/client/ayon_core/cli_commands.py +++ b/client/ayon_core/cli_commands.py @@ -112,7 +112,7 @@ class Commands: app_full_name, launch_type=LaunchTypes.farm_publish, ) - os.environ |= env + os.environ.update(env) pyblish.api.register_host("shell") @@ -123,7 +123,7 @@ class Commands: else: pyblish.api.register_target("farm") - os.environ["AYON_PUBLISH_DATA"] = os.pathsep.join(path) + os.environ["AYON_PUBLISH_DATA"] = path os.environ["HEADLESS_PUBLISH"] = 'true' # to use in app lib log.info("Running publish ...") diff --git a/client/ayon_core/plugins/publish/collect_rendered_files.py b/client/ayon_core/plugins/publish/collect_rendered_files.py index 152771da6f..8a60e7619d 100644 --- a/client/ayon_core/plugins/publish/collect_rendered_files.py +++ b/client/ayon_core/plugins/publish/collect_rendered_files.py @@ -74,7 +74,7 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin): required = ["user", "comment", "job", "instances", "version"] - if any(elem not in data.keys() for elem in required): + if any(elem not in data for elem in required): raise ValueError(data_err) if "folderPath" not in data and "asset" not in data: From 1f2b531a908eb4825233296bfa49ccdc3ef84196 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Sat, 2 Mar 2024 00:46:06 +0800 Subject: [PATCH 392/573] tweak on the description comment of publish validation error --- .../max/plugins/publish/validate_instance_in_context.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py b/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py index 6f44356d9f..963a601009 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py @@ -46,11 +46,11 @@ class ValidateInstanceInContext(pyblish.api.InstancePlugin, raise PublishValidationError( message=message, description=( - "## Publishing to a different asset\n" + "## Publishing to a different context data\n" "There are publish instances present which are publishing " - "into a different asset or task than your current context.\n\n" + "into a different folder path or task than your current context.\n\n" "Usually this is not what you want but there can be cases " - "where you might want to publish into another asset or " + "where you might want to publish into another context or " "shot. If that's the case you can disable the validation " "on the instance to ignore it." ) From 5ae84b44dc63db5ba08b64d3da736dcb62279062 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 11:54:19 +0100 Subject: [PATCH 393/573] remove project functions and constants from client --- client/ayon_core/client/constants.py | 2 - client/ayon_core/client/conversion_utils.py | 1 - client/ayon_core/client/operations.py | 42 --------------------- 3 files changed, 45 deletions(-) diff --git a/client/ayon_core/client/constants.py b/client/ayon_core/client/constants.py index 379c0d665f..0d1db74d85 100644 --- a/client/ayon_core/client/constants.py +++ b/client/ayon_core/client/constants.py @@ -17,8 +17,6 @@ REPRESENTATION_FILES_FIELDS = { "files.size", } -CURRENT_PROJECT_SCHEMA = "openpype:project-3.0" -CURRENT_PROJECT_CONFIG_SCHEMA = "openpype:config-2.0" CURRENT_ASSET_DOC_SCHEMA = "openpype:asset-3.0" CURRENT_SUBSET_SCHEMA = "openpype:subset-3.0" CURRENT_VERSION_SCHEMA = "openpype:version-3.0" diff --git a/client/ayon_core/client/conversion_utils.py b/client/ayon_core/client/conversion_utils.py index f9c50b32be..039384a81b 100644 --- a/client/ayon_core/client/conversion_utils.py +++ b/client/ayon_core/client/conversion_utils.py @@ -7,7 +7,6 @@ import six from ayon_core.client.operations_base import REMOVED_VALUE from .constants import ( - CURRENT_PROJECT_SCHEMA, CURRENT_ASSET_DOC_SCHEMA, CURRENT_SUBSET_SCHEMA, CURRENT_VERSION_SCHEMA, diff --git a/client/ayon_core/client/operations.py b/client/ayon_core/client/operations.py index 71b3ca226a..f05652fb50 100644 --- a/client/ayon_core/client/operations.py +++ b/client/ayon_core/client/operations.py @@ -4,14 +4,7 @@ import collections import uuid import datetime -from ayon_api.server_api import ( - PROJECT_NAME_ALLOWED_SYMBOLS, - PROJECT_NAME_REGEX, -) - from .constants import ( - CURRENT_PROJECT_SCHEMA, - CURRENT_PROJECT_CONFIG_SCHEMA, CURRENT_ASSET_DOC_SCHEMA, CURRENT_SUBSET_SCHEMA, CURRENT_VERSION_SCHEMA, @@ -55,41 +48,6 @@ def _create_or_convert_to_id(entity_id=None): return entity_id -def new_project_document( - project_name, project_code, config, data=None, entity_id=None -): - """Create skeleton data of project document. - - Args: - project_name (str): Name of project. Used as identifier of a project. - project_code (str): Shorter version of projet without spaces and - special characters (in most of cases). Should be also considered - as unique name across projects. - config (Dic[str, Any]): Project config consist of roots, templates, - applications and other project Anatomy related data. - data (Dict[str, Any]): Project data with information about it's - attributes (e.g. 'fps' etc.) or integration specific keys. - entity_id (Union[str, ObjectId]): Predefined id of document. New id is - created if not passed. - - Returns: - Dict[str, Any]: Skeleton of project document. - """ - - if data is None: - data = {} - - data["code"] = project_code - - return { - "_id": _create_or_convert_to_id(entity_id), - "name": project_name, - "type": CURRENT_PROJECT_SCHEMA, - "entity_data": data, - "config": config - } - - def new_asset_document( name, project_id, parent_id, parents, data=None, entity_id=None ): From dbbff25fa1678be25bdd506a734f0586f40f7e11 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 12:22:16 +0100 Subject: [PATCH 394/573] change representation context data in load logic --- client/ayon_core/client/entities.py | 16 ++--- client/ayon_core/pipeline/load/utils.py | 83 ++++++++++++------------- 2 files changed, 46 insertions(+), 53 deletions(-) diff --git a/client/ayon_core/client/entities.py b/client/ayon_core/client/entities.py index f9a32e15cd..89fdd01bbc 100644 --- a/client/ayon_core/client/entities.py +++ b/client/ayon_core/client/entities.py @@ -550,23 +550,17 @@ def get_representations_parents(project_name, representations): for repre in representations } con = get_ayon_server_api_connection() - parents_by_repre_id = con.get_representations_parents(project_name, - repre_ids) - folder_ids = set() - for parents in parents_by_repre_id .values(): - folder_ids.add(parents[2]["id"]) - - tasks_by_folder_id = {} + parents_by_repre_id = con.get_representations_parents( + project_name, repre_ids + ) new_parents = {} - for repre_id, parents in parents_by_repre_id .items(): + for repre_id, parents in parents_by_repre_id.items(): version, subset, folder, project = parents - folder_tasks = tasks_by_folder_id.get(folder["id"]) or {} - folder["tasks"] = folder_tasks new_parents[repre_id] = ( convert_v4_version_to_v3(version), convert_v4_subset_to_v3(subset), - convert_v4_folder_to_v3(folder, project_name), + folder, project ) return new_parents diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index 0726a27645..f1b467fff5 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -10,8 +10,6 @@ import ayon_api from ayon_core.host import ILoadHost from ayon_core.client import ( - get_assets, - get_asset_by_id, get_subsets, get_subset_by_id, get_versions, @@ -156,15 +154,16 @@ def get_contexts_for_repre_docs(project_name, repre_docs): subset_docs = get_subsets(project_name, subset_ids) subset_docs_by_id = {} - asset_ids = set() + folder_ids = set() for subset_doc in subset_docs: subset_docs_by_id[subset_doc["_id"]] = subset_doc - asset_ids.add(subset_doc["parent"]) + folder_ids.add(subset_doc["parent"]) - asset_docs = get_assets(project_name, asset_ids) - asset_docs_by_id = { - asset_doc["_id"]: asset_doc - for asset_doc in asset_docs + folder_entities_by_id = { + folder_entity["id"]: folder_entity + for folder_entity in ayon_api.get_folders( + project_name, folder_ids=folder_ids + ) } project_entity = ayon_api.get_project(project_name) @@ -172,10 +171,10 @@ def get_contexts_for_repre_docs(project_name, repre_docs): 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"]] + folder_entity = folder_entities_by_id[subset_doc["parent"]] context = { "project": project_entity, - "asset": asset_doc, + "folder": folder_entity, "subset": subset_doc, "version": version_doc, "representation": repre_doc, @@ -206,24 +205,25 @@ def get_subset_contexts(subset_ids, project_name=None): project_name = get_current_project_name() subset_docs = get_subsets(project_name, subset_ids) subset_docs_by_id = {} - asset_ids = set() + folder_ids = set() for subset_doc in subset_docs: subset_docs_by_id[subset_doc["_id"]] = subset_doc - asset_ids.add(subset_doc["parent"]) + folder_ids.add(subset_doc["parent"]) - asset_docs = get_assets(project_name, asset_ids) - asset_docs_by_id = { - asset_doc["_id"]: asset_doc - for asset_doc in asset_docs + folder_entities_by_id = { + folder_entity["id"]: folder_entity + for folder_entity in ayon_api.get_folders( + project_name, folder_ids=folder_ids + ) } project_entity = ayon_api.get_project(project_name) for subset_id, subset_doc in subset_docs_by_id.items(): - asset_doc = asset_docs_by_id[subset_doc["parent"]] + folder_entity = folder_entities_by_id[subset_doc["parent"]] context = { "project": project_entity, - "asset": asset_doc, + "folder": folder_entity, "subset": subset_doc } contexts[subset_id] = context @@ -254,26 +254,26 @@ def get_representation_context(representation): if not representation: raise AssertionError("Representation was not found in database") - version, subset, asset, project = get_representation_parents( - project_name, representation - ) - if not version: + ( + version_doc, + subset_doc, + folder_entity, + project_entity + ) = get_representation_parents(project_name, representation) + if not version_doc: raise AssertionError("Version was not found in database") - if not subset: + if not subset_doc: raise AssertionError("Subset was not found in database") - if not asset: - raise AssertionError("Asset was not found in database") - if not project: + if not folder_entity: + raise AssertionError("Folder was not found in database") + if not project_entity: raise AssertionError("Project was not found in database") context = { - "project": { - "name": project["name"], - "code": project["data"].get("code", '') - }, - "asset": asset, - "subset": subset, - "version": version, + "project": project_entity, + "folder": folder_entity, + "subset": subset_doc, + "version": version_doc, "representation": representation, } @@ -304,7 +304,7 @@ def load_with_repre_context( log.info( "Running '%s' on '%s'" % ( - Loader.__name__, repre_context["asset"]["name"] + Loader.__name__, repre_context["folder"]["path"] ) ) @@ -334,7 +334,7 @@ def load_with_subset_context( log.info( "Running '%s' on '%s'" % ( - Loader.__name__, subset_context["asset"]["name"] + Loader.__name__, subset_context["folder"]["path"] ) ) @@ -478,7 +478,9 @@ def update_container(container, version=-1): project_name, version, current_version["parent"], fields=["_id"] ) subset_doc = get_subset_by_id(project_name, current_version["parent"]) - asset_doc = get_asset_by_id(project_name, subset_doc["parent"]) + folder_entity = ayon_api.get_folder_by_id( + project_name, subset_doc["parent"] + ) assert new_version is not None, "This is a bug" @@ -497,13 +499,10 @@ def update_container(container, version=-1): "Can't update container because loader '{}' was not found." .format(container.get("loader")) ) - project_doc = get_project(project_name) + project_entity = ayon_api.get_project(project_name) context = { - "project": { - "name": project_doc["name"], - "code": project_doc["data"]["code"], - }, - "asset": asset_doc, + "project": project_entity, + "folder": folder_entity, "subset": subset_doc, "version": new_version, "representation": new_representation, From 1204c5f2c736cf0b16403569bfcf8d1850369cdc Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 4 Mar 2024 21:13:45 +0800 Subject: [PATCH 395/573] tweaks on the debug message --- .../hosts/max/plugins/publish/validate_renderpasses.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py b/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py index 52c4c14367..1e4990ae3a 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py @@ -75,8 +75,8 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, filename, ext = os.path.splitext(file) if filename not in rt.rendOutputFilename: cls.log.error( - "Render output folder " - f"doesn't match the max scene name {filename} " + "Render output folder must include" + f"the max scene name {filename} " ) invalid_folder_name = os.path.dirname( rt.rendOutputFilename).replace( @@ -121,9 +121,8 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, invalid.extend(invalid_image_format) elif renderer == "Arnold": cls.log.debug( - "Renderpass validation not supported Arnold yet," + "Renderpass validation does not support Arnold yet," " validation skipped...") - return invalid @classmethod From e01de841c0490dfd96702256c1cdcfeb2c1dda65 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 4 Mar 2024 21:25:00 +0800 Subject: [PATCH 396/573] missing space --- .../hosts/max/plugins/publish/validate_renderpasses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py b/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py index 1e4990ae3a..4e2298045c 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py @@ -76,7 +76,7 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, if filename not in rt.rendOutputFilename: cls.log.error( "Render output folder must include" - f"the max scene name {filename} " + f" the max scene name {filename} " ) invalid_folder_name = os.path.dirname( rt.rendOutputFilename).replace( From 9d79ec3f0996ca6c479769c7a614ffeafef50c4b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 14:57:07 +0100 Subject: [PATCH 397/573] modified create logic to use folder and task entities --- client/ayon_core/pipeline/create/context.py | 147 +++++++++++------- .../pipeline/create/creator_plugins.py | 29 ++-- .../pipeline/create/legacy_create.py | 34 ++-- .../ayon_core/pipeline/create/product_name.py | 16 +- client/ayon_core/pipeline/create/utils.py | 14 +- 5 files changed, 127 insertions(+), 113 deletions(-) diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index 091ae902c6..46c4d2d1b4 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -10,12 +10,8 @@ from contextlib import contextmanager import pyblish.logic import pyblish.api +import ayon_api -from ayon_core.client import ( - get_assets, - get_asset_by_name, - get_asset_name_identifier, -) from ayon_core.settings import get_project_settings from ayon_core.lib.attribute_definitions import ( UnknownDef, @@ -854,7 +850,7 @@ class CreatedInstance: """Instance entity with data that will be stored to workfile. I think `data` must be required argument containing all minimum information - about instance like "asset" and "task" and all data used for filling + about instance like "folderPath" and "task" and all data used for filling product name as creators may have custom data for product name filling. Notes: @@ -1713,12 +1709,12 @@ class CreateContext: are stored. We should store the workfile (if is available) too. """ - project_name, asset_name, task_name, workfile_path = ( + project_name, folder_path, task_name, workfile_path = ( self._get_current_host_context() ) self._current_project_name = project_name - self._current_folder_path = asset_name + self._current_folder_path = folder_path self._current_task_name = task_name self._current_workfile_path = workfile_path @@ -1950,24 +1946,25 @@ class CreateContext: self, creator_identifier, variant, - asset_doc=None, - task_name=None, + folder_entity=None, + task_entity=None, pre_create_data=None ): """Trigger create of plugins with standartized arguments. - Arguments 'asset_doc' and 'task_name' use current context as default - values. If only 'task_name' is provided it will be overriden by - task name from current context. If 'task_name' is not provided - when 'asset_doc' is, it is considered that task name is not specified, - which can lead to error if product name template requires task name. + Arguments 'folder_entity' and 'task_name' use current context as + default values. If only 'task_name' is provided it will be overriden + by task name from current context. If 'task_name' is not provided + when 'folder_entity' is, it is considered that task name is not + specified, which can lead to error if product name template requires + task name. Args: creator_identifier (str): Identifier of creator plugin. variant (str): Variant used for product name. - asset_doc (Dict[str, Any]): Asset document which define context of - creation (possible context of created instance/s). - task_name (str): Name of task to which is context related. + folder_entity (Dict[str, Any]): Folder entity which define context + of creation (possible context of created instance/s). + task_entity (Dict[str, Any]): Task entity. pre_create_data (Dict[str, Any]): Pre-create attribute values. Returns: @@ -1980,14 +1977,20 @@ class CreateContext: creator = self._get_creator_in_create(creator_identifier) project_name = self.project_name - if asset_doc is None: - asset_name = self.get_current_asset_name() - asset_doc = get_asset_by_name(project_name, asset_name) - task_name = self.get_current_task_name() - if asset_doc is None: + if folder_entity is None: + folder_path = self.get_current_folder_path() + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + if folder_entity is None: raise CreatorError( - "Asset with name {} was not found".format(asset_name) + "Folder '{}' was not found".format(folder_path) ) + if task_entity is None: + task_name = self.get_current_task_name() + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) if pre_create_data is None: pre_create_data = {} @@ -2005,15 +2008,14 @@ class CreateContext: product_name = creator.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, self.host_name, ) - asset_name = get_asset_name_identifier(asset_doc) instance_data = { - "folderPath": asset_name, + "folderPath": folder_entity["path"], "task": task_name, "productType": creator.product_type, "variant": variant @@ -2235,46 +2237,75 @@ class CreateContext: if not instances: return - task_names_by_asset_name = {} + project_name = self.project_name + + task_names_by_folder_path = {} for instance in instances: - asset_name = instance.get("folderPath") + folder_path = instance.get("folderPath") task_name = instance.get("task") - if asset_name: - task_names_by_asset_name[asset_name] = set() + if folder_path: + task_names_by_folder_path[folder_path] = set() if task_name: - task_names_by_asset_name[asset_name].add(task_name) + task_names_by_folder_path[folder_path].add(task_name) - asset_names = { - asset_name - for asset_name in task_names_by_asset_name.keys() - if asset_name is not None - } - asset_docs = list(get_assets( - self.project_name, - asset_names=asset_names, - fields={"name", "data.tasks", "data.parents"} - )) + # Backwards compatibility for cases where folder name is set instead + # of folder path + folder_names = set() + folder_paths = set() + for folder_path in task_names_by_folder_path.keys(): + if folder_path is None: + pass + elif "/" in folder_path: + folder_paths.add(folder_path) + else: + folder_names.add(folder_path) - task_names_by_asset_name = {} - asset_docs_by_name = collections.defaultdict(list) - for asset_doc in asset_docs: - asset_name = get_asset_name_identifier(asset_doc) - tasks = asset_doc.get("data", {}).get("tasks") or {} - task_names_by_asset_name[asset_name] = set(tasks.keys()) - asset_docs_by_name[asset_doc["name"]].append(asset_doc) + folder_paths_by_id = {} + if folder_paths: + for folder_entity in ayon_api.get_folders( + project_name, + folder_paths=folder_paths, + fields={"id", "path"} + ): + folder_id = folder_entity["id"] + folder_paths_by_id[folder_id] = folder_entity["path"] + + folder_entities_by_name = collections.defaultdict(list) + if folder_names: + for folder_entity in ayon_api.get_folders( + project_name, + folder_names=folder_names, + fields={"id", "name", "path"} + ): + folder_id = folder_entity["id"] + folder_name = folder_entity["name"] + folder_paths_by_id[folder_id] = folder_entity["path"] + folder_entities_by_name[folder_name].append(folder_entity) + + tasks_entities = ayon_api.get_tasks( + project_name, + folder_ids=folder_paths_by_id.keys(), + fields={"name", "folderId"} + ) + + task_names_by_folder_path = collections.defaultdict(set) + for task_entity in tasks_entities: + folder_id = task_entity["folderId"] + folder_path = folder_paths_by_id[folder_id] + task_names_by_folder_path[folder_path].add(task_entity["name"]) for instance in instances: if not instance.has_valid_asset or not instance.has_valid_task: continue - asset_name = instance["folderPath"] - if asset_name and "/" not in asset_name: - asset_docs = asset_docs_by_name.get(asset_name) - if len(asset_docs) == 1: - asset_name = get_asset_name_identifier(asset_docs[0]) - instance["folderPath"] = asset_name + folder_path = instance["folderPath"] + if folder_path and "/" not in folder_path: + folder_entities = folder_entities_by_name.get(folder_path) + if len(folder_entities) == 1: + folder_path = folder_entities[0]["path"] + instance["folderPath"] = folder_path - if asset_name not in task_names_by_asset_name: + if folder_path not in task_names_by_folder_path: instance.set_asset_invalid(True) continue @@ -2282,7 +2313,7 @@ class CreateContext: if not task_name: continue - if task_name not in task_names_by_asset_name[asset_name]: + if task_name not in task_names_by_folder_path[folder_path]: instance.set_task_invalid(True) def save_changes(self): diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index cb8e4a2d1c..2c184b5ba5 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -472,8 +472,8 @@ class BaseCreator: def get_dynamic_data( self, project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, host_name, instance @@ -489,31 +489,21 @@ class BaseCreator: def get_product_name( self, project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, host_name=None, instance=None ): """Return product name for passed context. - CHANGES: - Argument `asset_id` was replaced with `asset_doc`. It is easier to - query asset before. In some cases would this method be called multiple - times and it would be too slow to query asset document on each - callback. - - NOTE: - Asset document is not used yet but is required if would like to use - task type in product templates. - Method is also called on product name update. In that case origin instance is passed in. Args: project_name (str): Project name. - asset_doc (dict): Asset document for which product is created. - task_name (str): For which task product is created. + folder_entity (dict): Folder entity. + task_entity (dict): Task entity. variant (str): Product name variant. In most of cases user input. host_name (Optional[str]): Which host creates product. Defaults to host name on create context. @@ -526,8 +516,8 @@ class BaseCreator: host_name = self.create_context.host_name dynamic_data = self.get_dynamic_data( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, host_name, instance @@ -535,8 +525,7 @@ class BaseCreator: return get_product_name( project_name, - asset_doc, - task_name, + task_entity, host_name, self.product_type, variant, diff --git a/client/ayon_core/pipeline/create/legacy_create.py b/client/ayon_core/pipeline/create/legacy_create.py index 5e23a74a79..018e958329 100644 --- a/client/ayon_core/pipeline/create/legacy_create.py +++ b/client/ayon_core/pipeline/create/legacy_create.py @@ -9,7 +9,6 @@ import os import logging import collections -from ayon_core.client import get_asset_by_id from ayon_core.pipeline.constants import AVALON_INSTANCE_ID from .product_name import get_product_name @@ -89,7 +88,7 @@ class LegacyCreator(object): @classmethod def get_dynamic_data( - cls, project_name, asset_id, task_name, variant, host_name + cls, project_name, folder_entity, task_entity, variant, host_name ): """Return dynamic data for current Creator plugin. @@ -124,7 +123,7 @@ class LegacyCreator(object): @classmethod def get_product_name( - cls, project_name, asset_id, task_name, variant, host_name=None + cls, project_name, folder_entity, task_entity, variant, host_name=None ): """Return product name created with entered arguments. @@ -137,8 +136,8 @@ class LegacyCreator(object): Args: project_name (str): Context's project name. - asset_id (str): Folder id. - task_name (str): Context's task name. + folder_entity (dict[str, Any]): Folder entity. + task_entity (dict[str, Any]): Task entity. variant (str): What is entered by user in creator tool. host_name (str): Name of host. @@ -148,17 +147,12 @@ class LegacyCreator(object): """ dynamic_data = cls.get_dynamic_data( - project_name, asset_id, task_name, variant, host_name - ) - - asset_doc = get_asset_by_id( - project_name, asset_id, fields=["data.tasks"] + project_name, folder_entity, task_entity, variant, host_name ) return get_product_name( project_name, - asset_doc, - task_name, + task_entity, host_name, cls.product_type, variant, @@ -166,7 +160,9 @@ class LegacyCreator(object): ) -def legacy_create(Creator, name, asset, options=None, data=None): +def legacy_create( + Creator, product_name, folder_path, options=None, data=None +): """Create a new instance Associate nodes with a product name and type. These nodes are later @@ -178,11 +174,11 @@ def legacy_create(Creator, name, asset, options=None, data=None): and finally asset browsers to help identify the origin of the asset. Arguments: - Creator (Creator): Class of creator - name (str): Name of product - asset (str): Name of asset - options (dict, optional): Additional options from GUI - data (dict, optional): Additional data from GUI + Creator (Creator): Class of creator. + product_name (str): Name of product. + folder_path (str): Folder path. + options (dict, optional): Additional options from GUI. + data (dict, optional): Additional data from GUI. Raises: NameError on `productName` already exists @@ -196,7 +192,7 @@ def legacy_create(Creator, name, asset, options=None, data=None): from ayon_core.pipeline import registered_host host = registered_host() - plugin = Creator(name, asset, options, data) + plugin = Creator(product_name, folder_path, options, data) if plugin.maintain_selection is True: with host.maintained_selection(): diff --git a/client/ayon_core/pipeline/create/product_name.py b/client/ayon_core/pipeline/create/product_name.py index 8413bfa9d8..7eaa338c91 100644 --- a/client/ayon_core/pipeline/create/product_name.py +++ b/client/ayon_core/pipeline/create/product_name.py @@ -1,4 +1,4 @@ -import os +import ayon_api from ayon_core.settings import get_project_settings from ayon_core.lib import filter_profiles, prepare_template_data @@ -81,8 +81,7 @@ def get_product_name_template( def get_product_name( project_name, - asset_doc, - task_name, + task_entity, host_name, product_type, variant, @@ -107,12 +106,10 @@ def get_product_name( Args: project_name (str): Project name. + task_entity (Optional[Dict[str, Any]]): Task entity. host_name (str): Host name. product_type (str): Product type. variant (str): In most of the cases it is user input during creation. - task_name (str): Task name on which context is instance created. - asset_doc (dict): Queried asset document with its tasks in data. - Used to get task type. default_template (Optional[str]): Default template if any profile does not match passed context. Constant 'DEFAULT_PRODUCT_TEMPLATE' is used if is not passed. @@ -132,9 +129,10 @@ def get_product_name( if not product_type: return "" - asset_tasks = asset_doc.get("data", {}).get("tasks") or {} - task_info = asset_tasks.get(task_name) or {} - task_type = task_info.get("type") + task_name = task_type = None + if task_entity: + task_name = task_entity["name"] + task_type = task_entity["taskType"] template = get_product_name_template( project_name, diff --git a/client/ayon_core/pipeline/create/utils.py b/client/ayon_core/pipeline/create/utils.py index 44063bd9ac..223a30997c 100644 --- a/client/ayon_core/pipeline/create/utils.py +++ b/client/ayon_core/pipeline/create/utils.py @@ -1,10 +1,10 @@ import collections +import ayon_api + from ayon_core.client import ( - get_assets, get_subsets, get_last_versions, - get_asset_name_identifier, ) @@ -54,14 +54,14 @@ def get_last_versions_for_instances( if not product_names: return output - asset_docs = get_assets( + folder_entities = ayon_api.get_folders( project_name, - asset_names=product_names_by_folder_path.keys(), - fields=["name", "_id", "data.parents"] + folder_paths=product_names_by_folder_path.keys(), + fields={"id", "path"} ) folder_paths_by_id = { - asset_doc["_id"]: get_asset_name_identifier(asset_doc) - for asset_doc in asset_docs + folder_entity["id"]: folder_entity["path"] + for folder_entity in folder_entities } if not folder_paths_by_id: return output From 4fade180eb263002949369631813b9c63794cac4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 15:08:49 +0100 Subject: [PATCH 398/573] modified how template data are prepared --- client/ayon_core/pipeline/template_data.py | 138 ++++++++++----------- 1 file changed, 64 insertions(+), 74 deletions(-) diff --git a/client/ayon_core/pipeline/template_data.py b/client/ayon_core/pipeline/template_data.py index 8ecba828a6..526c7d35c5 100644 --- a/client/ayon_core/pipeline/template_data.py +++ b/client/ayon_core/pipeline/template_data.py @@ -1,5 +1,5 @@ import ayon_api -from ayon_core.client import get_asset_by_name + from ayon_core.settings import get_studio_settings from ayon_core.lib.local_settings import get_ayon_username @@ -31,8 +31,7 @@ def get_general_template_data(settings=None): def get_project_template_data(project_entity=None, project_name=None): """Extract data from project document that are used in templates. - Project document must have 'name' and (at this moment) optional - key 'data.code'. + Project document must have 'name' and 'code'. One of 'project_name' or 'project_entity' must be passed. With prepared project document is function much faster because don't have to query. @@ -52,7 +51,7 @@ def get_project_template_data(project_entity=None, project_name=None): if not project_name: project_name = project_entity["name"] - if not project_entity: + elif not project_entity: project_entity = ayon_api.get_project(project_name, fields=["code"]) project_code = project_entity["code"] @@ -64,87 +63,74 @@ def get_project_template_data(project_entity=None, project_name=None): } -def get_asset_template_data(asset_doc, project_name): - """Extract data from asset document that are used in templates. +def get_folder_template_data(folder_entity, project_name): + """Extract data from folder entity that are used in templates. Output dictionary contains keys: - - 'asset' - asset name - - 'hierarchy' - parent asset names joined with '/' - - 'parent' - direct parent name, project name used if is under project + - 'folder' - dictionary with 'name' key filled with folder name + - 'asset' - folder name + - 'hierarchy' - parent folder names joined with '/' + - 'parent' - direct parent name, project name used if is under + project Required document fields: - Asset: 'name', 'data.parents' + Folder: 'path' -> Plan to require: 'folderType' Args: - asset_doc (Dict[str, Any]): Queried asset document. - project_name (str): Is used for 'parent' key if asset doc does not have - any. + folder_entity (Dict[str, Any]): Folder entity. + project_name (str): Is used for 'parent' key if folder entity + does not have any. Returns: - Dict[str, str]: Data that are based on asset document and can be used + Dict[str, str]: Data that are based on folder entity and can be used in templates. """ - asset_parents = asset_doc["data"]["parents"] - hierarchy = "/".join(asset_parents) - if asset_parents: - parent_name = asset_parents[-1] + path = folder_entity["path"] + hierarchy_parts = path.split("/") + # Remove empty string from the beginning + hierarchy_parts.pop(0) + # Remove last part which is folder name + folder_name = hierarchy_parts.pop(-1) + hierarchy = "/".join(hierarchy_parts) + if hierarchy_parts: + parent_name = hierarchy_parts[-1] else: parent_name = project_name return { - "asset": asset_doc["name"], "folder": { - "name": asset_doc["name"] + "name": folder_name, }, + "asset": folder_name, "hierarchy": hierarchy, "parent": parent_name } -def get_task_type(asset_doc, task_name): - """Get task type based on asset document and task name. - - Required document fields: - Asset: 'data.tasks' - - Args: - asset_doc (Dict[str, Any]): Queried asset document. - task_name (str): Task name which is under asset. - - Returns: - str: Task type name. - None: Task was not found on asset document. - """ - - asset_tasks_info = asset_doc["data"]["tasks"] - return asset_tasks_info.get(task_name, {}).get("type") - - -def get_task_template_data(project_entity, asset_doc, task_name): - """"Extract task specific data from project and asset documents. +def get_task_template_data(project_entity, task_entity): + """Prepare task template data. Required document fields: Project: 'tasksTypes' - Asset: 'data.tasks'. + Task: 'type' Args: - project_entity (Dict[str, Any]): Queried project entity. - asset_doc (Dict[str, Any]): Queried asset document. - task_name (str): Name of task for which data should be returned. + project_entity (Dict[str, Any]): Project entity. + task_entity (Dict[str, Any]): Task entity. Returns: Dict[str, Dict[str, str]]: Template data - """ + """ project_task_types = project_entity["taskTypes"] task_types_by_name = {task["name"]: task for task in project_task_types} - task_type = get_task_type(asset_doc, task_name) + task_type = task_entity["taskType"] task_code = task_types_by_name.get(task_type, {}).get("shortName") return { "task": { - "name": task_name, + "name": task_entity["name"], "type": task_type, "short": task_code, } @@ -153,10 +139,10 @@ def get_task_template_data(project_entity, asset_doc, task_name): def get_template_data( project_entity, - asset_doc=None, - task_name=None, + folder_entity=None, + task_entity=None, host_name=None, - settings=None + settings=None, ): """Prepare data for templates filling from entered documents and info. @@ -169,13 +155,14 @@ def get_template_data( Required document fields: Project: 'name', 'code', 'taskTypes.name' - Asset: 'name', 'data.parents', 'data.tasks' + Folder: 'name', 'path' + Task: 'type' Args: project_entity (Dict[str, Any]): Project entity. - asset_doc (Dict[str, Any]): Mongo document of asset from MongoDB. - task_name (Union[str, None]): Task name under passed asset. - host_name (Union[str, None]): Used to fill '{app}' key. + folder_entity (Optional[Dict[str, Any]]): Folder entity. + task_entity (Optional[Dict[str, Any]): Task entity. + host_name (Optional[str]): Used to fill '{app}' key. settings (Union[Dict, None]): Prepared studio or project settings. They're queried if not passed (may be slower). @@ -185,13 +172,13 @@ def get_template_data( template_data = get_general_template_data(settings) template_data.update(get_project_template_data(project_entity)) - if asset_doc: - template_data.update(get_asset_template_data( - asset_doc, project_entity["name"] + if folder_entity: + template_data.update(get_folder_template_data( + folder_entity, project_entity["name"] )) - if task_name: + if task_entity: template_data.update(get_task_template_data( - project_entity, asset_doc, task_name + project_entity, task_entity )) if host_name: @@ -202,7 +189,7 @@ def get_template_data( def get_template_data_with_names( project_name, - asset_name=None, + folder_path=None, task_name=None, host_name=None, settings=None @@ -213,14 +200,12 @@ def get_template_data_with_names( Only difference is that documents are queried. Args: - project_name (str): Project name for which template data are - calculated. - asset_name (Union[str, None]): Asset name for which template data are - calculated. - task_name (Union[str, None]): Task name under passed asset. - host_name (Union[str, None]):Used to fill '{app}' key. + project_name (str): Project name. + folder_path (Optional[str]): Folder path. + task_name (Optional[str]): Task name. + host_name (Optional[str]):Used to fill '{app}' key. because workdir template may contain `{app}` key. - settings (Union[Dict, None]): Prepared studio or project settings. + settings (Optional[Dict]): Prepared studio or project settings. They're queried if not passed. Returns: @@ -228,13 +213,18 @@ def get_template_data_with_names( """ project_entity = ayon_api.get_project(project_name) - asset_doc = None - if asset_name: - asset_doc = get_asset_by_name( + folder_entity = None + task_entity = None + if folder_path: + folder_entity = ayon_api.get_folder_by_path( project_name, - asset_name, - fields=["name", "data.parents", "data.tasks"] + folder_path, + fields={"id", "path", "folderType"} ) + if task_name and folder_entity: + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) return get_template_data( - project_entity, asset_doc, task_name, host_name, settings + project_entity, folder_entity, task_entity, host_name, settings ) From 02eef200a18cacc40e394e6e53a3040fc57e786e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 15:09:13 +0100 Subject: [PATCH 399/573] change how current context can be changed --- client/ayon_core/pipeline/context_tools.py | 89 ++++++++++++---------- 1 file changed, 48 insertions(+), 41 deletions(-) diff --git a/client/ayon_core/pipeline/context_tools.py b/client/ayon_core/pipeline/context_tools.py index 93fc329081..bda994490d 100644 --- a/client/ayon_core/pipeline/context_tools.py +++ b/client/ayon_core/pipeline/context_tools.py @@ -13,10 +13,7 @@ from pyblish.lib import MessageHandler from ayon_core import AYON_CORE_ROOT from ayon_core.host import HostBase from ayon_core.client import ( - get_asset_by_id, - get_asset_by_name, version_is_latest, - get_asset_name_identifier, get_ayon_server_api_connection, ) from ayon_core.lib import is_in_tests @@ -404,46 +401,52 @@ def get_current_project_entity(fields=None): This function should be called only in process where host is installed. - Returns: - dict: Project document. - None: Project is not set. - """ + Args: + fields (Optional[Iterable[str]]): Limit returned data of project + entity. + Returns: + Union[dict[str, Any], None]: Project entity of current project or None. + + """ project_name = get_current_project_name() return ayon_api.get_project(project_name, fields=fields) -def get_current_project_asset(asset_name=None, asset_id=None, fields=None): - """Helper function to get asset document based on global Session. +def get_current_project_folder(folder_path=None, folder_id=None, fields=None): + """Helper function to get folder entity based on current context. This function should be called only in process where host is installed. - Asset is found out based on passed asset name or id (not both). Asset name - is not used for filtering if asset id is passed. When both asset name and - id are missing then asset name from current process is used. + Folder is found out based on passed folder path or id (not both). Folder + path is not used for filtering if folder id is passed. When both + folder path and id are missing then current folder path is used. Args: - asset_name (str): Name of asset used for filter. - asset_id (Union[str, ObjectId]): Asset document id. If entered then + folder_path (Union[str, None]): Folder path used for filter. + folder_id (Union[str, None]): Folder id. If entered then is used as only filter. - fields (Union[List[str], None]): Limit returned data of asset documents + fields (Optional[Iterable[str]]): Limit returned data of folder entity to specific keys. Returns: - dict: Asset document. - None: Asset is not set or not exist. + Union[dict[str, Any], None]: Fodler entity or None. """ project_name = get_current_project_name() - if asset_id: - return get_asset_by_id(project_name, asset_id, fields=fields) + if folder_id: + return ayon_api.get_folder_by_id( + project_name, folder_id, fields=fields + ) - if not asset_name: - asset_name = get_current_folder_path() + if not folder_path: + folder_path = get_current_folder_path() # Skip if is not set even on context - if not asset_name: + if not folder_path: return None - return get_asset_by_name(project_name, asset_name, fields=fields) + return ayon_api.get_folder_by_path( + project_name, folder_path, fields=fields + ) def is_representation_from_latest(representation): @@ -475,18 +478,18 @@ def get_template_data_from_session(session=None, settings=None): if session is not None: project_name = session["AYON_PROJECT_NAME"] - asset_name = session["AYON_FOLDER_PATH"] + folder_path = session["AYON_FOLDER_PATH"] task_name = session["AYON_TASK_NAME"] host_name = session["AYON_HOST_NAME"] else: context = get_current_context() project_name = context["project_name"] - asset_name = context["folder_path"] + folder_path = context["folder_path"] task_name = context["task_name"] host_name = get_current_host_name() return get_template_data_with_names( - project_name, asset_name, task_name, host_name, settings + project_name, folder_path, task_name, host_name, settings ) @@ -503,12 +506,12 @@ def get_current_context_template_data(settings=None): context = get_current_context() project_name = context["project_name"] - asset_name = context["folder_path"] + folder_path = context["folder_path"] task_name = context["task_name"] host_name = get_current_host_name() return get_template_data_with_names( - project_name, asset_name, task_name, host_name, settings + project_name, folder_path, task_name, host_name, settings ) @@ -536,9 +539,9 @@ def get_workdir_from_session(session=None, template_key=None): if not template_key: task_type = template_data["task"]["type"] template_key = get_workfile_template_key( + project_name, task_type, host_name, - project_name=project_name ) anatomy = Anatomy(project_name) @@ -568,33 +571,33 @@ def get_custom_workfile_template_from_session( if session is not None: project_name = session["AYON_PROJECT_NAME"] - asset_name = session["AYON_FOLDER_PATH"] + folder_path = session["AYON_FOLDER_PATH"] task_name = session["AYON_TASK_NAME"] host_name = session["AYON_HOST_NAME"] else: context = get_current_context() project_name = context["project_name"] - asset_name = context["folder_path"] + folder_path = context["folder_path"] task_name = context["task_name"] host_name = get_current_host_name() return get_custom_workfile_template_by_string_context( project_name, - asset_name, + folder_path, task_name, host_name, project_settings=project_settings ) -def change_current_context(asset_doc, task_name, template_key=None): +def change_current_context(folder_entity, task_entity, template_key=None): """Update active Session to a new task work area. - This updates the live Session to a different task under asset. + This updates the live Session to a different task under folder. Args: - asset_doc (Dict[str, Any]): The asset document to set. - task_name (str): The task to set under asset. + folder_entity (Dict[str, Any]): Folder entity to set. + task_entity (Dict[str, Any]): Task entity to set. template_key (Union[str, None]): Prepared template key to be used for workfile template in Anatomy. @@ -604,18 +607,22 @@ def change_current_context(asset_doc, task_name, template_key=None): project_name = get_current_project_name() workdir = None - if asset_doc: + folder_path = None + task_name = None + if folder_entity: + folder_path = folder_entity["path"] + if task_entity: + task_name = task_entity["name"] project_entity = ayon_api.get_project(project_name) host_name = get_current_host_name() workdir = get_workdir( project_entity, - asset_doc, - task_name, + folder_entity, + task_entity, host_name, template_key=template_key ) - folder_path = get_asset_name_identifier(asset_doc) envs = { "AYON_PROJECT_NAME": project_name, "AYON_FOLDER_PATH": folder_path, @@ -635,7 +642,7 @@ def change_current_context(asset_doc, task_name, template_key=None): # Convert env keys to human readable keys data["project_name"] = project_name - data["folder_path"] = get_asset_name_identifier(asset_doc) + data["folder_path"] = folder_path data["task_name"] = task_name data["workdir_path"] = workdir From e25856eb03582bc9a4286d0368a3d296135cfe3e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 15:09:47 +0100 Subject: [PATCH 400/573] modified workfile utils to use folder and task entity --- .../pipeline/workfile/build_workfile.py | 193 ++++++++++-------- .../pipeline/workfile/path_resolving.py | 91 +++++---- .../workfile/workfile_template_builder.py | 163 ++++++++------- 3 files changed, 254 insertions(+), 193 deletions(-) diff --git a/client/ayon_core/pipeline/workfile/build_workfile.py b/client/ayon_core/pipeline/workfile/build_workfile.py index 1f07493f9a..50273dce9e 100644 --- a/client/ayon_core/pipeline/workfile/build_workfile.py +++ b/client/ayon_core/pipeline/workfile/build_workfile.py @@ -13,12 +13,12 @@ import re import collections import json +import ayon_api + from ayon_core.client import ( - get_asset_by_name, get_subsets, get_last_versions, get_representations, - get_linked_assets, ) from ayon_core.settings import get_project_settings from ayon_core.lib import ( @@ -76,21 +76,21 @@ class BuildWorkfile: def build_workfile(self): """Prepares and load containers into workfile. - Loads latest versions of current and linked assets to workfile by logic - stored in Workfile profiles from presets. Profiles are set by host, - filtered by current task name and used by families. + Loads latest versions of current and linked folders to workfile by + logic stored in Workfile profiles from presets. Profiles are set + by host, filtered by current task name and used by families. Each product type can specify representation names and loaders for representations and first available and successful loaded representation is returned as container. - At the end you'll get list of loaded containers per each asset. + At the end you'll get list of loaded containers per each folder. loaded_containers [{ - "asset_doc": , + "folder_entity": , "containers": [, , ...] }, { - "asset_doc": , + "folder_entity": , "containers": [, ...] }, { ... @@ -100,22 +100,21 @@ class BuildWorkfile: List[Dict[str, Any]]: Loaded containers during build. """ - from ayon_core.pipeline.context_tools import ( - get_current_project_name, - get_current_folder_path, - get_current_task_name, - ) + from ayon_core.pipeline.context_tools import get_current_context loaded_containers = [] - # Get current asset name and entity - project_name = get_current_project_name() - current_folder_path = get_current_folder_path() - current_asset_doc = get_asset_by_name( + # Get current folder and task entities + context = get_current_context() + project_name = context["project_name"] + current_folder_path = context["folder_path"] + current_task_name = context["task_name"] + + current_folder_entity = ayon_api.get_folder_by_path( project_name, current_folder_path ) - # Skip if asset was not found - if not current_asset_doc: + # Skip if folder was not found + if not current_folder_entity: print("Folder entity `{}` was not found".format( current_folder_path )) @@ -138,12 +137,9 @@ class BuildWorkfile: self.log.warning("There are no registered loaders.") return loaded_containers - # Get current task name - current_task_name = get_current_task_name() - # Load workfile presets for task self.build_presets = self.get_build_presets( - current_task_name, current_asset_doc + current_task_name, current_folder_entity["id"] ) # Skip if there are any presets for task @@ -180,45 +176,53 @@ class BuildWorkfile: "loading preset for it's linked folders." ).format(current_task_name)) - # Prepare assets to process by workfile presets - asset_docs = [] + # Prepare folders to process by workfile presets + folder_entities = [] current_folder_id = None if current_context_profiles: - # Add current asset entity if preset has current context set - asset_docs.append(current_asset_doc) - current_folder_id = current_asset_doc["_id"] + # Add current folder entity if preset has current context set + folder_entities.append(current_folder_entity) + current_folder_id = current_folder_entity["_id"] if link_context_profiles: - # Find and append linked assets if preset has set linked mapping - link_assets = get_linked_assets(project_name, current_asset_doc) - if link_assets: - asset_docs.extend(link_assets) + # Find and append linked folders if preset has set linked mapping + linked_folder_entities = self._get_linked_folder_entities( + project_name, current_folder_entity["id"] + ) + if linked_folder_entities: + folder_entities.extend(linked_folder_entities) - # Skip if there are no assets. This can happen if only linked mapping - # is set and there are no links for his asset. - if not asset_docs: + # Skip if there are no folders. This can happen if only linked mapping + # is set and there are no links for his folder. + if not folder_entities: self.log.warning( - "Asset does not have linked assets. Nothing to process." + "Folder does not have linked folders. Nothing to process." ) return loaded_containers - # Prepare entities from database for assets - prepared_entities = self._collect_last_version_repres(asset_docs) + # Prepare entities from database for folders + prepared_entities = self._collect_last_version_repres( + folder_entities + ) # Load containers by prepared entities and presets - # - Current asset containers + # - Current folder containers if current_folder_id and current_folder_id in prepared_entities: current_context_data = prepared_entities.pop(current_folder_id) - loaded_data = self.load_containers_by_asset_data( - current_context_data, current_context_profiles, loaders_by_name + loaded_data = self.load_containers_by_folder_data( + current_context_data, + current_context_profiles, + loaders_by_name ) if loaded_data: loaded_containers.append(loaded_data) # - Linked assets container - for linked_asset_data in prepared_entities.values(): - loaded_data = self.load_containers_by_asset_data( - linked_asset_data, link_context_profiles, loaders_by_name + for linked_folder_data in prepared_entities.values(): + loaded_data = self.load_containers_by_folder_data( + linked_folder_data, + link_context_profiles, + loaders_by_name ) if loaded_data: loaded_containers.append(loaded_data) @@ -226,7 +230,7 @@ class BuildWorkfile: # Return list of loaded containers return loaded_containers - def get_build_presets(self, task_name, asset_doc): + def get_build_presets(self, task_name, folder_id): """ Returns presets to build workfile for task name. Presets are loaded for current project received by @@ -235,6 +239,7 @@ class BuildWorkfile: Args: task_name (str): Task name used for filtering build presets. + folder_id (str): Folder id. Returns: Dict[str, Any]: preset per entered task name @@ -245,10 +250,9 @@ class BuildWorkfile: get_current_project_name, ) + project_name = get_current_project_name() host_name = get_current_host_name() - project_settings = get_project_settings( - get_current_project_name() - ) + project_settings = get_project_settings(project_name) host_settings = project_settings.get(host_name) or {} # Get presets for host @@ -261,13 +265,15 @@ class BuildWorkfile: if not builder_profiles: return None - task_type = ( - asset_doc - .get("data", {}) - .get("tasks", {}) - .get(task_name, {}) - .get("type") + task_entity = ayon_api.get_task_by_name( + project_name, + folder_id, + task_name, ) + task_type = None + if task_entity: + task_type = task_entity["taskType"] + filter_data = { "task_types": task_type, "tasks": task_name @@ -349,6 +355,31 @@ class BuildWorkfile: return valid_profiles + def _get_linked_folder_entities(self, project_name, folder_id): + """Get linked folder entities for entered folder. + + Args: + project_name (str): Project name. + folder_id (str): Folder id. + + Returns: + list[dict[str, Any]]: Linked folder entities. + + """ + links = ayon_api.get_folder_links( + project_name, folder_id, link_direction="in" + ) + linked_folder_ids = { + link["entityId"] + for link in links + if link["entityType"] == "folder" + } + if not linked_folder_ids: + return [] + return list(ayon_api.get_folders( + project_name, folder_ids=linked_folder_ids + )) + def _prepare_profile_for_products(self, subset_docs, profiles): """Select profile for each product by it's data. @@ -405,28 +436,28 @@ class BuildWorkfile: break return profiles_by_product_id - def load_containers_by_asset_data( - self, asset_doc_data, build_profiles, loaders_by_name + def load_containers_by_folder_data( + self, linked_folder_data, build_profiles, loaders_by_name ): - """Load containers for entered asset entity by Build profiles. + """Load containers for entered folder entity by Build profiles. Args: - asset_doc_data (Dict[str, Any]): Prepared data with products, - last versions and representations for specific asset. + linked_folder_data (Dict[str, Any]): Prepared data with products, + last versions and representations for specific folder. build_profiles (Dict[str, Any]): Build profiles. loaders_by_name (Dict[str, LoaderPlugin]): Available loaders per name. Returns: - Dict[str, Any]: Output contains asset document + Dict[str, Any]: Output contains folder entity and loaded containers. """ # Make sure all data are not empty - if not asset_doc_data or not build_profiles or not loaders_by_name: + if not linked_folder_data or not build_profiles or not loaders_by_name: return - asset_doc = asset_doc_data["asset_doc"] + folder_entity = linked_folder_data["folder_entity"] valid_profiles = self._filter_build_profiles( build_profiles, loaders_by_name @@ -442,7 +473,7 @@ class BuildWorkfile: products_by_id = {} version_by_product_id = {} repres_by_version_id = {} - for product_id, in_data in asset_doc_data["subsets"].items(): + for product_id, in_data in linked_folder_data["subsets"].items(): subset_doc = in_data["subset_doc"] products_by_id[subset_doc["_id"]] = subset_doc @@ -454,8 +485,8 @@ class BuildWorkfile: ) if not products_by_id: - self.log.warning("There are not products for folder {0}".format( - asset_doc["name"] + self.log.warning("There are not products for folder {}".format( + folder_entity["path"] )) return @@ -480,7 +511,7 @@ class BuildWorkfile: # DEBUG message msg = "Valid representations for Folder: `{}`".format( - asset_doc["name"] + folder_entity["path"] ) for product_id, repres in valid_repres_by_product_id.items(): subset_doc = products_by_id[product_id] @@ -498,7 +529,7 @@ class BuildWorkfile: ) return { - "asset_doc": asset_doc, + "folder_entity": folder_entity, "containers": containers } @@ -628,11 +659,11 @@ class BuildWorkfile: return loaded_containers - def _collect_last_version_repres(self, asset_docs): - """Collect products, versions and representations for asset_entities. + def _collect_last_version_repres(self, folder_entities): + """Collect products, versions and representations for folder_entities. Args: - asset_docs (List[Dict[str, Any]]): Asset entities for which + folder_entities (List[Dict[str, Any]]): Folder entities for which want to find data. Returns: @@ -641,11 +672,11 @@ class BuildWorkfile: Example output: ``` { - {Asset ID}: { - "asset_doc": , + : { + "folder_entity": , "subsets": { - {Subset ID}: { - "subset_doc": , + : { + "subset_doc": , "version": { "version_doc": , "repres": [ @@ -665,17 +696,17 @@ class BuildWorkfile: from ayon_core.pipeline.context_tools import get_current_project_name output = {} - if not asset_docs: + if not folder_entities: return output - asset_docs_by_ids = { - asset_doc["_id"]: asset_doc - for asset_doc in asset_docs + folder_entities_by_id = { + folder_entity["id"]: folder_entity + for folder_entity in folder_entities } project_name = get_current_project_name() subset_docs = list(get_subsets( - project_name, asset_ids=asset_docs_by_ids.keys() + project_name, asset_ids=folder_entities_by_id.keys() )) subset_docs_by_id = { subset_doc["_id"]: subset_doc @@ -701,11 +732,11 @@ class BuildWorkfile: subset_doc = subset_docs_by_id[product_id] folder_id = subset_doc["parent"] - asset_doc = asset_docs_by_ids[folder_id] + folder_entity = folder_entities_by_id[folder_id] if folder_id not in output: output[folder_id] = { - "asset_doc": asset_doc, + "folder_entity": folder_entity, "subsets": {} } diff --git a/client/ayon_core/pipeline/workfile/path_resolving.py b/client/ayon_core/pipeline/workfile/path_resolving.py index fc0de60b36..5d36f432ad 100644 --- a/client/ayon_core/pipeline/workfile/path_resolving.py +++ b/client/ayon_core/pipeline/workfile/path_resolving.py @@ -5,7 +5,6 @@ import platform import ayon_api -from ayon_core.client import get_asset_by_name from ayon_core.settings import get_project_settings from ayon_core.lib import ( filter_profiles, @@ -17,7 +16,11 @@ from ayon_core.pipeline.template_data import get_template_data def get_workfile_template_key_from_context( - asset_name, task_name, host_name, project_name, project_settings=None + project_name, + folder_path, + task_name, + host_name, + project_settings=None ): """Helper function to get template key for workfile template. @@ -25,41 +28,39 @@ def get_workfile_template_key_from_context( context". Args: - asset_name(str): Name of asset document. - task_name(str): Task name for which is template key retrieved. - Must be available on asset document under `data.tasks`. - host_name(str): Name of host implementation for which is workfile - used. - project_name(str): Project name where asset and task is. - project_settings(Dict[str, Any]): Project settings for passed + project_name (str): Project name. + folder_path (str): Folder path. + task_name (str): Task name. + host_name (str): Host name. + project_settings (Dict[str, Any]): Project settings for passed 'project_name'. Not required at all but makes function faster. """ - asset_doc = get_asset_by_name( - project_name, asset_name, fields=["data.tasks"] + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path, fields={"id"} ) - asset_tasks = asset_doc.get("data", {}).get("tasks") or {} - task_info = asset_tasks.get(task_name) or {} - task_type = task_info.get("type") + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) + task_type = task_entity.get("type") return get_workfile_template_key( - task_type, host_name, project_name, project_settings + project_name, task_type, host_name, project_settings ) def get_workfile_template_key( - task_type, host_name, project_name, project_settings=None + project_name, task_type, host_name, project_settings=None ): """Workfile template key which should be used to get workfile template. Function is using profiles from project settings to return right template - for passet task type and host name. + for passed task type and host name. Args: - task_type(str): Name of task type. - host_name(str): Name of host implementation (e.g. "maya", "nuke", ...) - project_name(str): Name of project in which context should look for - settings. + project_name(str): Project name. + task_type(str): Task type. + host_name(str): Host name (e.g. "maya", "nuke", ...) project_settings(Dict[str, Any]): Prepared project settings for project name. Optional to make processing faster. """ @@ -128,9 +129,9 @@ def get_workdir_with_workdir_data( if not template_key: template_key = get_workfile_template_key( + workdir_data["project"]["name"], workdir_data["task"]["type"], workdir_data["app"], - workdir_data["project"]["name"], project_settings ) @@ -144,8 +145,8 @@ def get_workdir_with_workdir_data( def get_workdir( project_entity, - asset_doc, - task_name, + folder_entity, + task_entity, host_name, anatomy=None, template_key=None, @@ -155,8 +156,8 @@ def get_workdir( Args: project_entity (Dict[str, Any]): Project entity. - asset_doc (Dict[str, Any]): Mongo document of asset from MongoDB. - task_name (str): Task name for which are workdir data preapred. + folder_entity (Dict[str, Any]): Folder entity. + task_entity (dict[str, Any]): Task entity. host_name (str): Host which is used to workdir. This is required because workdir template may contain `{app}` key. In `Session` is stored under `AYON_HOST_NAME` key. @@ -180,7 +181,10 @@ def get_workdir( ) workdir_data = get_template_data( - project_entity, asset_doc, task_name, host_name + project_entity, + folder_entity, + task_entity, + host_name, ) # Output is TemplateResult object which contain useful data return get_workdir_with_workdir_data( @@ -341,8 +345,8 @@ def get_last_workfile( def get_custom_workfile_template( project_entity, - asset_doc, - task_name, + folder_entity, + task_entity, host_name, anatomy=None, project_settings=None @@ -360,13 +364,13 @@ def get_custom_workfile_template( points to a file which is copied as first workfile - It is expected that passed argument are already queried documents of - project and asset as parents of processing task name. + It is expected that passed argument are already queried entities of + project and folder as parents of processing task name. Args: project_entity (Dict[str, Any]): Project entity. - asset_doc (Dict[str, Any]): Asset document from MongoDB. - task_name (str): Name of task for which templates are filtered. + folder_entity (Dict[str, Any]): Folder entity. + task_entity (Dict[str, Any]): Task entity. host_name (str): Name of host. anatomy (Anatomy): Optionally passed anatomy object for passed project name. @@ -415,9 +419,9 @@ def get_custom_workfile_template( if anatomy is None: anatomy = Anatomy(project_name) - # get project, asset, task anatomy context data + # get project, folder, task anatomy context data anatomy_context_data = get_template_data( - project_entity, asset_doc, task_name, host_name + project_entity, folder_entity, task_entity, host_name ) # add root dict anatomy_context_data["root"] = anatomy.roots @@ -448,7 +452,7 @@ def get_custom_workfile_template( def get_custom_workfile_template_by_string_context( project_name, - asset_name, + folder_path, task_name, host_name, anatomy=None, @@ -456,13 +460,13 @@ def get_custom_workfile_template_by_string_context( ): """Filter and fill workfile template profiles by passed context. - Passed context are string representations of project, asset and task. - Function will query documents of project and asset to be able use + Passed context are string representations of project, folder and task. + Function will query documents of project and folder to be able to use `get_custom_workfile_template` for rest of logic. Args: project_name (str): Project name. - asset_name (str): Asset name. + folder_path (str): Folder path. task_name (str): Task name. host_name (str): Name of host. anatomy (Anatomy): Optionally prepared anatomy object for passed @@ -476,12 +480,15 @@ def get_custom_workfile_template_by_string_context( """ project_entity = ayon_api.get_project(project_name) - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path(project_name, folder_path) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) return get_custom_workfile_template( project_entity, - asset_doc, - task_name, + folder_entity, + task_entity, host_name, anatomy, project_settings diff --git a/client/ayon_core/pipeline/workfile/workfile_template_builder.py b/client/ayon_core/pipeline/workfile/workfile_template_builder.py index 1b5a848ea2..a39c5dd31d 100644 --- a/client/ayon_core/pipeline/workfile/workfile_template_builder.py +++ b/client/ayon_core/pipeline/workfile/workfile_template_builder.py @@ -18,12 +18,17 @@ import copy from abc import ABCMeta, abstractmethod import six -from ayon_api import get_products, get_last_versions +from ayon_api import ( + get_folders, + get_folder_by_path, + get_folder_links, + get_task_by_name, + get_products, + get_last_versions, +) from ayon_api.graphql_queries import folders_graphql_query from ayon_core.client import ( - get_asset_by_name, - get_linked_assets, get_representations, get_ayon_server_api_connection, ) @@ -48,6 +53,8 @@ from ayon_core.pipeline.create import ( CreateContext, ) +_NOT_SET = object() + class TemplateNotFound(Exception): """Exception raised when template does not exist.""" @@ -117,9 +124,9 @@ class AbstractTemplateBuilder(object): self._project_settings = None - self._current_asset_doc = None - self._linked_asset_docs = None - self._task_type = None + self._current_folder_entity = _NOT_SET + self._current_task_entity = _NOT_SET + self._linked_folder_entities = _NOT_SET @property def project_name(self): @@ -155,33 +162,39 @@ class AbstractTemplateBuilder(object): return self._project_settings @property - def current_asset_doc(self): - if self._current_asset_doc is None: - self._current_asset_doc = get_asset_by_name( + def current_folder_entity(self): + if self._current_folder_entity is _NOT_SET: + self._current_folder_entity = get_folder_by_path( self.project_name, self.current_folder_path ) - return self._current_asset_doc + return self._current_folder_entity @property - def linked_asset_docs(self): - if self._linked_asset_docs is None: - self._linked_asset_docs = get_linked_assets( - self.project_name, self.current_asset_doc - ) - return self._linked_asset_docs + def linked_folder_entities(self): + if self._linked_folder_entities is _NOT_SET: + self._linked_folder_entities = self._get_linked_folder_entities() + return self._linked_folder_entities + + @property + def current_task_entity(self): + if self._current_task_entity is _NOT_SET: + task_entity = None + folder_entity = self.current_folder_entity + if folder_entity: + task_entity = get_task_by_name( + self.project_name, + folder_entity["id"], + self.current_task_name + ) + self._current_task_entity = task_entity + return self._current_task_entity @property def current_task_type(self): - asset_doc = self.current_asset_doc - if not asset_doc: - return None - return ( - asset_doc - .get("data", {}) - .get("tasks", {}) - .get(self.current_task_name, {}) - .get("type") - ) + task_entity = self.current_task_entity + if task_entity: + return task_entity["taskType"] + return None @property def create_context(self): @@ -242,9 +255,9 @@ class AbstractTemplateBuilder(object): self._loaders_by_name = None self._creators_by_name = None - self._current_asset_doc = None - self._linked_asset_docs = None - self._task_type = None + self._current_folder_entity = _NOT_SET + self._current_task_entity = _NOT_SET + self._linked_folder_entities = _NOT_SET self._project_settings = None @@ -256,6 +269,22 @@ class AbstractTemplateBuilder(object): self._loaders_by_name = get_loaders_by_name() return self._loaders_by_name + def _get_linked_folder_entities(self): + project_name = self.project_name + folder_entity = self.current_folder_entity + if not folder_entity: + return [] + links = get_folder_links( + project_name, folder_entity["id"], link_direction="in" + ) + linked_folder_ids = { + link["entityId"] + for link in links + if link["entityType"] == "folder" + } + + return list(get_folders(project_name, folder_ids=linked_folder_ids)) + def _collect_legacy_creators(self): creators_by_name = {} for creator in discover_legacy_creator_plugins(): @@ -305,8 +334,8 @@ class AbstractTemplateBuilder(object): different key or if the key is not already used for something else. Key should be self explanatory to content. - - wrong: 'asset' - - good: 'asset_name' + - wrong: 'folder' + - good: 'folder_name' Args: key (str): Key under which is key stored. @@ -351,8 +380,8 @@ class AbstractTemplateBuilder(object): different key or if the key is not already used for something else. Key should be self explanatory to content. - - wrong: 'asset' - - good: 'asset_name' + - wrong: 'folder' + - good: 'folder_path' Args: key (str): Key under which is key stored. @@ -371,8 +400,8 @@ class AbstractTemplateBuilder(object): different key or if the key is not already used for something else. Key should be self explanatory to content. - - wrong: 'asset' - - good: 'asset_name' + - wrong: 'folder' + - good: 'folder_path' Args: key (str): Key under which is key stored. @@ -1021,8 +1050,8 @@ class PlaceholderPlugin(object): Using shared data from builder but stored under plugin identifier. Key should be self explanatory to content. - - wrong: 'asset' - - good: 'asset_name' + - wrong: 'folder' + - good: 'folder_path' Args: key (str): Key under which is key stored. @@ -1061,8 +1090,8 @@ class PlaceholderPlugin(object): Using shared data from builder but stored under plugin identifier. Key should be self explanatory to content. - - wrong: 'asset' - - good: 'asset_name' + - wrong: 'folder' + - good: 'folder_path' Shared populate data are cleaned up during populate while loop. @@ -1323,7 +1352,7 @@ class PlaceholderLoadMixin(object): items=loader_items, tooltip=( "Loader" - "\nDefines what OpenPype loader will be used to" + "\nDefines what AYON loader will be used to" " load assets." "\nUseable loader depends on current host's loader list." "\nField is case sensitive." @@ -1480,7 +1509,7 @@ class PlaceholderLoadMixin(object): return [] project_name = self.builder.project_name - current_asset_doc = self.builder.current_asset_doc + current_folder_entity = self.builder.current_folder_entity folder_path_regex = placeholder.data["folder_path"] product_name_regex_value = placeholder.data["product_name"] @@ -1492,7 +1521,7 @@ class PlaceholderLoadMixin(object): builder_type = placeholder.data["builder_type"] folder_ids = [] if builder_type == "context_folder": - folder_ids = [current_asset_doc["_id"]] + folder_ids = [current_folder_entity["_id"]] elif builder_type == "all_folders": folder_ids = list(self._query_by_folder_regex( @@ -1547,17 +1576,18 @@ class PlaceholderLoadMixin(object): """Reduce representations to last verison.""" mapping = {} + # TODO use representation context with entities for repre_doc in representations: repre_context = repre_doc["context"] - asset_name = repre_context["asset"] + folder_name = repre_context["asset"] product_name = repre_context["subset"] version = repre_context.get("version", -1) - if asset_name not in mapping: - mapping[asset_name] = {} + if folder_name not in mapping: + mapping[folder_name] = {} - product_mapping = mapping[asset_name] + product_mapping = mapping[folder_name] if product_name not in product_mapping: product_mapping[product_name] = collections.defaultdict(list) @@ -1788,31 +1818,24 @@ class PlaceholderCreateMixin(object): # create product name context = self._builder.get_current_context() project_name = context["project_name"] - asset_name = context["folder_path"] + folder_path = context["folder_path"] task_name = context["task_name"] + host_name = self.builder.host_name - if legacy_create: - asset_doc = get_asset_by_name( - project_name, asset_name, fields=["_id"] - ) - assert asset_doc, "No current asset found in Session" - product_name = creator_plugin.get_product_name( - project_name, - asset_doc["_id"], - task_name, - create_variant, - ) + folder_entity = get_folder_by_path(project_name, folder_path) + if not folder_entity: + raise ValueError("Current context does not have set folder") + task_entity = get_task_by_name( + project_name, folder_entity["id"], task_name + ) - else: - asset_doc = get_asset_by_name(project_name, asset_name) - assert asset_doc, "No current asset found in Session" - product_name = creator_plugin.get_product_name( - project_name, - asset_doc, - task_name, - create_variant, - self.builder.host_name - ) + product_name = creator_plugin.get_product_name( + project_name, + folder_entity, + task_entity, + create_variant, + host_name + ) creator_data = { "creator_name": creator_name, @@ -1828,13 +1851,13 @@ class PlaceholderCreateMixin(object): if legacy_create: creator_instance = creator_plugin( product_name, - asset_name + folder_path ).process() else: creator_instance = self.builder.create_context.create( creator_plugin.identifier, create_variant, - asset_doc, + folder_entity, task_name=task_name, pre_create_data=pre_create_data ) From 43a96ceb2abf0763314f24483bbbb088a2b8722b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 15:38:33 +0100 Subject: [PATCH 401/573] applications launch context has folder and task entity --- .../hooks/pre_copy_template_workfile.py | 13 +++---- .../ayon_core/hooks/pre_global_host_data.py | 30 ++++++++++++---- client/ayon_core/hooks/pre_ocio_hook.py | 2 +- .../celaction/hooks/pre_celaction_setup.py | 6 ++-- .../unreal/hooks/pre_workfile_preparation.py | 2 +- client/ayon_core/lib/applications.py | 35 ++++++++++--------- client/ayon_core/lib/path_tools.py | 2 +- .../launch_hooks/post_start_timer.py | 6 ++-- 8 files changed, 58 insertions(+), 38 deletions(-) diff --git a/client/ayon_core/hooks/pre_copy_template_workfile.py b/client/ayon_core/hooks/pre_copy_template_workfile.py index 13f2f64565..096ad7dd7e 100644 --- a/client/ayon_core/hooks/pre_copy_template_workfile.py +++ b/client/ayon_core/hooks/pre_copy_template_workfile.py @@ -54,21 +54,22 @@ class CopyTemplateWorkfile(PreLaunchHook): self.log.info("Last workfile does not exist.") project_name = self.data["project_name"] - asset_name = self.data["folder_path"] + folder_path = self.data["folder_path"] task_name = self.data["task_name"] host_name = self.application.host_name project_settings = get_project_settings(project_name) project_entity = self.data.get("project_entity") - asset_doc = self.data.get("asset_doc") + folder_entity = self.data.get("folder_entity") + task_entity = self.data.get("task_entity") anatomy = self.data.get("anatomy") - if project_entity and asset_doc: + if project_entity and folder_entity and task_entity: self.log.debug("Started filtering of custom template paths.") template_path = get_custom_workfile_template( project_entity, - asset_doc, - task_name, + folder_entity, + task_entity, host_name, anatomy, project_settings @@ -81,7 +82,7 @@ class CopyTemplateWorkfile(PreLaunchHook): )) template_path = get_custom_workfile_template_by_string_context( project_name, - asset_name, + folder_path, task_name, host_name, anatomy, diff --git a/client/ayon_core/hooks/pre_global_host_data.py b/client/ayon_core/hooks/pre_global_host_data.py index b6cfe2d5d4..27e66450ab 100644 --- a/client/ayon_core/hooks/pre_global_host_data.py +++ b/client/ayon_core/hooks/pre_global_host_data.py @@ -1,4 +1,4 @@ -from ayon_api import get_project, get_folder_by_path +from ayon_api import get_project, get_folder_by_path, get_task_by_name from ayon_core.lib.applications import ( PreLaunchHook, @@ -17,7 +17,7 @@ class GlobalHostDataHook(PreLaunchHook): """Prepare global objects to `data` that will be used for sure.""" self.prepare_global_data() - if not self.data.get("asset_doc"): + if not self.data.get("folder_entity"): return app = self.launch_context.application @@ -29,7 +29,8 @@ class GlobalHostDataHook(PreLaunchHook): "app": app, "project_entity": self.data["project_entity"], - "asset_doc": self.data["asset_doc"], + "folder_entity": self.data["folder_entity"], + "task_entity": self.data["task_entity"], "anatomy": self.data["anatomy"], @@ -70,12 +71,27 @@ class GlobalHostDataHook(PreLaunchHook): project_name, project_entity=project_entity ) - asset_name = self.data.get("folder_path") - if not asset_name: + folder_path = self.data.get("folder_path") + if not folder_path: self.log.warning( - "Asset name was not set. Skipping asset document query." + "Folder path is not set. Skipping folder query." ) return - folder_entity = get_folder_by_path(project_name, asset_name) + folder_entity = get_folder_by_path(project_name, folder_path) self.data["folder_entity"] = folder_entity + + task_name = self.data.get("task_name") + if not task_name: + self.log.warning( + "Task name is not set. Skipping task query." + ) + return + + if not folder_entity: + return + + task_entity = get_task_by_name( + project_name, folder_entity["id"], task_name + ) + self.data["task_entity"] = task_entity \ No newline at end of file diff --git a/client/ayon_core/hooks/pre_ocio_hook.py b/client/ayon_core/hooks/pre_ocio_hook.py index 08d9563975..e135a5bb12 100644 --- a/client/ayon_core/hooks/pre_ocio_hook.py +++ b/client/ayon_core/hooks/pre_ocio_hook.py @@ -28,7 +28,7 @@ class OCIOEnvHook(PreLaunchHook): template_data = get_template_data_with_names( project_name=self.data["project_name"], - asset_name=self.data["folder_path"], + folder_path=self.data["folder_path"], task_name=self.data["task_name"], host_name=self.host_name, settings=self.data["project_settings"] diff --git a/client/ayon_core/hosts/celaction/hooks/pre_celaction_setup.py b/client/ayon_core/hosts/celaction/hooks/pre_celaction_setup.py index bf1b4937cd..73b368e4e3 100644 --- a/client/ayon_core/hosts/celaction/hooks/pre_celaction_setup.py +++ b/client/ayon_core/hosts/celaction/hooks/pre_celaction_setup.py @@ -16,9 +16,9 @@ class CelactionPrelaunchHook(PreLaunchHook): launch_types = {LaunchTypes.local} def execute(self): - asset_doc = self.data["asset_doc"] - width = asset_doc["data"]["resolutionWidth"] - height = asset_doc["data"]["resolutionHeight"] + folder_attributes = self.data["folder_entity"]["attrib"] + width = folder_attributes["resolutionWidth"] + height = folder_attributes["resolutionHeight"] # Add workfile path to launch arguments workfile_path = self.workfile_path() diff --git a/client/ayon_core/hosts/unreal/hooks/pre_workfile_preparation.py b/client/ayon_core/hosts/unreal/hooks/pre_workfile_preparation.py index a8feaa31d8..2f78bf026d 100644 --- a/client/ayon_core/hosts/unreal/hooks/pre_workfile_preparation.py +++ b/client/ayon_core/hosts/unreal/hooks/pre_workfile_preparation.py @@ -61,9 +61,9 @@ class UnrealPrelaunchHook(PreLaunchHook): # Get workfile template key for current context workfile_template_key = get_workfile_template_key( + project_entity["name"], task_type, self.host_name, - project_name=project_entity["name"] ) # Fill templates template_obj = anatomy.templates_obj[workfile_template_key]["file"] diff --git a/client/ayon_core/lib/applications.py b/client/ayon_core/lib/applications.py index 866a616518..e67f996bab 100644 --- a/client/ayon_core/lib/applications.py +++ b/client/ayon_core/lib/applications.py @@ -12,7 +12,6 @@ from abc import ABCMeta, abstractmethod import six from ayon_core import AYON_CORE_ROOT -from ayon_core.client import get_asset_name_identifier from ayon_core.settings import get_project_settings, get_studio_settings from .log import Logger from .profiles_filtering import filter_profiles @@ -1381,7 +1380,7 @@ class EnvironmentPrepData(dict): data (dict): Data must contain required keys. """ required_keys = ( - "project_entity", "asset_doc", "task_name", "app", "anatomy" + "project_entity", "folder_entity", "task_name", "app", "anatomy" ) def __init__(self, data): @@ -1546,13 +1545,13 @@ def prepare_app_environments( app.environment ] - asset_doc = data.get("asset_doc") + folder_entity = data.get("folder_entity") # Add tools environments groups_by_name = {} tool_by_group_name = collections.defaultdict(dict) - if asset_doc: + if folder_entity: # Make sure each tool group can be added only once - for key in asset_doc["data"].get("tools_env") or []: + for key in folder_entity["attrib"].get("tools") or []: tool = app.manager.tools.get(key) if not tool or not tool.is_valid_for_app(app): continue @@ -1675,8 +1674,8 @@ def prepare_context_environments(data, env_group=None, addons_manager=None): log = data["log"] project_entity = data["project_entity"] - asset_doc = data["asset_doc"] - task_name = data["task_name"] + folder_entity = data["folder_entity"] + task_entity = data["task_entity"] if not project_entity: log.info( "Skipping context environments preparation." @@ -1694,12 +1693,12 @@ def prepare_context_environments(data, env_group=None, addons_manager=None): "AYON_PROJECT_NAME": project_entity["name"], "AYON_APP_NAME": app.full_name } - if asset_doc: - asset_name = get_asset_name_identifier(asset_doc) - context_env["AYON_FOLDER_PATH"] = asset_name + if folder_entity: + folder_path = folder_entity["path"] + context_env["AYON_FOLDER_PATH"] = folder_path - if task_name: - context_env["AYON_TASK_NAME"] = task_name + if task_entity: + context_env["AYON_TASK_NAME"] = task_entity["name"] log.debug( "Context environments set:\n{}".format( @@ -1719,15 +1718,19 @@ def prepare_context_environments(data, env_group=None, addons_manager=None): data["env"]["AYON_HOST_NAME"] = app.host_name - if not asset_doc or not task_name: + if not folder_entity or not task_entity: # QUESTION replace with log.info and skip workfile discovery? # - technically it should be possible to launch host without context raise ApplicationLaunchFailed( - "Host launch require asset and task context." + "Host launch require folder and task context." ) workdir_data = get_template_data( - project_entity, asset_doc, task_name, app.host_name, project_settings + project_entity, + folder_entity, + task_entity, + app.host_name, + project_settings ) data["workdir_data"] = workdir_data @@ -1853,9 +1856,9 @@ def _prepare_last_workfile(data, workdir, addons_manager): project_settings = data["project_settings"] task_type = workdir_data["task"]["type"] template_key = get_workfile_template_key( + project_name, task_type, app.host_name, - project_name, project_settings=project_settings ) # Find last workfile diff --git a/client/ayon_core/lib/path_tools.py b/client/ayon_core/lib/path_tools.py index fec6a0c47d..74475adfe3 100644 --- a/client/ayon_core/lib/path_tools.py +++ b/client/ayon_core/lib/path_tools.py @@ -78,7 +78,7 @@ def collect_frames(files): files(list) or (set with single value): list of source paths Returns: - (dict): {'/asset/subset_v001.0001.png': '0001', ....} + dict: {'/folder/subset_v001.0001.png': '0001', ....} """ patterns = [clique.PATTERNS["frames"]] diff --git a/client/ayon_core/modules/timers_manager/launch_hooks/post_start_timer.py b/client/ayon_core/modules/timers_manager/launch_hooks/post_start_timer.py index 4e94603aa9..da5d430939 100644 --- a/client/ayon_core/modules/timers_manager/launch_hooks/post_start_timer.py +++ b/client/ayon_core/modules/timers_manager/launch_hooks/post_start_timer.py @@ -11,13 +11,13 @@ class PostStartTimerHook(PostLaunchHook): def execute(self): project_name = self.data.get("project_name") - asset_name = self.data.get("folder_path") + folder_path = self.data.get("folder_path") task_name = self.data.get("task_name") missing_context_keys = set() if not project_name: missing_context_keys.add("project_name") - if not asset_name: + if not folder_path: missing_context_keys.add("folder_path") if not task_name: missing_context_keys.add("task_name") @@ -40,5 +40,5 @@ class PostStartTimerHook(PostLaunchHook): return timers_manager.start_timer_with_webserver( - project_name, asset_name, task_name, logger=self.log + project_name, folder_path, task_name, logger=self.log ) From 88ff06a7b27c40a309798410e777fd1a89127a11 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 15:41:41 +0100 Subject: [PATCH 402/573] change requirements --- client/ayon_core/lib/applications.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/lib/applications.py b/client/ayon_core/lib/applications.py index e67f996bab..4bf0c31d93 100644 --- a/client/ayon_core/lib/applications.py +++ b/client/ayon_core/lib/applications.py @@ -1380,7 +1380,7 @@ class EnvironmentPrepData(dict): data (dict): Data must contain required keys. """ required_keys = ( - "project_entity", "folder_entity", "task_name", "app", "anatomy" + "project_entity", "folder_entity", "task_entity", "app", "anatomy" ) def __init__(self, data): From d1a09aa46a3e726a7a6a1c8dff0005154c66984f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 15:44:24 +0100 Subject: [PATCH 403/573] aftereffects use folder and task entities --- .../hosts/aftereffects/api/__init__.py | 4 +- .../hosts/aftereffects/api/launch_logic.py | 13 +++--- .../ayon_core/hosts/aftereffects/api/lib.py | 32 ++++++++------- .../plugins/create/create_render.py | 8 +++- .../plugins/create/workfile_creator.py | 41 ++++++++++++------- .../plugins/load/load_background.py | 5 +-- .../aftereffects/plugins/load/load_file.py | 8 ++-- .../publish/help/validate_instance_asset.xml | 2 +- .../publish/help/validate_scene_settings.xml | 6 +-- .../publish/validate_instance_asset.py | 26 ++++++------ .../publish/validate_scene_settings.py | 12 +++--- 11 files changed, 90 insertions(+), 67 deletions(-) diff --git a/client/ayon_core/hosts/aftereffects/api/__init__.py b/client/ayon_core/hosts/aftereffects/api/__init__.py index 28062cc35d..4c4a8cce2f 100644 --- a/client/ayon_core/hosts/aftereffects/api/__init__.py +++ b/client/ayon_core/hosts/aftereffects/api/__init__.py @@ -17,7 +17,7 @@ from .pipeline import ( from .lib import ( maintained_selection, get_extension_manifest_path, - get_asset_settings, + get_folder_settings, set_settings ) @@ -37,7 +37,7 @@ __all__ = [ # lib "maintained_selection", "get_extension_manifest_path", - "get_asset_settings", + "get_folder_settings", "set_settings", # plugin diff --git a/client/ayon_core/hosts/aftereffects/api/launch_logic.py b/client/ayon_core/hosts/aftereffects/api/launch_logic.py index 0d1a6cf585..d0e4e8beae 100644 --- a/client/ayon_core/hosts/aftereffects/api/launch_logic.py +++ b/client/ayon_core/hosts/aftereffects/api/launch_logic.py @@ -286,20 +286,21 @@ class AfterEffectsRoute(WebSocketRoute): # This method calls function on the client side # client functions - async def set_context(self, project, asset, task): + async def set_context(self, project, folder, task): """ - Sets 'project' and 'asset' to envs, eg. setting context + Sets 'project', 'folder' and 'task' to envs, eg. setting context Args: project (str) - asset (str) + folder (str) + task (str) """ log.info("Setting context change") - log.info("project {} asset {} ".format(project, asset)) + log.info("project {} folder {} ".format(project, folder)) if project: os.environ["AYON_PROJECT_NAME"] = project - if asset: - os.environ["AYON_FOLDER_PATH"] = asset + if folder: + os.environ["AYON_FOLDER_PATH"] = folder if task: os.environ["AYON_TASK_NAME"] = task diff --git a/client/ayon_core/hosts/aftereffects/api/lib.py b/client/ayon_core/hosts/aftereffects/api/lib.py index 0a2ee7b7ac..d476378dcd 100644 --- a/client/ayon_core/hosts/aftereffects/api/lib.py +++ b/client/ayon_core/hosts/aftereffects/api/lib.py @@ -4,8 +4,10 @@ import json import contextlib import logging +import ayon_api + from ayon_core.pipeline.context_tools import get_current_context -from ayon_core.client import get_asset_by_name + from .ws_stub import get_stub log = logging.getLogger(__name__) @@ -85,21 +87,21 @@ def get_background_layers(file_url): return layers -def get_asset_settings(asset_doc): - """Get settings on current asset from database. +def get_folder_settings(folder_entity): + """Get settings of current folder. Returns: dict: Scene data. """ - asset_data = asset_doc["data"] - fps = asset_data.get("fps", 0) - frame_start = asset_data.get("frameStart", 0) - frame_end = asset_data.get("frameEnd", 0) - handle_start = asset_data.get("handleStart", 0) - handle_end = asset_data.get("handleEnd", 0) - resolution_width = asset_data.get("resolutionWidth", 0) - resolution_height = asset_data.get("resolutionHeight", 0) + folder_attributes = folder_entity["attrib"] + fps = folder_attributes.get("fps", 0) + frame_start = folder_attributes.get("frameStart", 0) + frame_end = folder_attributes.get("frameEnd", 0) + handle_start = folder_attributes.get("handleStart", 0) + handle_end = folder_attributes.get("handleEnd", 0) + resolution_width = folder_attributes.get("resolutionWidth", 0) + resolution_height = folder_attributes.get("resolutionHeight", 0) duration = (frame_end - frame_start + 1) + handle_start + handle_end return { @@ -127,9 +129,11 @@ def set_settings(frames, resolution, comp_ids=None, print_msg=True): frame_start = frames_duration = fps = width = height = None current_context = get_current_context() - asset_doc = get_asset_by_name(current_context["project_name"], - current_context["folder_path"]) - settings = get_asset_settings(asset_doc) + folder_entity = ayon_api.get_folder_by_path( + current_context["project_name"], + current_context["folder_path"] + ) + settings = get_folder_settings(folder_entity) msg = '' if frames: diff --git a/client/ayon_core/hosts/aftereffects/plugins/create/create_render.py b/client/ayon_core/hosts/aftereffects/plugins/create/create_render.py index 93aec33222..29df34876a 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/create/create_render.py +++ b/client/ayon_core/hosts/aftereffects/plugins/create/create_render.py @@ -218,7 +218,13 @@ class RenderCreator(Creator): """ def get_dynamic_data( - self, project_name, asset_doc, task_name, variant, host_name, instance + self, + project_name, + folder_entity, + task_entity, + variant, + host_name, + instance ): dynamic_data = {} if instance is not None: diff --git a/client/ayon_core/hosts/aftereffects/plugins/create/workfile_creator.py b/client/ayon_core/hosts/aftereffects/plugins/create/workfile_creator.py index 0dae779496..b46e82bf1a 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/create/workfile_creator.py +++ b/client/ayon_core/hosts/aftereffects/plugins/create/workfile_creator.py @@ -1,5 +1,6 @@ +import ayon_api + import ayon_core.hosts.aftereffects.api as api -from ayon_core.client import get_asset_by_name from ayon_core.pipeline import ( AutoCreator, CreatedInstance @@ -39,32 +40,37 @@ class AEWorkfileCreator(AutoCreator): context = self.create_context project_name = context.get_current_project_name() - asset_name = context.get_current_folder_path() + folder_path = context.get_current_folder_path() task_name = context.get_current_task_name() host_name = context.host_name - existing_asset_name = None + existing_folder_path = None if existing_instance is not None: - existing_asset_name = existing_instance.get("folderPath") + existing_folder_path = existing_instance.get("folderPath") if existing_instance is None: - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, self.default_variant, host_name, ) data = { - "folderPath": asset_name, + "folderPath": folder_path, "task": task_name, "variant": self.default_variant, } data.update(self.get_dynamic_data( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, self.default_variant, host_name, None, @@ -79,17 +85,22 @@ class AEWorkfileCreator(AutoCreator): new_instance.data_to_store()) elif ( - existing_asset_name != asset_name + existing_folder_path != folder_path or existing_instance["task"] != task_name ): - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, self.default_variant, host_name, ) - existing_instance["folderPath"] = asset_name + existing_instance["folderPath"] = folder_path existing_instance["task"] = task_name existing_instance["productName"] = product_name diff --git a/client/ayon_core/hosts/aftereffects/plugins/load/load_background.py b/client/ayon_core/hosts/aftereffects/plugins/load/load_background.py index b834875e89..eec69e57c0 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/load/load_background.py +++ b/client/ayon_core/hosts/aftereffects/plugins/load/load_background.py @@ -31,7 +31,7 @@ class BackgroundLoader(api.AfterEffectsLoader): comp_name = get_unique_layer_name( existing_items, - "{}_{}".format(context["asset"]["name"], name)) + "{}_{}".format(context["folder"]["name"], name)) path = self.filepath_from_context(context) layers = get_background_layers(path) @@ -59,11 +59,10 @@ class BackgroundLoader(api.AfterEffectsLoader): def update(self, container, context): """ Switch asset or change version """ stub = self.get_stub() - asset_doc = context["asset"] + folder_name = context["folder"]["name"] subset_doc = context["subset"] repre_doc = context["representation"] - folder_name = asset_doc["name"] product_name = subset_doc["name"] _ = container.pop("layer") diff --git a/client/ayon_core/hosts/aftereffects/plugins/load/load_file.py b/client/ayon_core/hosts/aftereffects/plugins/load/load_file.py index bceea66e8e..9f05a55628 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/load/load_file.py +++ b/client/ayon_core/hosts/aftereffects/plugins/load/load_file.py @@ -25,7 +25,10 @@ class FileLoader(api.AfterEffectsLoader): layers = stub.get_items(comps=True, folders=True, footages=True) existing_layers = [layer.name for layer in layers] comp_name = get_unique_layer_name( - existing_layers, "{}_{}".format(context["asset"]["name"], name)) + existing_layers, "{}_{}".format( + context["folder"]["name"], name + ) + ) import_options = {} @@ -69,11 +72,10 @@ class FileLoader(api.AfterEffectsLoader): stub = self.get_stub() layer = container.pop("layer") - asset_doc = context["asset"] + folder_name = context["folder"]["name"] subset_doc = context["subset"] repre_doc = context["representation"] - folder_name = asset_doc["name"] product_name = subset_doc["name"] namespace_from_container = re.sub(r'_\d{3}$', '', diff --git a/client/ayon_core/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml b/client/ayon_core/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml index d89a851c64..1507ba8d7b 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml +++ b/client/ayon_core/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml @@ -15,7 +15,7 @@ You can fix this with "repair" button on the right and refresh Publish at the bo ### __Detailed Info__ (optional) This might happen if you are reuse old workfile and open it in different context. -(Eg. you created product name "renderCompositingDefault" from folder "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing product for "Robot" asset stayed in the workfile.) +(Eg. you created product name "renderCompositingDefault" from folder "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing product for "Robot" folder stayed in the workfile.)
\ No newline at end of file diff --git a/client/ayon_core/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml b/client/ayon_core/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml index 0591020ed3..b2da7af114 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml +++ b/client/ayon_core/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml @@ -5,20 +5,20 @@ ## Invalid scene setting found -One of the settings in a scene doesn't match to asset settings in database. +One of the settings in a scene doesn't match to folder 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. +Change values for {invalid_keys_str} in the scene OR change them in the folder database if they are wrong there. In the scene it is right mouse click on published composition > `Composition Settings`. ### __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. +This error is shown when for example resolution in the scene doesn't match to resolution set on the folder in the database. Either value in the database or in the scene is wrong. diff --git a/client/ayon_core/hosts/aftereffects/plugins/publish/validate_instance_asset.py b/client/ayon_core/hosts/aftereffects/plugins/publish/validate_instance_asset.py index bf54ec2115..c4411bd4c2 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/publish/validate_instance_asset.py +++ b/client/ayon_core/hosts/aftereffects/plugins/publish/validate_instance_asset.py @@ -8,8 +8,8 @@ from ayon_core.pipeline.publish import ( from ayon_core.hosts.aftereffects.api import get_stub -class ValidateInstanceAssetRepair(pyblish.api.Action): - """Repair the instance asset with value from Context.""" +class ValidateInstanceFolderRepair(pyblish.api.Action): + """Repair the instance folder with value from Context.""" label = "Repair" icon = "wrench" @@ -34,31 +34,31 @@ class ValidateInstanceAssetRepair(pyblish.api.Action): stub.imprint(instance[0].instance_id, data) -class ValidateInstanceAsset(pyblish.api.InstancePlugin): - """Validate the instance asset is the current selected context asset. +class ValidateInstanceFolder(pyblish.api.InstancePlugin): + """Validate the instance folder is the current selected context folder. As it might happen that multiple worfiles are opened at same time, switching between them would mess with selected context. (From Launcher or Ftrack). - In that case outputs might be output under wrong asset! + In that case outputs might be output under wrong folder! - Repair action will use Context asset value (from Workfiles or Launcher) + Repair action will use Context folder value (from Workfiles or Launcher) Closing and reopening with Workfiles will refresh Context value. """ - label = "Validate Instance Asset" + label = "Validate Instance Folder" hosts = ["aftereffects"] - actions = [ValidateInstanceAssetRepair] + actions = [ValidateInstanceFolderRepair] order = ValidateContentsOrder def process(self, instance): - instance_asset = instance.data["folderPath"] - current_asset = get_current_folder_path() + instance_folder = instance.data["folderPath"] + current_folder = get_current_folder_path() msg = ( - f"Instance asset {instance_asset} is not the same " - f"as current context {current_asset}." + f"Instance folder {instance_folder} is not the same " + f"as current context {current_folder}." ) - if instance_asset != current_asset: + if instance_folder != current_folder: raise PublishXmlValidationError(self, msg) diff --git a/client/ayon_core/hosts/aftereffects/plugins/publish/validate_scene_settings.py b/client/ayon_core/hosts/aftereffects/plugins/publish/validate_scene_settings.py index 0a90ae2a5a..6375f5cc61 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/publish/validate_scene_settings.py +++ b/client/ayon_core/hosts/aftereffects/plugins/publish/validate_scene_settings.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Validate scene settings. Requires: - instance -> assetEntity + instance -> folderEntity instance -> anatomyData """ import os @@ -13,7 +13,7 @@ from ayon_core.pipeline import ( PublishXmlValidationError, OptionalPyblishPluginMixin ) -from ayon_core.hosts.aftereffects.api import get_asset_settings +from ayon_core.hosts.aftereffects.api import get_folder_settings class ValidateSceneSettings(OptionalPyblishPluginMixin, @@ -48,7 +48,7 @@ class ValidateSceneSettings(OptionalPyblishPluginMixin, fps handleStart handleEnd - skip_resolution_check - fill entity type ('asset') to skip validation + skip_resolution_check - fill entity type ('folder') to skip validation resolutionWidth resolutionHeight TODO support in extension is missing for now @@ -71,11 +71,11 @@ class ValidateSceneSettings(OptionalPyblishPluginMixin, if not self.is_active(instance.data): return - asset_doc = instance.data["assetEntity"] - expected_settings = get_asset_settings(asset_doc) + folder_entity = instance.data["folderEntity"] + expected_settings = get_folder_settings(folder_entity) self.log.info("config from DB::{}".format(expected_settings)) - task_name = instance.data["anatomyData"]["task"]["name"] + task_name = instance.data["task"] if any(re.search(pattern, task_name) for pattern in self.skip_resolution_check): expected_settings.pop("resolutionWidth") From 6a726ad802b1ad7d262f92a868456ec2ae3f2eec Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 15:47:26 +0100 Subject: [PATCH 404/573] blender use folder and task entity --- client/ayon_core/hosts/blender/api/ops.py | 10 ++--- .../ayon_core/hosts/blender/api/pipeline.py | 16 ++++---- client/ayon_core/hosts/blender/api/plugin.py | 14 +++---- .../blender/plugins/create/create_workfile.py | 40 ++++++++++++------- .../blender/plugins/load/import_workfile.py | 4 +- .../hosts/blender/plugins/load/load_abc.py | 2 +- .../hosts/blender/plugins/load/load_action.py | 2 +- .../hosts/blender/plugins/load/load_audio.py | 2 +- .../hosts/blender/plugins/load/load_blend.py | 2 +- .../blender/plugins/load/load_blendscene.py | 2 +- .../blender/plugins/load/load_camera_abc.py | 2 +- .../blender/plugins/load/load_camera_fbx.py | 2 +- .../hosts/blender/plugins/load/load_fbx.py | 2 +- .../blender/plugins/load/load_layout_json.py | 2 +- .../hosts/blender/plugins/load/load_look.py | 2 +- .../blender/plugins/publish/extract_abc.py | 2 +- .../plugins/publish/extract_abc_animation.py | 2 +- .../blender/plugins/publish/extract_blend.py | 2 +- .../publish/extract_blend_animation.py | 2 +- .../plugins/publish/extract_camera_abc.py | 2 +- .../plugins/publish/extract_camera_fbx.py | 2 +- .../blender/plugins/publish/extract_fbx.py | 2 +- .../plugins/publish/extract_fbx_animation.py | 2 +- .../blender/plugins/publish/extract_layout.py | 2 +- .../plugins/publish/extract_playblast.py | 2 +- .../plugins/publish/extract_thumbnail.py | 2 +- 26 files changed, 68 insertions(+), 58 deletions(-) diff --git a/client/ayon_core/hosts/blender/api/ops.py b/client/ayon_core/hosts/blender/api/ops.py index 91a79f9049..d71ee6faf5 100644 --- a/client/ayon_core/hosts/blender/api/ops.py +++ b/client/ayon_core/hosts/blender/api/ops.py @@ -355,7 +355,7 @@ class SetFrameRange(bpy.types.Operator): bl_label = "Set Frame Range" def execute(self, context): - data = pipeline.get_asset_data() + data = pipeline.get_folder_attributes() pipeline.set_frame_range(data) return {"FINISHED"} @@ -365,7 +365,7 @@ class SetResolution(bpy.types.Operator): bl_label = "Set Resolution" def execute(self, context): - data = pipeline.get_asset_data() + data = pipeline.get_folder_attributes() pipeline.set_resolution(data) return {"FINISHED"} @@ -388,9 +388,9 @@ class TOPBAR_MT_avalon(bpy.types.Menu): else: pyblish_menu_icon_id = 0 - asset = get_current_folder_path() - task = get_current_task_name() - context_label = f"{asset}, {task}" + folder_path = get_current_folder_path() + task_name = get_current_task_name() + context_label = f"{folder_path}, {task_name}" context_label_item = layout.row() context_label_item.operator( LaunchWorkFiles.bl_idname, text=context_label diff --git a/client/ayon_core/hosts/blender/api/pipeline.py b/client/ayon_core/hosts/blender/api/pipeline.py index 3270564b09..a2019b3288 100644 --- a/client/ayon_core/hosts/blender/api/pipeline.py +++ b/client/ayon_core/hosts/blender/api/pipeline.py @@ -9,6 +9,7 @@ from . import lib from . import ops import pyblish.api +import ayon_api from ayon_core.host import ( HostBase, @@ -16,7 +17,6 @@ from ayon_core.host import ( IPublishHost, ILoadHost ) -from ayon_core.client import get_asset_by_name from ayon_core.pipeline import ( schema, get_current_project_name, @@ -221,12 +221,12 @@ def message_window(title, message): _process_app_events() -def get_asset_data(): +def get_folder_attributes(): project_name = get_current_project_name() - asset_name = get_current_folder_path() - asset_doc = get_asset_by_name(project_name, asset_name) + folder_path = get_current_folder_path() + folder_entity = ayon_api.get_folder_by_path(project_name, folder_path) - return asset_doc.get("data") + return folder_entity["attrib"] def set_frame_range(data): @@ -279,7 +279,7 @@ def on_new(): set_resolution_startup = settings.get("set_resolution_startup") set_frames_startup = settings.get("set_frames_startup") - data = get_asset_data() + data = get_folder_attributes() if set_resolution_startup: set_resolution(data) @@ -300,7 +300,7 @@ def on_open(): set_resolution_startup = settings.get("set_resolution_startup") set_frames_startup = settings.get("set_frames_startup") - data = get_asset_data() + data = get_folder_attributes() if set_resolution_startup: set_resolution(data) @@ -468,7 +468,7 @@ def containerise(name: str, """ - node_name = f"{context['asset']['name']}_{name}" + node_name = f"{context['folder']['name']}_{name}" if namespace: node_name = f"{namespace}:{node_name}" if suffix: diff --git a/client/ayon_core/hosts/blender/api/plugin.py b/client/ayon_core/hosts/blender/api/plugin.py index 4b45d8ffa3..f8742a8113 100644 --- a/client/ayon_core/hosts/blender/api/plugin.py +++ b/client/ayon_core/hosts/blender/api/plugin.py @@ -49,7 +49,7 @@ def prepare_scene_name( def get_unique_number( folder_name: str, product_name: str ) -> str: - """Return a unique number based on the asset name.""" + """Return a unique number based on the folder name.""" avalon_container = bpy.data.collections.get(AVALON_CONTAINERS) if not avalon_container: return "01" @@ -232,9 +232,9 @@ class BaseCreator(Creator): bpy.context.scene.collection.children.link(instances) # Create asset group - asset_name = instance_data["folderPath"].split("/")[-1] + folder_name = instance_data["folderPath"].split("/")[-1] - name = prepare_scene_name(asset_name, product_name) + name = prepare_scene_name(folder_name, product_name) if self.create_as_asset_group: # Create instance as empty instance_node = bpy.data.objects.new(name=name, object_data=None) @@ -312,9 +312,9 @@ class BaseCreator(Creator): "productName" in changes.changed_keys or "folderPath" in changes.changed_keys ) and created_instance.product_type != "workfile": - asset_name = data["folderPath"].split("/")[-1] + folder_name = data["folderPath"].split("/")[-1] name = prepare_scene_name( - asset_name, data["productName"] + folder_name, data["productName"] ) node.name = name @@ -465,7 +465,7 @@ class AssetLoader(LoaderPlugin): filepath = self.filepath_from_context(context) assert Path(filepath).exists(), f"{filepath} doesn't exist." - folder_name = context["asset"]["name"] + folder_name = context["folder"]["name"] product_name = context["subset"]["name"] unique_number = get_unique_number( folder_name, product_name @@ -498,7 +498,7 @@ class AssetLoader(LoaderPlugin): # loader=self.__class__.__name__, # ) - # folder_name = context["asset"]["name"] + # folder_name = context["folder"]["name"] # product_name = context["subset"]["name"] # instance_name = prepare_scene_name( # folder_name, product_name, unique_number diff --git a/client/ayon_core/hosts/blender/plugins/create/create_workfile.py b/client/ayon_core/hosts/blender/plugins/create/create_workfile.py index 29a03cb369..296a03e317 100644 --- a/client/ayon_core/hosts/blender/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/blender/plugins/create/create_workfile.py @@ -1,7 +1,7 @@ import bpy +import ayon_api from ayon_core.pipeline import CreatedInstance, AutoCreator -from ayon_core.client import get_asset_by_name from ayon_core.hosts.blender.api.plugin import BaseCreator from ayon_core.hosts.blender.api.pipeline import ( AVALON_PROPERTY, @@ -33,33 +33,38 @@ class CreateWorkfile(BaseCreator, AutoCreator): ) project_name = self.project_name - asset_name = self.create_context.get_current_folder_path() + folder_path = self.create_context.get_current_folder_path() task_name = self.create_context.get_current_task_name() host_name = self.create_context.host_name - existing_asset_name = None + existing_folder_path = None if workfile_instance is not None: - existing_asset_name = workfile_instance.get("folderPath") + existing_folder_path = workfile_instance.get("folderPath") if not workfile_instance: - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, task_name, host_name, ) data = { - "folderPath": asset_name, + "folderPath": folder_path, "task": task_name, "variant": task_name, } data.update( self.get_dynamic_data( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, task_name, host_name, workfile_instance, @@ -72,20 +77,25 @@ class CreateWorkfile(BaseCreator, AutoCreator): self._add_instance_to_context(workfile_instance) elif ( - existing_asset_name != asset_name + existing_folder_path != folder_path or workfile_instance["task"] != task_name ): # Update instance context if it's different - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, self.default_variant, host_name, ) - workfile_instance["folderPath"] = asset_name + workfile_instance["folderPath"] = folder_path workfile_instance["task"] = task_name workfile_instance["productName"] = product_name diff --git a/client/ayon_core/hosts/blender/plugins/load/import_workfile.py b/client/ayon_core/hosts/blender/plugins/load/import_workfile.py index 5a801da848..d919dff9b3 100644 --- a/client/ayon_core/hosts/blender/plugins/load/import_workfile.py +++ b/client/ayon_core/hosts/blender/plugins/load/import_workfile.py @@ -4,8 +4,8 @@ from ayon_core.hosts.blender.api import plugin def append_workfile(context, fname, do_import): - folder_name = context['asset']['name'] - product_name = context['subset']['name'] + folder_name = context["folder"]["name"] + product_name = context["subset"]["name"] group_name = plugin.prepare_scene_name(folder_name, product_name) diff --git a/client/ayon_core/hosts/blender/plugins/load/load_abc.py b/client/ayon_core/hosts/blender/plugins/load/load_abc.py index 3fc879f9c8..6225d404eb 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_abc.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_abc.py @@ -134,7 +134,7 @@ class CacheModelLoader(plugin.AssetLoader): """ libpath = self.filepath_from_context(context) - folder_name = context["asset"]["name"] + folder_name = context["folder"]["name"] product_name = context["subset"]["name"] asset_name = plugin.prepare_scene_name(folder_name, product_name) diff --git a/client/ayon_core/hosts/blender/plugins/load/load_action.py b/client/ayon_core/hosts/blender/plugins/load/load_action.py index df7ffe439d..be61c4f20b 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_action.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_action.py @@ -44,7 +44,7 @@ class BlendActionLoader(plugin.AssetLoader): """ libpath = self.filepath_from_context(context) - folder_name = context["asset"]["name"] + folder_name = context["folder"]["name"] product_name = context["subset"]["name"] lib_container = plugin.prepare_scene_name(folder_name, product_name) container_name = plugin.prepare_scene_name( diff --git a/client/ayon_core/hosts/blender/plugins/load/load_audio.py b/client/ayon_core/hosts/blender/plugins/load/load_audio.py index 85d5277d40..d745c51f5c 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_audio.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_audio.py @@ -39,7 +39,7 @@ class AudioLoader(plugin.AssetLoader): options: Additional settings dictionary """ libpath = self.filepath_from_context(context) - folder_name = context["asset"]["name"] + folder_name = context["folder"]["name"] product_name = context["subset"]["name"] asset_name = plugin.prepare_scene_name(folder_name, product_name) diff --git a/client/ayon_core/hosts/blender/plugins/load/load_blend.py b/client/ayon_core/hosts/blender/plugins/load/load_blend.py index fdae9c1b6b..acbe6c485d 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_blend.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_blend.py @@ -127,7 +127,7 @@ class BlendLoader(plugin.AssetLoader): options: Additional settings dictionary """ libpath = self.filepath_from_context(context) - folder_name = context["asset"]["name"] + folder_name = context["folder"]["name"] product_name = context["subset"]["name"] try: diff --git a/client/ayon_core/hosts/blender/plugins/load/load_blendscene.py b/client/ayon_core/hosts/blender/plugins/load/load_blendscene.py index 52ecdd6a0a..2a0a3313c1 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_blendscene.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_blendscene.py @@ -82,7 +82,7 @@ class BlendSceneLoader(plugin.AssetLoader): options: Additional settings dictionary """ libpath = self.filepath_from_context(context) - folder_name = context["asset"]["name"] + folder_name = context["folder"]["name"] product_name = context["subset"]["name"] try: diff --git a/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py b/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py index da90f0b1ab..ff84231f27 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py @@ -84,7 +84,7 @@ class AbcCameraLoader(plugin.AssetLoader): libpath = self.filepath_from_context(context) - folder_name = context["asset"]["name"] + folder_name = context["folder"]["name"] product_name = context["subset"]["name"] asset_name = plugin.prepare_scene_name(folder_name, product_name) diff --git a/client/ayon_core/hosts/blender/plugins/load/load_camera_fbx.py b/client/ayon_core/hosts/blender/plugins/load/load_camera_fbx.py index 2024356e70..4ba7b65b9d 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_camera_fbx.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_camera_fbx.py @@ -87,7 +87,7 @@ class FbxCameraLoader(plugin.AssetLoader): options: Additional settings dictionary """ libpath = self.filepath_from_context(context) - folder_name = context["asset"]["name"] + folder_name = context["folder"]["name"] product_name = context["subset"]["name"] asset_name = plugin.prepare_scene_name(folder_name, product_name) diff --git a/client/ayon_core/hosts/blender/plugins/load/load_fbx.py b/client/ayon_core/hosts/blender/plugins/load/load_fbx.py index 7b4acfed9a..7feeec528c 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_fbx.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_fbx.py @@ -131,7 +131,7 @@ class FbxModelLoader(plugin.AssetLoader): options: Additional settings dictionary """ libpath = self.filepath_from_context(context) - folder_name = context["asset"]["name"] + folder_name = context["folder"]["name"] product_name = context["subset"]["name"] asset_name = plugin.prepare_scene_name(folder_name, product_name) diff --git a/client/ayon_core/hosts/blender/plugins/load/load_layout_json.py b/client/ayon_core/hosts/blender/plugins/load/load_layout_json.py index 84793775e5..5381a7b3b9 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_layout_json.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_layout_json.py @@ -148,7 +148,7 @@ class JsonLayoutLoader(plugin.AssetLoader): options: Additional settings dictionary """ libpath = self.filepath_from_context(context) - folder_name = context["asset"]["name"] + folder_name = context["folder"]["name"] product_name = context["subset"]["name"] asset_name = plugin.prepare_scene_name(folder_name, product_name) diff --git a/client/ayon_core/hosts/blender/plugins/load/load_look.py b/client/ayon_core/hosts/blender/plugins/load/load_look.py index 59896e0ae0..0e79be5720 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_look.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_look.py @@ -93,7 +93,7 @@ class BlendLookLoader(plugin.AssetLoader): """ libpath = self.filepath_from_context(context) - folder_name = context["asset"]["name"] + folder_name = context["folder"]["name"] product_name = context["subset"]["name"] lib_container = plugin.prepare_scene_name( diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_abc.py b/client/ayon_core/hosts/blender/plugins/publish/extract_abc.py index cf753637ea..094f88fd8c 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_abc.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_abc.py @@ -19,7 +19,7 @@ class ExtractABC(publish.Extractor, publish.OptionalPyblishPluginMixin): # Define extract output file path stagingdir = self.staging_dir(instance) - folder_name = instance.data["assetEntity"]["name"] + folder_name = instance.data["folderEntity"]["name"] product_name = instance.data["productName"] instance_name = f"{folder_name}_{product_name}" filename = f"{instance_name}.abc" diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_abc_animation.py b/client/ayon_core/hosts/blender/plugins/publish/extract_abc_animation.py index 0086dccd67..f33af13282 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_abc_animation.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_abc_animation.py @@ -23,7 +23,7 @@ class ExtractAnimationABC( # Define extract output file path stagingdir = self.staging_dir(instance) - folder_name = instance.data["assetEntity"]["name"] + folder_name = instance.data["folderEntity"]["name"] product_name = instance.data["productName"] instance_name = f"{folder_name}_{product_name}" filename = f"{instance_name}.abc" diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_blend.py b/client/ayon_core/hosts/blender/plugins/publish/extract_blend.py index dd2e33df80..bc9ccbf584 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_blend.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_blend.py @@ -20,7 +20,7 @@ class ExtractBlend(publish.Extractor, publish.OptionalPyblishPluginMixin): # Define extract output file path stagingdir = self.staging_dir(instance) - folder_name = instance.data["assetEntity"]["name"] + folder_name = instance.data["folderEntity"]["name"] product_name = instance.data["productName"] instance_name = f"{folder_name}_{product_name}" filename = f"{instance_name}.blend" diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_blend_animation.py b/client/ayon_core/hosts/blender/plugins/publish/extract_blend_animation.py index da663b46ea..98bdee2015 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_blend_animation.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_blend_animation.py @@ -23,7 +23,7 @@ class ExtractBlendAnimation( # Define extract output file path stagingdir = self.staging_dir(instance) - folder_name = instance.data["assetEntity"]["name"] + folder_name = instance.data["folderEntity"]["name"] product_name = instance.data["productName"] instance_name = f"{folder_name}_{product_name}" filename = f"{instance_name}.blend" diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_camera_abc.py b/client/ayon_core/hosts/blender/plugins/publish/extract_camera_abc.py index ff14d70696..cc783e552c 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_camera_abc.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_camera_abc.py @@ -21,7 +21,7 @@ class ExtractCameraABC(publish.Extractor, publish.OptionalPyblishPluginMixin): # Define extract output file path stagingdir = self.staging_dir(instance) - folder_name = instance.data["assetEntity"]["name"] + folder_name = instance.data["folderEntity"]["name"] product_name = instance.data["productName"] instance_name = f"{folder_name}_{product_name}" filename = f"{instance_name}.abc" diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_camera_fbx.py b/client/ayon_core/hosts/blender/plugins/publish/extract_camera_fbx.py index 03059f1e13..bcaf9ebc44 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_camera_fbx.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_camera_fbx.py @@ -20,7 +20,7 @@ class ExtractCamera(publish.Extractor, publish.OptionalPyblishPluginMixin): # Define extract output file path stagingdir = self.staging_dir(instance) - folder_name = instance.data["assetEntity"]["name"] + folder_name = instance.data["folderEntity"]["name"] product_name = instance.data["productName"] instance_name = f"{folder_name}_{product_name}" filename = f"{instance_name}.fbx" diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_fbx.py b/client/ayon_core/hosts/blender/plugins/publish/extract_fbx.py index 8fea077e7c..7ebda2c4cd 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_fbx.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_fbx.py @@ -21,7 +21,7 @@ class ExtractFBX(publish.Extractor, publish.OptionalPyblishPluginMixin): # Define extract output file path stagingdir = self.staging_dir(instance) - folder_name = instance.data["assetEntity"]["name"] + folder_name = instance.data["folderEntity"]["name"] product_name = instance.data["productName"] instance_name = f"{folder_name}_{product_name}" filename = f"{instance_name}.fbx" diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_fbx_animation.py b/client/ayon_core/hosts/blender/plugins/publish/extract_fbx_animation.py index b98167c741..ae02909152 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_fbx_animation.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_fbx_animation.py @@ -145,7 +145,7 @@ class ExtractAnimationFBX( root.select_set(True) armature.select_set(True) - folder_name = instance.data["assetEntity"]["name"] + folder_name = instance.data["folderEntity"]["name"] product_name = instance.data["productName"] instance_name = f"{folder_name}_{product_name}" fbx_filename = f"{instance_name}_{armature.name}.fbx" diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_layout.py b/client/ayon_core/hosts/blender/plugins/publish/extract_layout.py index 16c0392070..6787ab7916 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_layout.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_layout.py @@ -228,7 +228,7 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin): json_data.append(json_element) - folder_name = instance.data["assetEntity"]["name"] + folder_name = instance.data["folderEntity"]["name"] product_name = instance.data["productName"] instance_name = f"{folder_name}_{product_name}" json_filename = f"{instance_name}.json" diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_playblast.py b/client/ayon_core/hosts/blender/plugins/publish/extract_playblast.py index acb09d0d77..ce6f40f967 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_playblast.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_playblast.py @@ -55,7 +55,7 @@ class ExtractPlayblast(publish.Extractor, publish.OptionalPyblishPluginMixin): # get output path stagingdir = self.staging_dir(instance) - folder_name = instance.data["assetEntity"]["name"] + folder_name = instance.data["folderEntity"]["name"] product_name = instance.data["productName"] filename = f"{folder_name}_{product_name}" diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_thumbnail.py b/client/ayon_core/hosts/blender/plugins/publish/extract_thumbnail.py index 89168fb9c9..4330c57d99 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_thumbnail.py @@ -32,7 +32,7 @@ class ExtractThumbnail(publish.Extractor): return stagingdir = self.staging_dir(instance) - folder_name = instance.data["assetEntity"]["name"] + folder_name = instance.data["folderEntity"]["name"] product_name = instance.data["productName"] filename = f"{folder_name}_{product_name}" From bf4a0a357934989dfd543e780670e308fa84f244 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 15:47:56 +0100 Subject: [PATCH 405/573] celaction use folder entity --- .../publish/collect_celaction_instances.py | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/client/ayon_core/hosts/celaction/plugins/publish/collect_celaction_instances.py b/client/ayon_core/hosts/celaction/plugins/publish/collect_celaction_instances.py index 4306a53bfe..7c22201e3e 100644 --- a/client/ayon_core/hosts/celaction/plugins/publish/collect_celaction_instances.py +++ b/client/ayon_core/hosts/celaction/plugins/publish/collect_celaction_instances.py @@ -1,8 +1,6 @@ import os import pyblish.api -from ayon_core.client import get_asset_name_identifier - class CollectCelactionInstances(pyblish.api.ContextPlugin): """ Adds the celaction render instances """ @@ -16,24 +14,20 @@ class CollectCelactionInstances(pyblish.api.ContextPlugin): staging_dir = os.path.dirname(current_file) scene_file = os.path.basename(current_file) version = context.data["version"] - asset_entity = context.data["assetEntity"] - project_entity = context.data["projectEntity"] - asset_name = get_asset_name_identifier(asset_entity) + folder_entity = context.data["folderEntity"] + + folder_attributes = folder_entity["attrib"] shared_instance_data = { - "folderPath": asset_name, - "frameStart": asset_entity["data"]["frameStart"], - "frameEnd": asset_entity["data"]["frameEnd"], - "handleStart": asset_entity["data"]["handleStart"], - "handleEnd": asset_entity["data"]["handleEnd"], - "fps": asset_entity["data"]["fps"], - "resolutionWidth": asset_entity["data"].get( - "resolutionWidth", - project_entity["data"]["resolutionWidth"]), - "resolutionHeight": asset_entity["data"].get( - "resolutionHeight", - project_entity["data"]["resolutionHeight"]), + "folderPath": folder_entity["path"], + "frameStart": folder_attributes["frameStart"], + "frameEnd": folder_attributes["frameEnd"], + "handleStart": folder_attributes["handleStart"], + "handleEnd": folder_attributes["handleEnd"], + "fps": folder_attributes["fps"], + "resolutionWidth": folder_attributes["resolutionWidth"], + "resolutionHeight": folder_attributes["resolutionHeight"], "pixelAspect": 1, "step": 1, "version": version @@ -83,7 +77,7 @@ class CollectCelactionInstances(pyblish.api.ContextPlugin): # getting instance state instance.data["publish"] = True - # add assetEntity data into instance + # add folderEntity data into instance instance.data.update({ "label": "{} - farm".format(product_name), "productType": product_type, From 122ee169193a7303cfd5dcb4025547bc7ba6f253 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 15:48:41 +0100 Subject: [PATCH 406/573] flame uses folde and task entities --- .../hosts/flame/plugins/load/load_clip_batch.py | 6 +++--- .../flame/plugins/publish/collect_timeline_otio.py | 11 +++-------- .../flame/plugins/publish/integrate_batch_group.py | 11 ++++++----- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/client/ayon_core/hosts/flame/plugins/load/load_clip_batch.py b/client/ayon_core/hosts/flame/plugins/load/load_clip_batch.py index 9f81103cb4..2ad1ec6b30 100644 --- a/client/ayon_core/hosts/flame/plugins/load/load_clip_batch.py +++ b/client/ayon_core/hosts/flame/plugins/load/load_clip_batch.py @@ -59,14 +59,14 @@ class LoadClipBatch(opfapi.ClipLoader): layer_rename_template = layer_rename_template.replace( "output", "representation") - asset_doc = context["asset"] + folder_entity = context["folder"] subset_doc = context["subset"] formatting_data = deepcopy(context["representation"]["context"]) formatting_data["batch"] = self.batch.name.get_value() formatting_data.update({ - "asset": asset_doc["name"], + "asset": folder_entity["name"], "folder": { - "name": asset_doc["name"], + "name": folder_entity["name"], }, "subset": subset_doc["name"], "family": subset_doc["data"]["family"], diff --git a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_otio.py b/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_otio.py index 6d04d53cea..9c06a4c76b 100644 --- a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_otio.py +++ b/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_otio.py @@ -1,6 +1,5 @@ import pyblish.api -from ayon_core.client import get_asset_name_identifier import ayon_core.hosts.flame.api as opfapi from ayon_core.hosts.flame.otio import flame_export from ayon_core.pipeline.create import get_product_name @@ -18,31 +17,27 @@ class CollecTimelineOTIO(pyblish.api.ContextPlugin): variant = "otioTimeline" # main - asset_doc = context.data["assetEntity"] - task_name = context.data["task"] + folder_entity = context.data["folderEntity"] project = opfapi.get_current_project() sequence = opfapi.get_current_sequence(opfapi.CTX.selection) # create product name product_name = get_product_name( context.data["projectName"], - asset_doc, - task_name, + context.data["taskEntity"], context.data["hostName"], product_type, variant, project_settings=context.data["project_settings"] ) - folder_path = get_asset_name_identifier(asset_doc) - # adding otio timeline to context with opfapi.maintained_segment_selection(sequence) as selected_seg: otio_timeline = flame_export.create_otio_timeline(sequence) instance_data = { "name": product_name, - "folderPath": folder_path, + "folderPath": folder_entity["path"], "productName": product_name, "productType": product_type, "family": product_type, diff --git a/client/ayon_core/hosts/flame/plugins/publish/integrate_batch_group.py b/client/ayon_core/hosts/flame/plugins/publish/integrate_batch_group.py index 66b2af7d2b..d8669f836d 100644 --- a/client/ayon_core/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/client/ayon_core/hosts/flame/plugins/publish/integrate_batch_group.py @@ -247,7 +247,7 @@ class IntegrateBatchGroup(pyblish.api.InstancePlugin): os.makedirs(render_dir_path, mode=0o777) # TODO: add most of these to `imageio/flame/batch/write_node` - name = "{project[code]}_{asset}_{task[name]}".format( + name = "{project[code]}_{folder[name]}_{task[name]}".format( **anatomy_data ) @@ -322,15 +322,16 @@ class IntegrateBatchGroup(pyblish.api.InstancePlugin): def _get_shot_task_dir_path(self, instance, task_data): project_entity = instance.data["projectEntity"] - asset_entity = instance.data["assetEntity"] + folder_entity = instance.data["folderEntity"] + task_entity = instance.data["taskEntity"] anatomy = instance.context.data["anatomy"] project_settings = instance.context.data["project_settings"] return get_workdir( project_entity, - asset_entity, - task_data["name"], + folder_entity, + task_entity, "flame", - anatomy, + anatomy=anatomy, project_settings=project_settings ) From 1b5d6f9001f9e6cd1a1ff30e1f228b2f189c7518 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 15:49:51 +0100 Subject: [PATCH 407/573] fusion use folder and task entities --- client/ayon_core/hosts/fusion/api/__init__.py | 4 +- client/ayon_core/hosts/fusion/api/lib.py | 71 ++++++++++--------- client/ayon_core/hosts/fusion/api/menu.py | 20 +++--- .../fusion/plugins/create/create_saver.py | 6 +- .../fusion/plugins/create/create_workfile.py | 45 +++++++----- .../hosts/fusion/plugins/load/load_alembic.py | 4 +- .../hosts/fusion/plugins/load/load_fbx.py | 4 +- .../fusion/plugins/load/load_sequence.py | 4 +- .../hosts/fusion/plugins/load/load_usd.py | 4 +- .../plugins/publish/collect_instances.py | 4 +- .../plugins/publish/extract_render_local.py | 8 +-- .../publish/validate_saver_resolution.py | 10 +-- .../publish/validate_unique_subsets.py | 2 +- 13 files changed, 99 insertions(+), 87 deletions(-) diff --git a/client/ayon_core/hosts/fusion/api/__init__.py b/client/ayon_core/hosts/fusion/api/__init__.py index aabc624016..ddd718e606 100644 --- a/client/ayon_core/hosts/fusion/api/__init__.py +++ b/client/ayon_core/hosts/fusion/api/__init__.py @@ -9,7 +9,7 @@ from .pipeline import ( from .lib import ( maintained_selection, update_frame_range, - set_asset_framerange, + set_current_context_framerange, get_current_comp, get_bmd_library, comp_lock_and_undo_chunk @@ -29,7 +29,7 @@ __all__ = [ # lib "maintained_selection", "update_frame_range", - "set_asset_framerange", + "set_current_context_framerange", "get_current_comp", "get_bmd_library", "comp_lock_and_undo_chunk", diff --git a/client/ayon_core/hosts/fusion/api/lib.py b/client/ayon_core/hosts/fusion/api/lib.py index 47cfb284f7..e5bf4b5a44 100644 --- a/client/ayon_core/hosts/fusion/api/lib.py +++ b/client/ayon_core/hosts/fusion/api/lib.py @@ -5,7 +5,7 @@ import contextlib from ayon_core.lib import Logger -from ayon_core.pipeline.context_tools import get_current_project_asset +from ayon_core.pipeline.context_tools import get_current_project_folder self = sys.modules[__name__] self._project = None @@ -52,23 +52,25 @@ def update_frame_range(start, end, comp=None, set_render_range=True, comp.SetAttrs(attrs) -def set_asset_framerange(): - """Set Comp's frame range based on current asset""" - asset_doc = get_current_project_asset() - start = asset_doc["data"]["frameStart"] - end = asset_doc["data"]["frameEnd"] - handle_start = asset_doc["data"]["handleStart"] - handle_end = asset_doc["data"]["handleEnd"] +def set_current_context_framerange(): + """Set Comp's frame range based on current folder.""" + folder_entity = get_current_project_folder() + folder_attributes = folder_entity["attrib"] + start = folder_attributes["frameStart"] + end = folder_attributes["frameEnd"] + handle_start = folder_attributes["handleStart"] + handle_end = folder_attributes["handleEnd"] update_frame_range(start, end, set_render_range=True, handle_start=handle_start, handle_end=handle_end) -def set_asset_resolution(): - """Set Comp's resolution width x height default based on current asset""" - asset_doc = get_current_project_asset() - width = asset_doc["data"]["resolutionWidth"] - height = asset_doc["data"]["resolutionHeight"] +def set_current_context_resolution(): + """Set Comp's resolution width x height default based on current folder""" + folder_entity = get_current_project_folder() + folder_attributes = folder_entity["attrib"] + width = folder_attributes["resolutionWidth"] + height = folder_attributes["resolutionHeight"] comp = get_current_comp() print("Setting comp frame format resolution to {}x{}".format(width, @@ -80,7 +82,7 @@ def set_asset_resolution(): def validate_comp_prefs(comp=None, force_repair=False): - """Validate current comp defaults with asset settings. + """Validate current comp defaults with folder settings. Validates fps, resolutionWidth, resolutionHeight, aspectRatio. @@ -92,22 +94,23 @@ def validate_comp_prefs(comp=None, force_repair=False): log = Logger.get_logger("validate_comp_prefs") - fields = [ - "name", - "data.fps", - "data.resolutionWidth", - "data.resolutionHeight", - "data.pixelAspect" - ] - asset_doc = get_current_project_asset(fields=fields) - asset_data = asset_doc["data"] + fields = { + "path", + "attrib.fps", + "attrib.resolutionWidth", + "attrib.resolutionHeight", + "attrib.pixelAspect", + } + folder_entity = get_current_project_folder(fields=fields) + folder_path = folder_entity["path"] + folder_attributes = folder_entity["attrib"] comp_frame_format_prefs = comp.GetPrefs("Comp.FrameFormat") # Pixel aspect ratio in Fusion is set as AspectX and AspectY so we convert # the data to something that is more sensible to Fusion - asset_data["pixelAspectX"] = asset_data.pop("pixelAspect") - asset_data["pixelAspectY"] = 1.0 + folder_attributes["pixelAspectX"] = folder_attributes.pop("pixelAspect") + folder_attributes["pixelAspectY"] = 1.0 validations = [ ("fps", "Rate", "FPS"), @@ -119,23 +122,23 @@ def validate_comp_prefs(comp=None, force_repair=False): invalid = [] for key, comp_key, label in validations: - asset_value = asset_data[key] + folder_value = folder_attributes[key] comp_value = comp_frame_format_prefs.get(comp_key) - if asset_value != comp_value: + if folder_value != comp_value: invalid_msg = "{} {} should be {}".format(label, comp_value, - asset_value) + folder_value) invalid.append(invalid_msg) if not force_repair: # Do not log warning if we force repair anyway log.warning( - "Comp {pref} {value} does not match asset " - "'{asset_name}' {pref} {asset_value}".format( + "Comp {pref} {value} does not match folder " + "'{folder_path}' {pref} {folder_value}".format( pref=label, value=comp_value, - asset_name=asset_doc["name"], - asset_value=asset_value) + folder_path=folder_path, + folder_value=folder_value) ) if invalid: @@ -143,7 +146,7 @@ def validate_comp_prefs(comp=None, force_repair=False): def _on_repair(): attributes = dict() for key, comp_key, _label in validations: - value = asset_data[key] + value = folder_value[key] comp_key_full = "Comp.FrameFormat.{}".format(comp_key) attributes[comp_key_full] = value comp.SetPrefs(attributes) @@ -159,7 +162,7 @@ def validate_comp_prefs(comp=None, force_repair=False): dialog = SimplePopup(parent=menu.menu) dialog.setWindowTitle("Fusion comp has invalid configuration") - msg = "Comp preferences mismatches '{}'".format(asset_doc["name"]) + msg = "Comp preferences mismatches '{}'".format(folder_path) msg += "\n" + "\n".join(invalid) dialog.set_message(msg) dialog.set_button_text("Repair") diff --git a/client/ayon_core/hosts/fusion/api/menu.py b/client/ayon_core/hosts/fusion/api/menu.py index 2f6a1fa808..642287eb10 100644 --- a/client/ayon_core/hosts/fusion/api/menu.py +++ b/client/ayon_core/hosts/fusion/api/menu.py @@ -10,8 +10,8 @@ from ayon_core.hosts.fusion.scripts import ( duplicate_with_inputs, ) from ayon_core.hosts.fusion.api.lib import ( - set_asset_framerange, - set_asset_resolution, + set_current_context_framerange, + set_current_context_resolution, ) from ayon_core.pipeline import get_current_folder_path from ayon_core.resources import get_ayon_icon_filepath @@ -49,15 +49,15 @@ class OpenPypeMenu(QtWidgets.QWidget): self.render_mode_widget = None self.setWindowTitle(MENU_LABEL) - asset_label = QtWidgets.QLabel("Context", self) - asset_label.setStyleSheet( + context_label = QtWidgets.QLabel("Context", self) + context_label.setStyleSheet( """QLabel { font-size: 14px; font-weight: 600; color: #5f9fb8; }""" ) - asset_label.setAlignment(QtCore.Qt.AlignHCenter) + context_label.setAlignment(QtCore.Qt.AlignHCenter) workfiles_btn = QtWidgets.QPushButton("Workfiles...", self) create_btn = QtWidgets.QPushButton("Create...", self) @@ -74,7 +74,7 @@ class OpenPypeMenu(QtWidgets.QWidget): layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(10, 20, 10, 20) - layout.addWidget(asset_label) + layout.addWidget(context_label) layout.addSpacing(20) @@ -103,7 +103,7 @@ class OpenPypeMenu(QtWidgets.QWidget): self.setLayout(layout) # Store reference so we can update the label - self.asset_label = asset_label + self.context_label = context_label workfiles_btn.clicked.connect(self.on_workfile_clicked) create_btn.clicked.connect(self.on_create_clicked) @@ -132,7 +132,7 @@ class OpenPypeMenu(QtWidgets.QWidget): def on_task_changed(self): # Update current context label label = get_current_folder_path() - self.asset_label.setText(label) + self.context_label.setText(label) def register_callback(self, name, fn): # Create a wrapper callback that we only store @@ -168,10 +168,10 @@ class OpenPypeMenu(QtWidgets.QWidget): duplicate_with_inputs.duplicate_with_input_connections() def on_set_resolution_clicked(self): - set_asset_resolution() + set_current_context_resolution() def on_set_framerange_clicked(self): - set_asset_framerange() + set_current_context_framerange() def launch_openpype_menu(): diff --git a/client/ayon_core/hosts/fusion/plugins/create/create_saver.py b/client/ayon_core/hosts/fusion/plugins/create/create_saver.py index b5abb2d949..b6cda1f302 100644 --- a/client/ayon_core/hosts/fusion/plugins/create/create_saver.py +++ b/client/ayon_core/hosts/fusion/plugins/create/create_saver.py @@ -15,7 +15,7 @@ class CreateSaver(GenericCreateSaver): product_type = "render" description = "Fusion Saver to generate image sequence" - default_frame_range_option = "asset_db" + default_frame_range_option = "current_folder" def get_detail_description(self): return """Fusion Saver to generate image sequence. @@ -24,7 +24,7 @@ class CreateSaver(GenericCreateSaver): product type. (But can publish even single frame 'render'.) Select what should be source of render range: - - "Current asset context" - values set on Asset in DB (Ftrack) + - "Current Folder context" - values set on folder on AYON server - "From render in/out" - from node itself - "From composition timeline" - from timeline @@ -50,7 +50,7 @@ class CreateSaver(GenericCreateSaver): def _get_frame_range_enum(self): frame_range_options = { - "asset_db": "Current asset context", + "current_folder": "Current Folder context", "render_range": "From render in/out", "comp_range": "From composition timeline", } diff --git a/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py b/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py index 53e2a0ff62..a2fe027ef4 100644 --- a/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py @@ -1,7 +1,8 @@ +import ayon_api + from ayon_core.hosts.fusion.api import ( get_current_comp ) -from ayon_core.client import get_asset_by_name from ayon_core.pipeline import ( AutoCreator, CreatedInstance, @@ -54,7 +55,6 @@ class FusionWorkfileCreator(AutoCreator): comp.SetData(self.data_key, data) def create(self, options=None): - comp = get_current_comp() if not comp: self.log.error("Unable to find current comp") @@ -67,33 +67,37 @@ class FusionWorkfileCreator(AutoCreator): break project_name = self.create_context.get_current_project_name() - asset_name = self.create_context.get_current_folder_path() + folder_path = self.create_context.get_current_folder_path() task_name = self.create_context.get_current_task_name() host_name = self.create_context.host_name - if existing_instance is None: - existing_instance_asset = None - else: - existing_instance_asset = existing_instance["folderPath"] + existing_folder_path = None + if existing_instance is not None: + existing_folder_path = existing_instance["folderPath"] if existing_instance is None: - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, self.default_variant, host_name, ) data = { - "folderPath": asset_name, + "folderPath": folder_path, "task": task_name, "variant": self.default_variant, } data.update(self.get_dynamic_data( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, self.default_variant, host_name, None @@ -107,17 +111,22 @@ class FusionWorkfileCreator(AutoCreator): self._add_instance_to_context(new_instance) elif ( - existing_instance_asset != asset_name + existing_folder_path != folder_path or existing_instance["task"] != task_name ): - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, self.default_variant, host_name, ) - existing_instance["folderPath"] = asset_name + existing_instance["folderPath"] = folder_path existing_instance["task"] = task_name existing_instance["productName"] = product_name diff --git a/client/ayon_core/hosts/fusion/plugins/load/load_alembic.py b/client/ayon_core/hosts/fusion/plugins/load/load_alembic.py index 17f043bb34..b0c45ad0e9 100644 --- a/client/ayon_core/hosts/fusion/plugins/load/load_alembic.py +++ b/client/ayon_core/hosts/fusion/plugins/load/load_alembic.py @@ -24,9 +24,9 @@ class FusionLoadAlembicMesh(load.LoaderPlugin): tool_type = "SurfaceAlembicMesh" def load(self, context, name, namespace, data): - # Fallback to asset name when namespace is None + # Fallback to folder name when namespace is None if namespace is None: - namespace = context['asset']['name'] + namespace = context["folder"]["name"] # Create the Loader with the filename path set comp = get_current_comp() diff --git a/client/ayon_core/hosts/fusion/plugins/load/load_fbx.py b/client/ayon_core/hosts/fusion/plugins/load/load_fbx.py index 75320431a8..04fe90f72e 100644 --- a/client/ayon_core/hosts/fusion/plugins/load/load_fbx.py +++ b/client/ayon_core/hosts/fusion/plugins/load/load_fbx.py @@ -38,9 +38,9 @@ class FusionLoadFBXMesh(load.LoaderPlugin): tool_type = "SurfaceFBXMesh" def load(self, context, name, namespace, data): - # Fallback to asset name when namespace is None + # Fallback to folder name when namespace is None if namespace is None: - namespace = context["asset"]["name"] + namespace = context["folder"]["name"] # Create the Loader with the filename path set comp = get_current_comp() diff --git a/client/ayon_core/hosts/fusion/plugins/load/load_sequence.py b/client/ayon_core/hosts/fusion/plugins/load/load_sequence.py index 678da54ad6..d42e0feda9 100644 --- a/client/ayon_core/hosts/fusion/plugins/load/load_sequence.py +++ b/client/ayon_core/hosts/fusion/plugins/load/load_sequence.py @@ -149,9 +149,9 @@ class FusionLoadSequence(load.LoaderPlugin): color = "orange" def load(self, context, name, namespace, data): - # Fallback to asset name when namespace is None + # Fallback to folder name when namespace is None if namespace is None: - namespace = context["asset"]["name"] + namespace = context["folder"]["name"] # Use the first file for now path = self.filepath_from_context(context) diff --git a/client/ayon_core/hosts/fusion/plugins/load/load_usd.py b/client/ayon_core/hosts/fusion/plugins/load/load_usd.py index e315c84713..16af9505d5 100644 --- a/client/ayon_core/hosts/fusion/plugins/load/load_usd.py +++ b/client/ayon_core/hosts/fusion/plugins/load/load_usd.py @@ -40,9 +40,9 @@ class FusionLoadUSD(load.LoaderPlugin): cls.enabled = is_usd_supported def load(self, context, name, namespace, data): - # Fallback to asset name when namespace is None + # Fallback to folder name when namespace is None if namespace is None: - namespace = context['asset']['name'] + namespace = context["folder"]["name"] # Create the Loader with the filename path set comp = get_current_comp() diff --git a/client/ayon_core/hosts/fusion/plugins/publish/collect_instances.py b/client/ayon_core/hosts/fusion/plugins/publish/collect_instances.py index 2cbd4d82f4..51d7e68fb6 100644 --- a/client/ayon_core/hosts/fusion/plugins/publish/collect_instances.py +++ b/client/ayon_core/hosts/fusion/plugins/publish/collect_instances.py @@ -25,8 +25,8 @@ class CollectInstanceData(pyblish.api.InstancePlugin): frame_range_source = creator_attributes.get("frame_range_source") instance.data["frame_range_source"] = frame_range_source - # get asset frame ranges to all instances - # render product type instances `asset_db` render target + # get folder frame ranges to all instances + # render product type instances `current_folder` render target start = context.data["frameStart"] end = context.data["frameEnd"] handle_start = context.data["handleStart"] diff --git a/client/ayon_core/hosts/fusion/plugins/publish/extract_render_local.py b/client/ayon_core/hosts/fusion/plugins/publish/extract_render_local.py index 23a8cdb8a0..39fa20cfc0 100644 --- a/client/ayon_core/hosts/fusion/plugins/publish/extract_render_local.py +++ b/client/ayon_core/hosts/fusion/plugins/publish/extract_render_local.py @@ -70,10 +70,10 @@ class FusionRenderLocal( # Log render status self.log.info( - "Rendered '{nm}' for asset '{ast}' under the task '{tsk}'".format( - nm=instance.data["name"], - ast=instance.data["folderPath"], - tsk=instance.data["task"], + "Rendered '{}' for folder '{}' under the task '{}'".format( + instance.data["name"], + instance.data["folderPath"], + instance.data["task"], ) ) diff --git a/client/ayon_core/hosts/fusion/plugins/publish/validate_saver_resolution.py b/client/ayon_core/hosts/fusion/plugins/publish/validate_saver_resolution.py index af8d4f41fa..17992b123c 100644 --- a/client/ayon_core/hosts/fusion/plugins/publish/validate_saver_resolution.py +++ b/client/ayon_core/hosts/fusion/plugins/publish/validate_saver_resolution.py @@ -11,10 +11,10 @@ from ayon_core.hosts.fusion.api import comp_lock_and_undo_chunk class ValidateSaverResolution( pyblish.api.InstancePlugin, OptionalPyblishPluginMixin ): - """Validate that the saver input resolution matches the asset resolution""" + """Validate that the saver input resolution matches the folder resolution""" order = pyblish.api.ValidatorOrder - label = "Validate Asset Resolution" + label = "Validate Folder Resolution" families = ["render", "image"] hosts = ["fusion"] optional = True @@ -29,7 +29,7 @@ class ValidateSaverResolution( if resolution != expected_resolution: raise PublishValidationError( "The input's resolution does not match " - "the asset's resolution {}x{}.\n\n" + "the folder's resolution {}x{}.\n\n" "The input's resolution is {}x{}.".format( expected_resolution[0], expected_resolution[1], resolution[0], resolution[1] @@ -55,8 +55,8 @@ class ValidateSaverResolution( @classmethod def get_expected_resolution(cls, instance): - data = instance.data["assetEntity"]["data"] - return data["resolutionWidth"], data["resolutionHeight"] + attributes = instance.data["folderEntity"]["attrib"] + return attributes["resolutionWidth"], attributes["resolutionHeight"] @classmethod def get_tool_resolution(cls, tool, frame): diff --git a/client/ayon_core/hosts/fusion/plugins/publish/validate_unique_subsets.py b/client/ayon_core/hosts/fusion/plugins/publish/validate_unique_subsets.py index 939ddbd117..d64a29286d 100644 --- a/client/ayon_core/hosts/fusion/plugins/publish/validate_unique_subsets.py +++ b/client/ayon_core/hosts/fusion/plugins/publish/validate_unique_subsets.py @@ -27,7 +27,7 @@ class ValidateUniqueSubsets(pyblish.api.ContextPlugin): instance ) - # Find which asset + subset combination has more than one instance + # Find which folder + subset combination has more than one instance # Those are considered invalid because they'd integrate to the same # destination. invalid = [] From a5589d313ad697b9bc6628ad7131c0b2d0dd3c95 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 15:50:35 +0100 Subject: [PATCH 408/573] harminy use folder and task entity --- .../ayon_core/hosts/harmony/api/__init__.py | 4 +-- .../ayon_core/hosts/harmony/api/pipeline.py | 29 ++++++++++--------- .../ayon_core/hosts/harmony/js/PypeHarmony.js | 2 +- .../harmony/js/loaders/ImageSequenceLoader.js | 6 ++-- .../harmony/js/loaders/TemplateLoader.js | 6 ++-- .../plugins/load/load_imagesequence.py | 6 ++-- .../harmony/plugins/load/load_template.py | 2 +- .../plugins/publish/collect_workfile.py | 3 +- .../publish/help/validate_instances.xml | 8 ++--- .../publish/help/validate_scene_settings.xml | 6 ++-- .../plugins/publish/validate_instances.py | 17 ++++++----- .../publish/validate_scene_settings.py | 22 +++++++------- 12 files changed, 56 insertions(+), 55 deletions(-) diff --git a/client/ayon_core/hosts/harmony/api/__init__.py b/client/ayon_core/hosts/harmony/api/__init__.py index d890215f63..bdcc43ea2f 100644 --- a/client/ayon_core/hosts/harmony/api/__init__.py +++ b/client/ayon_core/hosts/harmony/api/__init__.py @@ -11,7 +11,7 @@ from .pipeline import ( select_instance, containerise, set_scene_settings, - get_asset_settings, + get_current_context_settings, ensure_scene_settings, check_inventory, application_launch, @@ -55,7 +55,7 @@ __all__ = [ "select_instance", "containerise", "set_scene_settings", - "get_asset_settings", + "get_current_context_settings", "ensure_scene_settings", "check_inventory", "application_launch", diff --git a/client/ayon_core/hosts/harmony/api/pipeline.py b/client/ayon_core/hosts/harmony/api/pipeline.py index 863053dddc..aaff5dc019 100644 --- a/client/ayon_core/hosts/harmony/api/pipeline.py +++ b/client/ayon_core/hosts/harmony/api/pipeline.py @@ -13,7 +13,7 @@ from ayon_core.pipeline import ( AVALON_CONTAINER_ID, ) from ayon_core.pipeline.load import get_outdated_containers -from ayon_core.pipeline.context_tools import get_current_project_asset +from ayon_core.pipeline.context_tools import get_current_project_folder from ayon_core.hosts.harmony import HARMONY_HOST_DIR import ayon_core.hosts.harmony.api as harmony @@ -42,24 +42,25 @@ def set_scene_settings(settings): {"function": "PypeHarmony.setSceneSettings", "args": settings}) -def get_asset_settings(): - """Get settings on current asset from database. +def get_current_context_settings(): + """Get settings on current folder from server. Returns: dict: Scene data. """ - asset_doc = get_current_project_asset() - asset_data = asset_doc["data"] - fps = asset_data.get("fps") - frame_start = asset_data.get("frameStart") - frame_end = asset_data.get("frameEnd") - handle_start = asset_data.get("handleStart") - handle_end = asset_data.get("handleEnd") - resolution_width = asset_data.get("resolutionWidth") - resolution_height = asset_data.get("resolutionHeight") - entity_type = asset_data.get("entityType") + folder_entity = get_current_project_folder() + folder_attributes = folder_entity["attrib"] + + fps = folder_attributes.get("fps") + frame_start = folder_attributes.get("frameStart") + frame_end = folder_attributes.get("frameEnd") + handle_start = folder_attributes.get("handleStart") + handle_end = folder_attributes.get("handleEnd") + resolution_width = folder_attributes.get("resolutionWidth") + resolution_height = folder_attributes.get("resolutionHeight") + entity_type = folder_attributes.get("entityType") scene_data = { "fps": fps, @@ -77,7 +78,7 @@ def get_asset_settings(): def ensure_scene_settings(): """Validate if Harmony scene has valid settings.""" - settings = get_asset_settings() + settings = get_current_context_settings() invalid_settings = [] valid_settings = {} diff --git a/client/ayon_core/hosts/harmony/js/PypeHarmony.js b/client/ayon_core/hosts/harmony/js/PypeHarmony.js index 41c8dc56ce..cf6a5e3763 100644 --- a/client/ayon_core/hosts/harmony/js/PypeHarmony.js +++ b/client/ayon_core/hosts/harmony/js/PypeHarmony.js @@ -34,7 +34,7 @@ PypeHarmony.message = function(message) { /** - * Set scene setting based on shot/asset settngs. + * Set scene setting based on folder settngs. * @function * @param {obj} settings Scene settings. */ diff --git a/client/ayon_core/hosts/harmony/js/loaders/ImageSequenceLoader.js b/client/ayon_core/hosts/harmony/js/loaders/ImageSequenceLoader.js index ebbd7163f9..a2ca5f9a99 100644 --- a/client/ayon_core/hosts/harmony/js/loaders/ImageSequenceLoader.js +++ b/client/ayon_core/hosts/harmony/js/loaders/ImageSequenceLoader.js @@ -87,7 +87,7 @@ ImageSequenceLoader.getUniqueColumnName = function(columnPrefix) { * // Arguments are in following order: * var args = [ * files, // Files in file sequences. - * asset, // Asset name. + * folderName, // Folder name. * productName, // Product name. * startFrame, // Sequence starting frame. * groupId // Unique group ID (uuid4). @@ -105,7 +105,7 @@ ImageSequenceLoader.prototype.importFiles = function(args) { var doc = $.scn; var files = args[0]; - var asset = args[1]; + var folderName = args[1]; var productName = args[2]; var startFrame = args[3]; var groupId = args[4]; @@ -124,7 +124,7 @@ ImageSequenceLoader.prototype.importFiles = function(args) { var num = 0; var name = ''; do { - name = asset + '_' + (num++) + '_' + productName; + name = folderName + '_' + (num++) + '_' + productName; } while (currentGroup.getNodeByName(name) != null); extension = filename.substr(pos+1).toLowerCase(); diff --git a/client/ayon_core/hosts/harmony/js/loaders/TemplateLoader.js b/client/ayon_core/hosts/harmony/js/loaders/TemplateLoader.js index 78167fcb39..c29e12c43b 100644 --- a/client/ayon_core/hosts/harmony/js/loaders/TemplateLoader.js +++ b/client/ayon_core/hosts/harmony/js/loaders/TemplateLoader.js @@ -30,7 +30,7 @@ var TemplateLoader = function() {}; * // arguments are in following order: * var args = [ * templatePath, // Path to tpl file. - * assetName, // Asset name. + * folderName, // Folder name. * productName, // Product name. * groupId // unique ID (uuid4) * ]; @@ -38,7 +38,7 @@ var TemplateLoader = function() {}; TemplateLoader.prototype.loadContainer = function(args) { var doc = $.scn; var templatePath = args[0]; - var assetName = args[1]; + var folderName = args[1]; var productName = args[2]; var groupId = args[3]; @@ -62,7 +62,7 @@ TemplateLoader.prototype.loadContainer = function(args) { var num = 0; var containerGroupName = ''; do { - containerGroupName = assetName + '_' + (num++) + '_' + productName; + containerGroupName = folderName + '_' + (num++) + '_' + productName; } while (currentGroup.getNodeByName(containerGroupName) != null); // import the template diff --git a/client/ayon_core/hosts/harmony/plugins/load/load_imagesequence.py b/client/ayon_core/hosts/harmony/plugins/load/load_imagesequence.py index 60b90fe42d..473fbf5f17 100644 --- a/client/ayon_core/hosts/harmony/plugins/load/load_imagesequence.py +++ b/client/ayon_core/hosts/harmony/plugins/load/load_imagesequence.py @@ -46,7 +46,7 @@ class ImageSequenceLoader(load.LoaderPlugin): else: files.append(fname.parent.joinpath(remainder[0]).as_posix()) - asset = context["asset"]["name"] + folder_name = context["folder"]["name"] product_name = context["subset"]["name"] group_id = str(uuid.uuid4()) @@ -55,7 +55,7 @@ class ImageSequenceLoader(load.LoaderPlugin): "function": f"PypeHarmony.Loaders.{self_name}.importFiles", # noqa: E501 "args": [ files, - asset, + folder_name, product_name, 1, group_id @@ -64,7 +64,7 @@ class ImageSequenceLoader(load.LoaderPlugin): )["result"] return harmony.containerise( - f"{asset}_{product_name}", + f"{folder_name}_{product_name}", namespace, read_node, context, diff --git a/client/ayon_core/hosts/harmony/plugins/load/load_template.py b/client/ayon_core/hosts/harmony/plugins/load/load_template.py index e981340c68..f26b09fb42 100644 --- a/client/ayon_core/hosts/harmony/plugins/load/load_template.py +++ b/client/ayon_core/hosts/harmony/plugins/load/load_template.py @@ -52,7 +52,7 @@ class TemplateLoader(load.LoaderPlugin): { "function": f"PypeHarmony.Loaders.{self_name}.loadContainer", "args": [template_path, - context["asset"]["name"], + context["folder"]["name"], context["subset"]["name"], group_id] } diff --git a/client/ayon_core/hosts/harmony/plugins/publish/collect_workfile.py b/client/ayon_core/hosts/harmony/plugins/publish/collect_workfile.py index b1010cfb57..79356b3a5f 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/collect_workfile.py +++ b/client/ayon_core/hosts/harmony/plugins/publish/collect_workfile.py @@ -19,8 +19,7 @@ class CollectWorkfile(pyblish.api.ContextPlugin): basename = os.path.basename(context.data["currentFile"]) product_name = get_product_name( context.data["projectName"], - context.data["assetEntity"], - context.data["task"], + context.data["taskEntity"], context.data["hostName"], product_type, "", diff --git a/client/ayon_core/hosts/harmony/plugins/publish/help/validate_instances.xml b/client/ayon_core/hosts/harmony/plugins/publish/help/validate_instances.xml index 67ad7e2d21..072faf6030 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/help/validate_instances.xml +++ b/client/ayon_core/hosts/harmony/plugins/publish/help/validate_instances.xml @@ -5,21 +5,21 @@ ## Invalid product context -Asset name found '{found}' in products, expected '{expected}'. +Folder path found '{found}' in products, 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. +You can fix this with `Repair` button on the right. This will use '{expected}' folder path and overwrite '{found}' folder path 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. +If this is unwanted, close workfile and open again, that way different folder 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 product "renderCompositingDefault" from asset "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing product for "Robot" asset stayed in the workfile.) +(Eg. you created product "renderCompositingDefault" from folder "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing product for "Robot" folder stayed in the workfile.) \ No newline at end of file diff --git a/client/ayon_core/hosts/harmony/plugins/publish/help/validate_scene_settings.xml b/client/ayon_core/hosts/harmony/plugins/publish/help/validate_scene_settings.xml index 36fa90456e..b645a97cb2 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/help/validate_scene_settings.xml +++ b/client/ayon_core/hosts/harmony/plugins/publish/help/validate_scene_settings.xml @@ -5,18 +5,18 @@ ## Invalid scene setting found -One of the settings in a scene doesn't match to asset settings in database. +One of the settings in a scene doesn't match to folder attributes on server. {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. +Change values for {invalid_keys_str} in the scene OR change them on the folder 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. +This error is shown when for example resolution in the scene doesn't match to resolution set on the folder on the server. Either value in the database or in the scene is wrong. diff --git a/client/ayon_core/hosts/harmony/plugins/publish/validate_instances.py b/client/ayon_core/hosts/harmony/plugins/publish/validate_instances.py index fa3d2e22cb..1200f6266b 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/validate_instances.py +++ b/client/ayon_core/hosts/harmony/plugins/publish/validate_instances.py @@ -35,7 +35,7 @@ class ValidateInstanceRepair(pyblish.api.Action): class ValidateInstance(pyblish.api.InstancePlugin): - """Validate the instance asset is the current asset.""" + """Validate the instance folder is the current folder.""" label = "Validate Instance" hosts = ["harmony"] @@ -43,17 +43,18 @@ class ValidateInstance(pyblish.api.InstancePlugin): order = ValidateContentsOrder def process(self, instance): - instance_asset = instance.data["folderPath"] - current_asset = get_current_folder_path() + instance_folder_path = instance.data["folderPath"] + current_colder_path = get_current_folder_path() msg = ( - "Instance asset is not the same as current asset:" - f"\nInstance: {instance_asset}\nCurrent: {current_asset}" + "Instance folder is not the same as current folder:" + f"\nInstance: {instance_folder_path}]" + f"\nCurrent: {current_colder_path}" ) formatting_data = { - "found": instance_asset, - "expected": current_asset + "found": instance_folder_path, + "expected": current_colder_path } - if instance_asset != current_asset: + if instance_folder_path != current_colder_path: raise PublishXmlValidationError(self, msg, formatting_data=formatting_data) diff --git a/client/ayon_core/hosts/harmony/plugins/publish/validate_scene_settings.py b/client/ayon_core/hosts/harmony/plugins/publish/validate_scene_settings.py index 6d46fbcd33..dc3db3b544 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/validate_scene_settings.py +++ b/client/ayon_core/hosts/harmony/plugins/publish/validate_scene_settings.py @@ -19,12 +19,12 @@ class ValidateSceneSettingsRepair(pyblish.api.Action): def process(self, context, plugin): """Repair action entry point.""" - expected = harmony.get_asset_settings() - asset_settings = _update_frames(dict.copy(expected)) - asset_settings["frameStart"] = 1 - asset_settings["frameEnd"] = asset_settings["frameEnd"] + \ - asset_settings["handleEnd"] - harmony.set_scene_settings(asset_settings) + expected = harmony.get_current_context_settings() + expected_settings = _update_frames(dict.copy(expected)) + expected_settings["frameStart"] = 1 + expected_settings["frameEnd"] = expected_settings["frameEnd"] + \ + expected_settings["handleEnd"] + harmony.set_scene_settings(expected_settings) if not os.path.exists(context.data["scenePath"]): self.log.info("correcting scene name") scene_dir = os.path.dirname(context.data["currentFile"]) @@ -56,10 +56,10 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin): def process(self, instance): """Plugin entry point.""" - # TODO 'get_asset_settings' could expect asset document as argument - # which is available on 'context.data["assetEntity"]' + # TODO 'get_current_context_settings' could expect folder entity + # as an argument which is available on 'context.data["folderEntity"]' # - the same approach can be used in 'ValidateSceneSettingsRepair' - expected_settings = harmony.get_asset_settings() + expected_settings = harmony.get_current_context_settings() self.log.info("scene settings from DB:{}".format(expected_settings)) expected_settings.pop("entityType") # not useful for the validation @@ -87,8 +87,8 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin): expected_settings.pop('frameStartHandle', None) expected_settings.pop('frameEndHandle', None) - asset_name = instance.context.data['anatomyData']['asset'] - if any(re.search(pattern, asset_name) + folder_name = instance.context.data["folderPath"].rsplit("/", 1)[-1] + if any(re.search(pattern, folder_name) for pattern in self.frame_check_filter): self.log.info("Skipping frames check because of " "task name and pattern {}".format( From 321e9ff96c8a859370affa26e9ed784f526b9ce7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 15:53:49 +0100 Subject: [PATCH 409/573] hiero use folder and task entities --- client/ayon_core/hosts/hiero/api/lib.py | 2 +- client/ayon_core/hosts/hiero/api/plugin.py | 33 +++++++------------ .../hosts/hiero/plugins/load/load_effects.py | 8 ++--- .../publish/collect_frame_tag_instances.py | 5 +-- .../plugins/publish/precollect_instances.py | 12 +++---- 5 files changed, 24 insertions(+), 36 deletions(-) diff --git a/client/ayon_core/hosts/hiero/api/lib.py b/client/ayon_core/hosts/hiero/api/lib.py index abf3690f9e..c46269b532 100644 --- a/client/ayon_core/hosts/hiero/api/lib.py +++ b/client/ayon_core/hosts/hiero/api/lib.py @@ -588,7 +588,7 @@ def imprint(track_item, data=None): Examples: data = { - 'asset': 'sq020sh0280', + 'folderPath': '/shots/sq020sh0280', 'productType': 'render', 'productName': 'productMain' } diff --git a/client/ayon_core/hosts/hiero/api/plugin.py b/client/ayon_core/hosts/hiero/api/plugin.py index 51601a7ee0..d2c0310f1e 100644 --- a/client/ayon_core/hosts/hiero/api/plugin.py +++ b/client/ayon_core/hosts/hiero/api/plugin.py @@ -409,8 +409,9 @@ class ClipLoader: "Cannot Load selected data, look into database " "or call your supervisor") - # inject asset data to representation dict - self._get_asset_data() + # inject folder data to representation dict + folder_entity = self.context["folder"] + self.data["folderAttributes"] = folder_entity["attrib"] log.info("__init__ self.data: `{}`".format(pformat(self.data))) log.info("__init__ options: `{}`".format(pformat(options))) @@ -424,7 +425,7 @@ class ClipLoader: self.active_sequence = lib.get_current_sequence(new=True) self.active_sequence.setFramerate( hiero.core.TimeBase.fromString( - str(self.data["assetData"]["fps"]))) + str(self.data["folderAttributes"]["fps"]))) else: self.active_sequence = lib.get_current_sequence() @@ -447,7 +448,7 @@ class ClipLoader: # create name repr = self.context["representation"] repr_cntx = repr["context"] - asset = str(repr_cntx["asset"]) + folder_name = str(repr_cntx["asset"]) product_name = str(repr_cntx["subset"]) representation = str(repr_cntx["representation"]) self.data["clip_name"] = self.clip_name_template.format(**repr_cntx) @@ -470,7 +471,7 @@ class ClipLoader: hierarchy = str("/".join(( "Loader", repr_cntx["hierarchy"].replace("\\", "/"), - asset + folder_name ))) self.data["binPath"] = hierarchy @@ -487,16 +488,6 @@ class ClipLoader: file = file.replace(frame, "#" * padding) self.data["path"] = file - def _get_asset_data(self): - """ Get all available asset data - - joint `data` key with asset.data dict into the representation - - """ - - asset_doc = self.context["asset"] - self.data["assetData"] = asset_doc["data"] - def _make_track_item(self, source_bin_item, audio=False): """ Create track item with """ @@ -533,9 +524,9 @@ class ClipLoader: self.handle_start = self.data["versionData"].get("handleStart") self.handle_end = self.data["versionData"].get("handleEnd") if self.handle_start is None: - self.handle_start = self.data["assetData"]["handleStart"] + self.handle_start = self.data["folderAttributes"]["handleStart"] if self.handle_end is None: - self.handle_end = self.data["assetData"]["handleEnd"] + self.handle_end = self.data["folderAttributes"]["handleEnd"] self.handle_start = int(self.handle_start) self.handle_end = int(self.handle_end) @@ -552,11 +543,11 @@ class ClipLoader: last_timeline_out = int(last_track_item.timelineOut()) + 1 self.timeline_in = last_timeline_out self.timeline_out = last_timeline_out + int( - self.data["assetData"]["clipOut"] - - self.data["assetData"]["clipIn"]) + self.data["folderAttributes"]["clipOut"] + - self.data["folderAttributes"]["clipIn"]) else: - self.timeline_in = int(self.data["assetData"]["clipIn"]) - self.timeline_out = int(self.data["assetData"]["clipOut"]) + self.timeline_in = int(self.data["folderAttributes"]["clipIn"]) + self.timeline_out = int(self.data["folderAttributes"]["clipOut"]) log.debug("__ self.timeline_in: {}".format(self.timeline_in)) log.debug("__ self.timeline_out: {}".format(self.timeline_out)) diff --git a/client/ayon_core/hosts/hiero/plugins/load/load_effects.py b/client/ayon_core/hosts/hiero/plugins/load/load_effects.py index c72f1bdc64..c6ac20bb04 100644 --- a/client/ayon_core/hosts/hiero/plugins/load/load_effects.py +++ b/client/ayon_core/hosts/hiero/plugins/load/load_effects.py @@ -35,7 +35,7 @@ class LoadEffects(load.LoaderPlugin): Arguments: context (dict): context of version name (str): name of the version - namespace (str): asset name + namespace (str): Folder name. data (dict): compulsory attribute > not used Returns: @@ -46,10 +46,10 @@ class LoadEffects(load.LoaderPlugin): active_sequence, "Loaded_{}".format(name)) # get main variables - namespace = namespace or context["asset"]["name"] + namespace = namespace or context["folder"]["name"] object_name = "{}_{}".format(name, namespace) - clip_in = context["asset"]["data"]["clipIn"] - clip_out = context["asset"]["data"]["clipOut"] + clip_in = context["folder"]["attrib"]["clipIn"] + clip_out = context["folder"]["attrib"]["clipOut"] data_imprint = { "objectName": object_name, diff --git a/client/ayon_core/hosts/hiero/plugins/publish/collect_frame_tag_instances.py b/client/ayon_core/hosts/hiero/plugins/publish/collect_frame_tag_instances.py index d73b5d4667..0e5d849b78 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/collect_frame_tag_instances.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/collect_frame_tag_instances.py @@ -5,8 +5,6 @@ import json import pyblish.api -from ayon_core.client import get_asset_name_identifier - class CollectFrameTagInstances(pyblish.api.ContextPlugin): """Collect frames from tags. @@ -104,8 +102,7 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin): # first collect all available product tag frames product_data = {} - context_asset_doc = context.data["assetEntity"] - context_folder_path = get_asset_name_identifier(context_asset_doc) + context_folder_path = context.data["folderEntity"]["path"] for tag_data in sequence_tags: frame = int(tag_data["start"]) diff --git a/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py b/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py index 911b96c280..d921f37934 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py @@ -85,7 +85,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin): if k not in ("id", "applieswhole", "label") }) - asset, asset_name = self._get_asset_data(tag_data) + asset, asset_name = self._get_folder_data(tag_data) product_name = tag_data.get("productName") if product_name is None: @@ -242,13 +242,13 @@ class PrecollectInstances(pyblish.api.ContextPlugin): self.log.debug( "_ instance.data: {}".format(pformat(instance.data))) - def _get_asset_data(self, data): + def _get_folder_data(self, data): folder_path = data.pop("folderPath", None) if data.get("asset_name"): - asset_name = data["asset_name"] + folder_name = data["asset_name"] else: - asset_name = data["asset"] + folder_name = data["asset"] # backward compatibility for clip tags # which are missing folderPath key @@ -257,10 +257,10 @@ class PrecollectInstances(pyblish.api.ContextPlugin): hierarchy_path = data["hierarchy"] folder_path = "/{}/{}".format( hierarchy_path, - asset_name + folder_name ) - return folder_path, asset_name + return folder_path, folder_name def create_audio_instance(self, context, **data): product_name = "audioMain" From 3c880d5e66937bbeec51b1313f56cd2c5191f52d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 16:10:57 +0100 Subject: [PATCH 410/573] houdini use folder and task entity --- .../hosts/houdini/api/creator_node_shelves.py | 19 ++- client/ayon_core/hosts/houdini/api/lib.py | 143 ++++++++++-------- .../ayon_core/hosts/houdini/api/pipeline.py | 6 +- client/ayon_core/hosts/houdini/api/plugin.py | 8 +- client/ayon_core/hosts/houdini/api/shelves.py | 4 +- .../houdini/plugins/create/create_hda.py | 19 ++- .../plugins/create/create_staticmesh.py | 17 ++- .../houdini/plugins/create/create_workfile.py | 41 +++-- .../inventory/set_camera_resolution.py | 6 +- .../houdini/plugins/load/load_alembic.py | 2 +- .../plugins/load/load_alembic_archive.py | 4 +- .../hosts/houdini/plugins/load/load_ass.py | 2 +- .../hosts/houdini/plugins/load/load_bgeo.py | 2 +- .../hosts/houdini/plugins/load/load_camera.py | 6 +- .../hosts/houdini/plugins/load/load_fbx.py | 2 +- .../hosts/houdini/plugins/load/load_hda.py | 2 +- .../hosts/houdini/plugins/load/load_image.py | 2 +- .../plugins/load/load_redshift_proxy.py | 2 +- .../houdini/plugins/load/load_usd_layer.py | 2 +- .../plugins/load/load_usd_reference.py | 2 +- .../hosts/houdini/plugins/load/load_vdb.py | 2 +- .../plugins/publish/collect_asset_handles.py | 14 +- .../plugins/publish/collect_instances.py | 2 +- .../publish/collect_instances_usd_layered.py | 14 +- .../plugins/publish/collect_usd_bootstrap.py | 33 ++-- .../plugins/publish/extract_usd_layered.py | 8 +- .../plugins/publish/validate_frame_range.py | 18 +-- .../plugins/publish/validate_subset_name.py | 13 +- .../validate_unreal_staticmesh_naming.py | 2 +- .../validate_usd_shade_model_exists.py | 24 ++- .../publish/validate_usd_shade_workspace.py | 2 +- .../hosts/houdini/startup/MainMenuCommon.xml | 2 +- 32 files changed, 239 insertions(+), 186 deletions(-) diff --git a/client/ayon_core/hosts/houdini/api/creator_node_shelves.py b/client/ayon_core/hosts/houdini/api/creator_node_shelves.py index 273d532d5a..6e48cb375b 100644 --- a/client/ayon_core/hosts/houdini/api/creator_node_shelves.py +++ b/client/ayon_core/hosts/houdini/api/creator_node_shelves.py @@ -12,7 +12,8 @@ import tempfile import logging import os -from ayon_core.client import get_asset_by_name +import ayon_api + from ayon_core.pipeline import registered_host from ayon_core.pipeline.create import CreateContext from ayon_core.resources import get_ayon_icon_filepath @@ -90,13 +91,19 @@ def create_interactive(creator_identifier, **kwargs): pane = stateutils.activePane(kwargs) if isinstance(pane, hou.NetworkEditor): pwd = pane.pwd() + project_name = context.get_current_project_name(), + folder_path = context.get_current_folder_path() + task_name = context.get_current_task_name() + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = creator.get_product_name( project_name=context.get_current_project_name(), - asset_doc=get_asset_by_name( - project_name=context.get_current_project_name(), - asset_name=context.get_current_folder_path() - ), - task_name=context.get_current_task_name(), + folder_entity=folder_entity, + task_entity=task_entity, variant=variant, host_name=context.host_name, ) diff --git a/client/ayon_core/hosts/houdini/api/lib.py b/client/ayon_core/hosts/houdini/api/lib.py index fc1808b225..c3b527b9fb 100644 --- a/client/ayon_core/hosts/houdini/api/lib.py +++ b/client/ayon_core/hosts/houdini/api/lib.py @@ -12,7 +12,6 @@ import six import ayon_api from ayon_core.lib import StringTemplate -from ayon_core.client import get_asset_by_name from ayon_core.settings import get_current_project_settings from ayon_core.pipeline import ( Anatomy, @@ -24,7 +23,7 @@ from ayon_core.pipeline import ( ) from ayon_core.pipeline.create import CreateContext from ayon_core.pipeline.template_data import get_template_data -from ayon_core.pipeline.context_tools import get_current_project_asset +from ayon_core.pipeline.context_tools import get_current_project_folder from ayon_core.tools.utils import PopupUpdateKeys, SimplePopup from ayon_core.tools.utils.host_tools import get_tool_by_name @@ -37,12 +36,12 @@ log = logging.getLogger(__name__) JSON_PREFIX = "JSON:::" -def get_asset_fps(asset_doc=None): - """Return current asset fps.""" +def get_folder_fps(folder_entity=None): + """Return current folder fps.""" - if asset_doc is None: - asset_doc = get_current_project_asset(fields=["data.fps"]) - return asset_doc["data"]["fps"] + if folder_entity is None: + folder_entity = get_current_project_folder(fields=["attrib.fps"]) + return folder_entity["attrib"]["fps"] def set_id(node, unique_id, overwrite=False): @@ -69,7 +68,7 @@ def get_id(node): return node.parm("id") -def generate_ids(nodes, asset_id=None): +def generate_ids(nodes, folder_id=None): """Returns new unique ids for the given nodes. Note: This does not assign the new ids, it only generates the values. @@ -86,28 +85,29 @@ def generate_ids(nodes, asset_id=None): Args: nodes (list): List of nodes. - asset_id (str or bson.ObjectId): The database id for the *asset* to - generate for. When None provided the current asset in the - active session is used. + folder_id (str): Folder id . Use current folder id if is ``None``. Returns: list: A list of (node, id) tuples. """ - if asset_id is None: + if folder_id is None: project_name = get_current_project_name() - asset_name = get_current_folder_path() - # Get the asset ID from the database for the asset of current context - asset_doc = get_asset_by_name(project_name, asset_name, fields=["_id"]) + folder_path = get_current_folder_path() + # Get folder id of current context folder + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path, fields={"id"} + ) + if not folder_entity: + raise ValueError("No current folder is set.") - assert asset_doc, "No current asset found in Session" - asset_id = asset_doc['_id'] + folder_id = folder_entity["id"] node_ids = [] for node in nodes: _, uid = str(uuid.uuid4()).rsplit("-", 1) - unique_id = "{}:{}".format(asset_id, uid) + unique_id = "{}:{}".format(folder_id, uid) node_ids.append((node, unique_id)) return node_ids @@ -200,7 +200,7 @@ def validate_fps(): """ - fps = get_asset_fps() + fps = get_folder_fps() current_fps = hou.fps() # returns float if current_fps != fps: @@ -527,28 +527,27 @@ def maintained_selection(): def reset_framerange(): - """Set frame range and FPS to current asset""" + """Set frame range and FPS to current folder.""" - # Get asset data project_name = get_current_project_name() - asset_name = get_current_folder_path() - # Get the asset ID from the database for the asset of current context - asset_doc = get_asset_by_name(project_name, asset_name) - asset_data = asset_doc["data"] + folder_path = get_current_folder_path() + + folder_entity = ayon_api.get_folder_by_path(project_name, folder_path) + folder_attributes = folder_entity["attrib"] # Get FPS - fps = get_asset_fps(asset_doc) + fps = get_folder_fps(folder_entity) # Get Start and End Frames - frame_start = asset_data.get("frameStart") - frame_end = asset_data.get("frameEnd") + frame_start = folder_attributes.get("frameStart") + frame_end = folder_attributes.get("frameEnd") if frame_start is None or frame_end is None: - log.warning("No edit information found for %s" % asset_name) + log.warning("No edit information found for '{}'".format(folder_path)) return - handle_start = asset_data.get("handleStart", 0) - handle_end = asset_data.get("handleEnd", 0) + handle_start = folder_attributes.get("handleStart", 0) + handle_end = folder_attributes.get("handleEnd", 0) frame_start -= int(handle_start) frame_end += int(handle_end) @@ -642,7 +641,7 @@ def get_frame_data(node, log=None): log.info( "Node '{}' has 'Render current frame' set.\n" - "Asset Handles are ignored.\n" + "Folder Handles are ignored.\n" "frameStart and frameEnd are set to the " "current frame.".format(node.path()) ) @@ -781,31 +780,43 @@ def get_output_children(output_node, include_sops=True): return out_list -def get_resolution_from_doc(doc): - """Get resolution from the given asset document. """ +def get_resolution_from_folder(folder_entity): + """Get resolution from the given folder entity. - if not doc or "data" not in doc: - print("Entered document is not valid. \"{}\"".format(str(doc))) + Args: + folder_entity (dict[str, Any]): Folder entity. + + Returns: + Union[Tuple[int, int], None]: Resolution width and height. + + """ + if not folder_entity or "attrib" not in folder_entity: + print("Entered folder is not valid. \"{}\"".format( + str(folder_entity) + )) return None - resolution_width = doc["data"].get("resolutionWidth") - resolution_height = doc["data"].get("resolutionHeight") + folder_attributes = folder_entity["attrib"] + resolution_width = folder_attributes.get("resolutionWidth") + resolution_height = folder_attributes.get("resolutionHeight") # Make sure both width and height are set if resolution_width is None or resolution_height is None: - print("No resolution information found for \"{}\"".format(doc["name"])) + print("No resolution information found for '{}'".format( + folder_entity["path"] + )) return None return int(resolution_width), int(resolution_height) -def set_camera_resolution(camera, asset_doc=None): - """Apply resolution to camera from asset document of the publish""" +def set_camera_resolution(camera, folder_entity=None): + """Apply resolution to camera from folder entity of the publish""" - if not asset_doc: - asset_doc = get_current_project_asset() + if not folder_entity: + folder_entity = get_current_project_folder() - resolution = get_resolution_from_doc(asset_doc) + resolution = get_resolution_from_folder(folder_entity) if resolution: print("Setting camera resolution: {} -> {}x{}".format( @@ -828,41 +839,47 @@ def get_camera_from_container(container): return cameras[0] -def get_current_context_template_data_with_asset_data(): - """ - TODOs: - Support both 'assetData' and 'folderData' in future. +def get_current_context_template_data_with_folder_attrs(): """ + Output contains 'folderAttributes' key with folder attribute values. + + Returns: + dict[str, Any]: Template data to fill templates. + + """ context = get_current_context() project_name = context["project_name"] - asset_name = context["folder_path"] + folder_path = context["folder_path"] task_name = context["task_name"] host_name = get_current_host_name() project_entity = ayon_api.get_project(project_name) anatomy = Anatomy(project_name, project_entity=project_entity) - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path(project_name, folder_path) + task_entity = ayon_api.get_task_by_name( + project_name, folder_path["id"], task_name + ) # get context specific vars - asset_data = asset_doc["data"] + folder_attributes = folder_entity["attrib"] # compute `frameStartHandle` and `frameEndHandle` - frame_start = asset_data.get("frameStart") - frame_end = asset_data.get("frameEnd") - handle_start = asset_data.get("handleStart") - handle_end = asset_data.get("handleEnd") + frame_start = folder_attributes.get("frameStart") + frame_end = folder_attributes.get("frameEnd") + handle_start = folder_attributes.get("handleStart") + handle_end = folder_attributes.get("handleEnd") if frame_start is not None and handle_start is not None: - asset_data["frameStartHandle"] = frame_start - handle_start + folder_attributes["frameStartHandle"] = frame_start - handle_start if frame_end is not None and handle_end is not None: - asset_data["frameEndHandle"] = frame_end + handle_end + folder_attributes["frameEndHandle"] = frame_end + handle_end template_data = get_template_data( - project_entity, asset_doc, task_name, host_name + project_entity, folder_entity, task_entity, host_name ) template_data["root"] = anatomy.roots - template_data["assetData"] = asset_data + template_data["folderAttributes"] = folder_attributes return template_data @@ -886,7 +903,7 @@ def get_context_var_changes(): return houdini_vars_to_update # Get Template data - template_data = get_current_context_template_data_with_asset_data() + template_data = get_current_context_template_data_with_folder_attrs() # Set Houdini Vars for item in houdini_vars: @@ -918,7 +935,7 @@ def get_context_var_changes(): def update_houdini_vars_context(): - """Update asset context variables""" + """Update folder context variables""" for var, (_old, new, is_directory) in get_context_var_changes().items(): if is_directory: @@ -937,7 +954,7 @@ def update_houdini_vars_context(): def update_houdini_vars_context_dialog(): - """Show pop-up to update asset context variables""" + """Show pop-up to update folder context variables""" update_vars = get_context_var_changes() if not update_vars: # Nothing to change @@ -953,7 +970,7 @@ def update_houdini_vars_context_dialog(): parent = hou.ui.mainQtWindow() dialog = SimplePopup(parent=parent) dialog.setModal(True) - dialog.setWindowTitle("Houdini scene has outdated asset variables") + dialog.setWindowTitle("Houdini scene has outdated folder variables") dialog.set_message(message) dialog.set_button_text("Fix") diff --git a/client/ayon_core/hosts/houdini/api/pipeline.py b/client/ayon_core/hosts/houdini/api/pipeline.py index cbc94a2408..4b0580c5af 100644 --- a/client/ayon_core/hosts/houdini/api/pipeline.py +++ b/client/ayon_core/hosts/houdini/api/pipeline.py @@ -68,7 +68,7 @@ class HoudiniHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): self._has_been_setup = True - # Set asset settings for the empty scene directly after launch of + # Set folder settings for the empty scene directly after launch of # Houdini so it initializes into the correct scene FPS, # Frame Range, etc. # TODO: make sure this doesn't trigger when @@ -338,7 +338,7 @@ def on_open(): lib.update_houdini_vars_context_dialog() # Validate FPS after update_task_from_path to - # ensure it is using correct FPS for the asset + # ensure it is using correct FPS for the folder lib.validate_fps() if any_outdated_containers(): @@ -388,7 +388,7 @@ def on_new(): def _set_context_settings(): """Apply the project settings from the project definition - Settings can be overwritten by an asset if the asset.data contains + Settings can be overwritten by a folder if the folder.attrib contains any information regarding those settings. Examples of settings: diff --git a/client/ayon_core/hosts/houdini/api/plugin.py b/client/ayon_core/hosts/houdini/api/plugin.py index 13cf3c9949..0809f4e566 100644 --- a/client/ayon_core/hosts/houdini/api/plugin.py +++ b/client/ayon_core/hosts/houdini/api/plugin.py @@ -145,13 +145,13 @@ class HoudiniCreatorBase(object): @staticmethod def create_instance_node( - asset_name, node_name, parent, node_type="geometry" + folder_path, node_name, parent, node_type="geometry" ): # type: (str, str, str) -> hou.Node """Create node representing instance. Arguments: - asset_name (str): Asset name. + folder_path (str): Folder path. node_name (str): Name of the new node. parent (str): Name of the parent node. node_type (str, optional): Type of the node. @@ -186,10 +186,10 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase): if node_type is None: node_type = "geometry" - asset_name = instance_data["folderPath"] + folder_path = instance_data["folderPath"] instance_node = self.create_instance_node( - asset_name, product_name, "/out", node_type) + folder_path, product_name, "/out", node_type) self.customize_node_look(instance_node) diff --git a/client/ayon_core/hosts/houdini/api/shelves.py b/client/ayon_core/hosts/houdini/api/shelves.py index b0f5af839e..b178139020 100644 --- a/client/ayon_core/hosts/houdini/api/shelves.py +++ b/client/ayon_core/hosts/houdini/api/shelves.py @@ -10,7 +10,7 @@ from ayon_core.lib import StringTemplate import hou -from .lib import get_current_context_template_data_with_asset_data +from .lib import get_current_context_template_data_with_folder_attrs log = logging.getLogger("ayon_core.hosts.houdini.shelves") @@ -31,7 +31,7 @@ def generate_shelves(): return # Get Template data - template_data = get_current_context_template_data_with_asset_data() + template_data = get_current_context_template_data_with_folder_attrs() for config in shelves_configs: selected_option = config["options"] diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py index 994977de7d..654ddfc6d6 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py @@ -1,9 +1,8 @@ # -*- coding: utf-8 -*- """Creator plugin for creating publishable Houdini Digital Assets.""" -from ayon_core.client import ( - get_asset_by_name, - get_subsets, -) +import ayon_api + +from ayon_core.client import get_subsets from ayon_core.hosts.houdini.api import plugin import hou @@ -17,16 +16,16 @@ class CreateHDA(plugin.HoudiniCreator): icon = "gears" maintain_selection = False - def _check_existing(self, asset_name, product_name): + def _check_existing(self, folder_path, product_name): # type: (str) -> bool """Check if existing product name versions already exists.""" # Get all products of the current folder project_name = self.project_name - asset_doc = get_asset_by_name( - project_name, asset_name, fields=["_id"] + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path, fields={"id"} ) subset_docs = get_subsets( - project_name, asset_ids=[asset_doc["_id"]], fields=["name"] + project_name, asset_ids=[folder_entity["id"]], fields=["name"] ) existing_product_names_low = { subset_doc["name"].lower() @@ -35,7 +34,7 @@ class CreateHDA(plugin.HoudiniCreator): return product_name.lower() in existing_product_names_low def create_instance_node( - self, asset_name, node_name, parent, node_type="geometry" + self, folder_path, node_name, parent, node_type="geometry" ): parent_node = hou.node("/obj") @@ -62,7 +61,7 @@ class CreateHDA(plugin.HoudiniCreator): hda_file_name="$HIP/{}.hda".format(node_name) ) hda_node.layoutChildren() - elif self._check_existing(asset_name, node_name): + elif self._check_existing(folder_path, node_name): raise plugin.OpenPypeCreatorError( ("product {} is already published with different HDA" "definition.").format(node_name)) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_staticmesh.py b/client/ayon_core/hosts/houdini/plugins/create/create_staticmesh.py index bc8a2507cd..3271107c6e 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_staticmesh.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_staticmesh.py @@ -88,16 +88,27 @@ class CreateStaticMesh(plugin.HoudiniCreator): return attrs + [createsubnetroot, vcformat, convert_units] def get_dynamic_data( - self, project_name, asset_doc, task_name, variant, host_name, instance + self, + project_name, + folder_entity, + task_entity, + variant, + host_name, + instance ): """ The default prodcut name templates for Unreal include {asset} and thus we should pass that along as dynamic data. """ dynamic_data = super(CreateStaticMesh, self).get_dynamic_data( - project_name, asset_doc, task_name, variant, host_name, instance + project_name, + folder_entity, + task_entity, + variant, + host_name, + instance ) - dynamic_data["asset"] = asset_doc["name"] + dynamic_data["asset"] = folder_entity["name"] return dynamic_data def get_selection(self): diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py b/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py index 898fe5e5f9..a958509e25 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- """Creator plugin for creating workfiles.""" +import ayon_api + from ayon_core.hosts.houdini.api import plugin from ayon_core.hosts.houdini.api.lib import read, imprint from ayon_core.hosts.houdini.api.pipeline import CONTEXT_CONTAINER from ayon_core.pipeline import CreatedInstance, AutoCreator -from ayon_core.client import get_asset_by_name import hou @@ -26,26 +27,31 @@ class CreateWorkfile(plugin.HoudiniCreatorBase, AutoCreator): ), None) project_name = self.project_name - asset_name = self.create_context.get_current_folder_path() + folder_path = self.create_context.get_current_folder_path() task_name = self.create_context.get_current_task_name() host_name = self.host_name if current_instance is None: - current_instance_asset = None + current_folder_path = None else: - current_instance_asset = current_instance["folderPath"] + current_folder_path = current_instance["folderPath"] if current_instance is None: - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, host_name, ) data = { - "folderPath": asset_name, + "folderPath": folder_path, "task": task_name, "variant": variant, } @@ -53,8 +59,8 @@ class CreateWorkfile(plugin.HoudiniCreatorBase, AutoCreator): data.update( self.get_dynamic_data( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, host_name, current_instance) @@ -65,19 +71,24 @@ class CreateWorkfile(plugin.HoudiniCreatorBase, AutoCreator): ) self._add_instance_to_context(current_instance) elif ( - current_instance_asset != asset_name + current_folder_path != folder_path or current_instance["task"] != task_name ): # Update instance context if is not the same - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, host_name, ) - current_instance["folderPath"] = asset_name + current_instance["folderPath"] = folder_path current_instance["task"] = task_name current_instance["productName"] = product_name diff --git a/client/ayon_core/hosts/houdini/plugins/inventory/set_camera_resolution.py b/client/ayon_core/hosts/houdini/plugins/inventory/set_camera_resolution.py index dadb80469a..b813f82e2e 100644 --- a/client/ayon_core/hosts/houdini/plugins/inventory/set_camera_resolution.py +++ b/client/ayon_core/hosts/houdini/plugins/inventory/set_camera_resolution.py @@ -3,7 +3,7 @@ from ayon_core.hosts.houdini.api.lib import ( get_camera_from_container, set_camera_resolution ) -from ayon_core.pipeline.context_tools import get_current_project_asset +from ayon_core.pipeline.context_tools import get_current_project_folder class SetCameraResolution(InventoryAction): @@ -19,8 +19,8 @@ class SetCameraResolution(InventoryAction): ) def process(self, containers): - asset_doc = get_current_project_asset() + folder_entity = get_current_project_folder() for container in containers: node = container["node"] camera = get_camera_from_container(node) - set_camera_resolution(camera, asset_doc) + set_camera_resolution(camera, folder_entity) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_alembic.py b/client/ayon_core/hosts/houdini/plugins/load/load_alembic.py index 5e138cde83..9ee72a270c 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_alembic.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_alembic.py @@ -28,7 +28,7 @@ class AbcLoader(load.LoaderPlugin): obj = hou.node("/obj") # Define node name - namespace = namespace if namespace else context["asset"]["name"] + namespace = namespace if namespace else context["folder"]["name"] node_name = "{}_{}".format(namespace, name) if namespace else name # Create a new geo node diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_alembic_archive.py b/client/ayon_core/hosts/houdini/plugins/load/load_alembic_archive.py index 0d505806ff..bc65863a76 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_alembic_archive.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_alembic_archive.py @@ -29,14 +29,14 @@ class AbcArchiveLoader(load.LoaderPlugin): obj = hou.node("/obj") # Define node name - namespace = namespace if namespace else context["asset"]["name"] + namespace = namespace if namespace else context["folder"]["name"] node_name = "{}_{}".format(namespace, name) if namespace else name # Create an Alembic archive node node = obj.createNode("alembicarchive", node_name=node_name) node.moveToGoodPosition() - # TODO: add FPS of project / asset + # TODO: add FPS of project / folder node.setParms({"fileName": file_path, "channelRef": True}) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_ass.py b/client/ayon_core/hosts/houdini/plugins/load/load_ass.py index 396eb3a9f7..b687b07199 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_ass.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_ass.py @@ -25,7 +25,7 @@ class AssLoader(load.LoaderPlugin): obj = hou.node("/obj") # Define node name - namespace = namespace if namespace else context["asset"]["name"] + namespace = namespace if namespace else context["folder"]["name"] node_name = "{}_{}".format(namespace, name) if namespace else name # Create a new geo node diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_bgeo.py b/client/ayon_core/hosts/houdini/plugins/load/load_bgeo.py index 4817e40961..246d1b3524 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_bgeo.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_bgeo.py @@ -29,7 +29,7 @@ class BgeoLoader(load.LoaderPlugin): obj = hou.node("/obj") # Define node name - namespace = namespace if namespace else context["asset"]["name"] + namespace = namespace if namespace else context["folder"]["name"] node_name = "{}_{}".format(namespace, name) if namespace else name # Create a new geo node diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_camera.py b/client/ayon_core/hosts/houdini/plugins/load/load_camera.py index 6f6560facc..63b50cd6fe 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_camera.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_camera.py @@ -104,13 +104,13 @@ class CameraLoader(load.LoaderPlugin): obj = hou.node("/obj") # Define node name - namespace = namespace if namespace else context["asset"]["name"] + namespace = namespace if namespace else context["folder"]["name"] node_name = "{}_{}".format(namespace, name) if namespace else name # Create a archive node node = self.create_and_connect(obj, "alembicarchive", node_name) - # TODO: add FPS of project / asset + # TODO: add FPS of project / folder node.setParms({"fileName": file_path, "channelRef": True}) # Apply some magic @@ -122,7 +122,7 @@ class CameraLoader(load.LoaderPlugin): camera = get_camera_from_container(node) self._match_maya_render_mask(camera) - set_camera_resolution(camera, asset_doc=context["asset"]) + set_camera_resolution(camera, folder_entity=context["folder"]) self[:] = nodes return pipeline.containerise(node_name, diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_fbx.py b/client/ayon_core/hosts/houdini/plugins/load/load_fbx.py index 4857dbb900..9ce7c79998 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_fbx.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_fbx.py @@ -79,7 +79,7 @@ class FbxLoader(load.LoaderPlugin): """Define node name.""" if not namespace: - namespace = context["asset"]["name"] + namespace = context["folder"]["name"] if namespace: node_name = "{}_{}".format(namespace, name) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_hda.py b/client/ayon_core/hosts/houdini/plugins/load/load_hda.py index ffe9e55036..0f7d30977a 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_hda.py @@ -30,7 +30,7 @@ class HdaLoader(load.LoaderPlugin): # Create a unique name counter = 1 - namespace = namespace or context["asset"]["name"] + namespace = namespace or context["folder"]["name"] formatted = "{}_{}".format(namespace, name) if namespace else name node_name = "{0}_{1:03d}".format(formatted, counter) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_image.py b/client/ayon_core/hosts/houdini/plugins/load/load_image.py index c89cc3b173..2cc8d0df18 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_image.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_image.py @@ -64,7 +64,7 @@ class ImageLoader(load.LoaderPlugin): parent = get_image_avalon_container() # Define node name - namespace = namespace if namespace else context["asset"]["name"] + namespace = namespace if namespace else context["folder"]["name"] node_name = "{}_{}".format(namespace, name) if namespace else name node = parent.createNode("file", node_name=node_name) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_redshift_proxy.py b/client/ayon_core/hosts/houdini/plugins/load/load_redshift_proxy.py index 3e9ce1ff2e..479c5bb300 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_redshift_proxy.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_redshift_proxy.py @@ -26,7 +26,7 @@ class RedshiftProxyLoader(load.LoaderPlugin): obj = hou.node("/obj") # Define node name - namespace = namespace if namespace else context["asset"]["name"] + namespace = namespace if namespace else context["folder"]["name"] node_name = "{}_{}".format(namespace, name) if namespace else name # Create a new geo node diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_usd_layer.py b/client/ayon_core/hosts/houdini/plugins/load/load_usd_layer.py index f4f8a718ad..fa3f3683ae 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_usd_layer.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_usd_layer.py @@ -34,7 +34,7 @@ class USDSublayerLoader(load.LoaderPlugin): stage = hou.node("/stage") # Define node name - namespace = namespace if namespace else context["asset"]["name"] + namespace = namespace if namespace else context["folder"]["name"] node_name = "{}_{}".format(namespace, name) if namespace else name # Create USD reference diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_usd_reference.py b/client/ayon_core/hosts/houdini/plugins/load/load_usd_reference.py index cb83a9a22e..38540b54c2 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_usd_reference.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_usd_reference.py @@ -34,7 +34,7 @@ class USDReferenceLoader(load.LoaderPlugin): stage = hou.node("/stage") # Define node name - namespace = namespace if namespace else context["asset"]["name"] + namespace = namespace if namespace else context["folder"]["name"] node_name = "{}_{}".format(namespace, name) if namespace else name # Create USD reference diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_vdb.py b/client/ayon_core/hosts/houdini/plugins/load/load_vdb.py index ed38e5a5d9..8af458aeb0 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_vdb.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_vdb.py @@ -26,7 +26,7 @@ class VdbLoader(load.LoaderPlugin): obj = hou.node("/obj") # Define node name - namespace = namespace if namespace else context["asset"]["name"] + namespace = namespace if namespace else context["folder"]["name"] node_name = "{}_{}".format(namespace, name) if namespace else name # Create a new geo node diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_asset_handles.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_asset_handles.py index 6b62ea09d4..943a29952e 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_asset_handles.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_asset_handles.py @@ -8,7 +8,7 @@ from ayon_core.pipeline import AYONPyblishPluginMixin class CollectAssetHandles(pyblish.api.InstancePlugin, AYONPyblishPluginMixin): - """Apply asset handles. + """Apply folder handles. If instance does not have: - frameStart @@ -19,7 +19,7 @@ class CollectAssetHandles(pyblish.api.InstancePlugin, - frameStartHandle - frameEndHandle - Then we will retrieve the asset's handles to compute + Then we will retrieve the folder's handles to compute the exclusive frame range and actual handle ranges. """ @@ -29,7 +29,7 @@ class CollectAssetHandles(pyblish.api.InstancePlugin, # this plugin runs after CollectAnatomyInstanceData order = pyblish.api.CollectorOrder + 0.499 - label = "Collect Asset Handles" + label = "Collect Folder Handles" use_asset_handles = True def process(self, instance): @@ -52,9 +52,9 @@ class CollectAssetHandles(pyblish.api.InstancePlugin, attr_values = self.get_attr_values_from_data(instance.data) if attr_values.get("use_handles", self.use_asset_handles): - asset_data = instance.data["assetEntity"]["data"] - handle_start = asset_data.get("handleStart", 0) - handle_end = asset_data.get("handleEnd", 0) + folder_attributes = instance.data["folderEntity"]["attrib"] + handle_start = folder_attributes.get("handleStart", 0) + handle_end = folder_attributes.get("handleEnd", 0) else: handle_start = 0 handle_end = 0 @@ -118,7 +118,7 @@ class CollectAssetHandles(pyblish.api.InstancePlugin, BoolDef("use_handles", tooltip="Disable this if you want the publisher to" " ignore start and end handles specified in the" - " asset data for this publish instance", + " folder attributes for this publish instance", default=cls.use_asset_handles, label="Use asset handles") ] diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_instances.py index edfa78e4d9..63537811cd 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_instances.py @@ -9,7 +9,7 @@ from ayon_core.hosts.houdini.api import lib class CollectInstances(pyblish.api.ContextPlugin): """Gather instances by all node in out graph and pre-defined attributes - This collector takes into account assets that are associated with + This collector takes into account folders that are associated with an specific node and marked with a unique identifier; Identifier: diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_instances_usd_layered.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_instances_usd_layered.py index 38d6ec733d..738d5306d1 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_instances_usd_layered.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_instances_usd_layered.py @@ -15,10 +15,10 @@ class CollectInstancesUsdLayered(pyblish.api.ContextPlugin): As opposed to storing `ayon.create.instance` as id on the node we store `pyblish.avalon.usdlayered`. - Additionally this instance has no need for storing family, asset, product - or name on the nodes. Instead all information is retrieved solely from - the output filepath, which is an Avalon URI: - avalon://{asset}/{product}.{representation} + Additionally this instance has no need for storing folder, product type, + product name or name on the nodes. Instead all information is retrieved + solely from the output filepath, which is an Avalon URI: + avalon://{folder}/{product}.{representation} Each final ROP node is considered a dependency for any of the Configured Save Path layers it sets along the way. As such, the instances shown in @@ -142,9 +142,9 @@ class CollectInstancesUsdLayered(pyblish.api.ContextPlugin): self.log.warning("Non Avalon URI Layer Path: %s" % save_path) return {} - # Collect asset + product from URI - name = "{product[name]} ({asset})".format(**uri_data) - fname = "{asset}_{product[name]}.{ext}".format(**uri_data) + # Collect folder + product from URI + name = "{product[name]} ({folder[path]})".format(**uri_data) + fname = "{folder[path]}_{product[name]}.{ext}".format(**uri_data) data = dict(uri_data) data["usdSavePath"] = save_path diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py index 24ac9f22c3..161b0aa8d4 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py @@ -1,11 +1,8 @@ import pyblish.api +import ayon_api -from ayon_core.client import ( - get_subset_by_name, - get_asset_by_name, - get_asset_name_identifier, -) -from ayon_core.pipeline import usdlib +from ayon_core.client import get_subset_by_name +from ayon_core.pipeline import usdlib, KnownPublishError class CollectUsdBootstrap(pyblish.api.InstancePlugin): @@ -54,9 +51,13 @@ class CollectUsdBootstrap(pyblish.api.InstancePlugin): self.log.debug("Add bootstrap for: %s" % bootstrap) project_name = instance.context.data["projectName"] - asset_name = instance.data["folderPath"] - asset_doc = get_asset_by_name(project_name, asset_name) - assert asset_doc, "Asset must exist: %s" % asset_name + folder_path = instance.data["folderPath"] + folder_name = folder_path.rsplit("/", 1)[-1] + folder_entity = ayon_api.get_folder_by_path(project_name, folder_path) + if not folder_entity: + raise KnownPublishError( + "Folder '{}' does not exist".format(folder_path) + ) # Check which are not about to be created and don't exist yet required = {"shot": ["usdShot"], "asset": ["usdAsset"]}.get(bootstrap) @@ -73,20 +74,20 @@ class CollectUsdBootstrap(pyblish.api.InstancePlugin): self.log.debug("Checking required bootstrap: %s" % required) for product_name in required: if self._product_exists( - project_name, instance, product_name, asset_doc + project_name, instance, product_name, folder_entity ): continue self.log.debug( "Creating {0} USD bootstrap: {1} {2}".format( - bootstrap, asset_name, product_name + bootstrap, folder_path, product_name ) ) product_type = "usd.bootstrap" new = instance.context.create_instance(product_name) new.data["productName"] = product_name - new.data["label"] = "{0} ({1})".format(product_name, asset_name) + new.data["label"] = "{0} ({1})".format(product_name, folder_name) new.data["productType"] = product_type new.data["family"] = product_type new.data["comment"] = "Automated bootstrap USD file." @@ -100,24 +101,24 @@ class CollectUsdBootstrap(pyblish.api.InstancePlugin): new.data[key] = instance.data[key] def _product_exists( - self, project_name, instance, product_name, asset_doc + self, project_name, instance, product_name, folder_entity ): """Return whether product exists in current context or in database.""" # Allow it to be created during this publish session context = instance.context - asset_doc_name = get_asset_name_identifier(asset_doc) + folder_path = folder_entity["path"] for inst in context: if ( inst.data["productName"] == product_name - and inst.data["folderPath"] == asset_doc_name + and inst.data["folderPath"] == folder_path ): return True # Or, if they already exist in the database we can # skip them too. if get_subset_by_name( - project_name, product_name, asset_doc["_id"], fields=["_id"] + project_name, product_name, folder_entity["id"], fields=["_id"] ): return True return False diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py index 99c61803e6..60eedcbe7e 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py @@ -4,10 +4,10 @@ import hou import sys from collections import deque +import ayon_api import pyblish.api from ayon_core.client import ( - get_asset_by_name, get_subset_by_name, get_last_version_by_subset_id, get_representation_by_name, @@ -284,13 +284,13 @@ class ExtractUSDLayered(publish.Extractor): # Compare this dependency with the latest published version # to detect whether we should make this into a new publish # version. If not, skip it. - asset_doc = get_asset_by_name( - project_name, dependency.data["folderPath"], fields=["_id"] + folder_entity = ayon_api.get_folder_by_path( + project_name, dependency.data["folderPath"], fields={"id"} ) subset_doc = get_subset_by_name( project_name, dependency.data["productName"], - asset_doc["_id"], + folder_entity["id"], fields=["_id"] ) if not subset_doc: diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_frame_range.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_frame_range.py index 36e1b9b2a5..2a3418ee7e 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_frame_range.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_frame_range.py @@ -7,8 +7,8 @@ from ayon_core.hosts.houdini.api.action import SelectInvalidAction import hou -class DisableUseAssetHandlesAction(RepairAction): - label = "Disable use asset handles" +class DisableUseFolderHandlesAction(RepairAction): + label = "Disable use folder handles" icon = "mdi.toggle-switch-off" @@ -23,7 +23,7 @@ class ValidateFrameRange(pyblish.api.InstancePlugin): order = pyblish.api.ValidatorOrder - 0.1 hosts = ["houdini"] label = "Validate Frame Range" - actions = [DisableUseAssetHandlesAction, SelectInvalidAction] + actions = [DisableUseFolderHandlesAction, SelectInvalidAction] def process(self, instance): @@ -41,11 +41,11 @@ class ValidateFrameRange(pyblish.api.InstancePlugin): "## Invalid Frame Range\n" "The frame range for the instance is invalid because " "the start frame is higher than the end frame.\n\nThis " - "is likely due to asset handles being applied to your " + "is likely due to folder handles being applied to your " "instance or the ROP node's start frame " "is set higher than the end frame.\n\nIf your ROP frame " - "range is correct and you do not want to apply asset " - "handles make sure to disable Use asset handles on the " + "range is correct and you do not want to apply folder " + "handles make sure to disable Use folder handles on the " "publish instance." ) ) @@ -71,7 +71,7 @@ class ValidateFrameRange(pyblish.api.InstancePlugin): cls.log.info( "The ROP node render range is set to " "{0[frameStartHandle]} - {0[frameEndHandle]} " - "The asset handles applied to the instance are start handle " + "The folder handles applied to the instance are start handle " "{0[handleStart]} and end handle {0[handleEnd]}" .format(instance.data) ) @@ -84,7 +84,7 @@ class ValidateFrameRange(pyblish.api.InstancePlugin): # Already fixed return - # Disable use asset handles + # Disable use folder handles context = instance.context create_context = context.data["create_context"] instance_id = instance.data.get("instance_id") @@ -102,5 +102,5 @@ class ValidateFrameRange(pyblish.api.InstancePlugin): created_instance.publish_attributes["CollectAssetHandles"]["use_handles"] = False # noqa create_context.save_changes() - cls.log.debug("use asset handles is turned off for '{}'" + cls.log.debug("use folder handles is turned off for '{}'" .format(instance)) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py index e94f09568d..e32f7be08a 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py @@ -54,15 +54,14 @@ class ValidateSubsetName(pyblish.api.InstancePlugin, rop_node = hou.node(instance.data["instance_node"]) # Check product name - asset_doc = instance.data["assetEntity"] + folder_entity = instance.data["folderEntity"] product_name = get_product_name( instance.context.data["projectName"], - asset_doc, - instance.data["task"], + instance.data["taskEntity"], instance.context.data["hostName"], instance.data["productType"], variant=instance.data["variant"], - dynamic_data={"asset": asset_doc["name"]} + dynamic_data={"asset": folder_entity["name"]} ) if instance.data.get("productName") != product_name: @@ -79,15 +78,15 @@ class ValidateSubsetName(pyblish.api.InstancePlugin, rop_node = hou.node(instance.data["instance_node"]) # Check product name - asset_doc = instance.data["assetEntity"] + folder_entity = instance.data["folderEntity"] product_name = get_product_name( instance.context.data["projectName"], - asset_doc, + instance.data["taskEntity"], instance.data["task"], instance.context.data["hostName"], instance.data["productType"], variant=instance.data["variant"], - dynamic_data={"asset": asset_doc["name"]} + dynamic_data={"asset": folder_entity["name"]} ) instance.data["productName"] = product_name diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_unreal_staticmesh_naming.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_unreal_staticmesh_naming.py index 33d0d42383..ae00bc9db4 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_unreal_staticmesh_naming.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_unreal_staticmesh_naming.py @@ -24,7 +24,7 @@ class ValidateUnrealStaticMeshName(pyblish.api.InstancePlugin, - UCX This validator also checks if product name is correct - - {static mesh prefix}_{Asset-Name}{Variant}. + - {static mesh prefix}_{FolderName}{Variant}. """ diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_model_exists.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_model_exists.py index 6d21b59a9c..74ded05cdb 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_model_exists.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_model_exists.py @@ -4,8 +4,11 @@ import re import pyblish.api from ayon_core.client import get_subset_by_name -from ayon_core.pipeline.publish import ValidateContentsOrder -from ayon_core.pipeline import PublishValidationError +from ayon_core.pipeline.publish import ( + ValidateContentsOrder, + KnownPublishError, + PublishValidationError, +) class ValidateUSDShadeModelExists(pyblish.api.InstancePlugin): @@ -18,7 +21,7 @@ class ValidateUSDShadeModelExists(pyblish.api.InstancePlugin): def process(self, instance): project_name = instance.context.data["projectName"] - asset_name = instance.data["folderPath"] + folder_path = instance.data["folderPath"] product_name = instance.data["productName"] # Assume shading variation starts after a dot separator @@ -27,16 +30,21 @@ class ValidateUSDShadeModelExists(pyblish.api.InstancePlugin): "^usdShade", "usdModel", shade_product_name ) - asset_doc = instance.data.get("assetEntity") - if not asset_doc: - raise RuntimeError("Asset document is not filled on instance.") + folder_entity = instance.data.get("folderEntity") + if not folder_entity: + raise KnownPublishError( + "Folder entity is not filled on instance." + ) subset_doc = get_subset_by_name( - project_name, model_product_name, asset_doc["_id"], fields=["_id"] + project_name, + model_product_name, + folder_entity["id"], + fields=["_id"] ) if not subset_doc: raise PublishValidationError( ("USD Model product not found: " - "{} ({})").format(model_product_name, asset_name), + "{} ({})").format(model_product_name, folder_path), title=self.label ) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_workspace.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_workspace.py index d85f20e3ce..2ea4b5d816 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_workspace.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_workspace.py @@ -53,7 +53,7 @@ class ValidateUsdShadeWorkspace(pyblish.api.InstancePlugin): # There were some issues with the editable node not having the right # configured path. So for now let's assure that is correct to.from value = ( - 'avalon://`chs("../asset_name")`/' + 'avalon://`chs("../folder_path")`/' 'usdShade`chs("../model_variantname1")`.usd' ) rop_value = rop.parm("lopoutput").rawValue() diff --git a/client/ayon_core/hosts/houdini/startup/MainMenuCommon.xml b/client/ayon_core/hosts/houdini/startup/MainMenuCommon.xml index 25e8c9177b..b93445a974 100644 --- a/client/ayon_core/hosts/houdini/startup/MainMenuCommon.xml +++ b/client/ayon_core/hosts/houdini/startup/MainMenuCommon.xml @@ -6,7 +6,7 @@ import os return os.environ.get("AYON_MENU_LABEL") or "AYON" ]]> - + Date: Mon, 4 Mar 2024 16:11:10 +0100 Subject: [PATCH 411/573] 3dsmax use folder and task entity --- client/ayon_core/hosts/max/api/lib.py | 57 +++++++++---------- .../hosts/max/api/lib_rendersettings.py | 12 ++-- .../max/plugins/create/create_workfile.py | 38 ++++++++----- .../plugins/publish/validate_frame_range.py | 10 ++-- .../publish/validate_resolution_setting.py | 19 +++---- 5 files changed, 71 insertions(+), 65 deletions(-) diff --git a/client/ayon_core/hosts/max/api/lib.py b/client/ayon_core/hosts/max/api/lib.py index 5c8395a2ff..5f13856c9b 100644 --- a/client/ayon_core/hosts/max/api/lib.py +++ b/client/ayon_core/hosts/max/api/lib.py @@ -6,11 +6,12 @@ import json from typing import Any, Dict, Union import six +import ayon_api + from ayon_core.pipeline import get_current_project_name, colorspace from ayon_core.settings import get_project_settings from ayon_core.pipeline.context_tools import ( - get_current_project_entity, - get_current_project_asset, + get_current_project_folder, ) from ayon_core.style import load_stylesheet from pymxs import runtime as rt @@ -217,49 +218,44 @@ def set_scene_resolution(width: int, height: int): def reset_scene_resolution(): """Apply the scene resolution from the project definition - scene resolution can be overwritten by an asset if the asset.data contains - any information regarding scene resolution . - Returns: - None + scene resolution can be overwritten by a folder if the folder.attrib + contains any information regarding scene resolution. """ - fields = ["attrib.resolutionWidth", "attrib.resolutionHeight"] - project_entity = get_current_project_entity(fields=fields) - project_attribs = project_entity["attrib"] - asset_doc = get_current_project_asset(fields=fields) - asset_data = asset_doc["data"] - # Set project resolution - project_width = int(project_attribs.get("resolutionWidth", 1920)) - project_height = int(project_attribs.get("resolutionHeight", 1080)) - width = int(asset_data.get("resolutionWidth", project_width)) - height = int(asset_data.get("resolutionHeight", project_height)) + + folder_entity = get_current_project_folder( + fields={"attrib.resolutionWidth", "attrib.resolutionHeight"} + ) + folder_attributes = folder_entity["attrib"] + width = int(folder_attributes["resolutionWidth"]) + height = int(folder_attributes["resolutionHeight"]) set_scene_resolution(width, height) -def get_frame_range(asset_doc=None) -> Union[Dict[str, Any], None]: - """Get the current assets frame range and handles. +def get_frame_range(folder_entiy=None) -> Union[Dict[str, Any], None]: + """Get the current folder frame range and handles. Args: - asset_doc (dict): Asset Entity Data + folder_entiy (dict): Folder eneity. Returns: dict: with frame start, frame end, handle start, handle end. """ # Set frame start/end - if asset_doc is None: - asset_doc = get_current_project_asset() + if folder_entiy is None: + folder_entiy = get_current_project_folder() - data = asset_doc["data"] - frame_start = data.get("frameStart") - frame_end = data.get("frameEnd") + folder_attributes = folder_entiy["attrib"] + frame_start = folder_attributes.get("frameStart") + frame_end = folder_attributes.get("frameEnd") if frame_start is None or frame_end is None: return {} frame_start = int(frame_start) frame_end = int(frame_end) - handle_start = int(data.get("handleStart", 0)) - handle_end = int(data.get("handleEnd", 0)) + handle_start = int(folder_attributes.get("handleStart", 0)) + handle_end = int(folder_attributes.get("handleEnd", 0)) frame_start_handle = frame_start - handle_start frame_end_handle = frame_end + handle_end @@ -274,7 +270,7 @@ def get_frame_range(asset_doc=None) -> Union[Dict[str, Any], None]: def reset_frame_range(fps: bool = True): - """Set frame range to current asset. + """Set frame range to current folder. This is part of 3dsmax documentation: animationRange: A System Global variable which lets you get and @@ -285,8 +281,9 @@ def reset_frame_range(fps: bool = True): scene frame rate in frames-per-second. """ if fps: - data_fps = get_current_project(fields=["data.fps"]) - fps_number = float(data_fps["data"]["fps"]) + project_name = get_current_project_name() + project_entity = ayon_api.get_project(project_name) + fps_number = float(project_entity["attrib"].get("fps")) rt.frameRate = fps_number frame_range = get_frame_range() @@ -330,7 +327,7 @@ def convert_unit_scale(): def set_context_setting(): """Apply the project settings from the project definition - Settings can be overwritten by an asset if the asset.data contains + Settings can be overwritten by an folder if the folder.attrib contains any information regarding those settings. Examples of settings: diff --git a/client/ayon_core/hosts/max/api/lib_rendersettings.py b/client/ayon_core/hosts/max/api/lib_rendersettings.py index 7ffc024ba3..8a9881f032 100644 --- a/client/ayon_core/hosts/max/api/lib_rendersettings.py +++ b/client/ayon_core/hosts/max/api/lib_rendersettings.py @@ -3,7 +3,7 @@ from pymxs import runtime as rt from ayon_core.lib import Logger from ayon_core.settings import get_project_settings from ayon_core.pipeline import get_current_project_name -from ayon_core.pipeline.context_tools import get_current_project_asset +from ayon_core.pipeline.context_tools import get_current_project_folder from ayon_core.hosts.max.api.lib import ( set_render_frame_range, @@ -57,14 +57,14 @@ class RenderSettings(object): if not os.path.exists(output_dir): os.makedirs(output_dir) # hard-coded, should be customized in the setting - context = get_current_project_asset() + folder_attributes = get_current_project_folder()["attrib"] # get project resolution - width = context["data"].get("resolutionWidth") - height = context["data"].get("resolutionHeight") + width = folder_attributes.get("resolutionWidth") + height = folder_attributes.get("resolutionHeight") # Set Frame Range - frame_start = context["data"].get("frame_start") - frame_end = context["data"].get("frame_end") + frame_start = folder_attributes.get("frame_start") + frame_end = folder_attributes.get("frame_end") set_render_frame_range(frame_start, frame_end) # get the production render renderer_class = get_current_renderer() diff --git a/client/ayon_core/hosts/max/plugins/create/create_workfile.py b/client/ayon_core/hosts/max/plugins/create/create_workfile.py index d3ae3382bc..790e0f8dd0 100644 --- a/client/ayon_core/hosts/max/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/max/plugins/create/create_workfile.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- """Creator plugin for creating workfiles.""" +import ayon_api + from ayon_core.pipeline import CreatedInstance, AutoCreator -from ayon_core.client import get_asset_by_name, get_asset_name_identifier from ayon_core.hosts.max.api import plugin from ayon_core.hosts.max.api.lib import read, imprint from pymxs import runtime as rt @@ -24,21 +25,26 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): if instance.creator_identifier == self.identifier ), None) project_name = self.project_name - asset_name = self.create_context.get_current_folder_path() + folder_path = self.create_context.get_current_folder_path() task_name = self.create_context.get_current_task_name() host_name = self.create_context.host_name if current_instance is None: - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, host_name, ) data = { - "folderPath": asset_name, + "folderPath": folder_path, "task": task_name, "variant": variant } @@ -46,8 +52,8 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): data.update( self.get_dynamic_data( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, host_name, current_instance) @@ -61,21 +67,25 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): self._add_instance_to_context(current_instance) imprint(instance_node.name, current_instance.data) elif ( - current_instance["folderPath"] != asset_name + current_instance["folderPath"] != folder_path or current_instance["task"] != task_name ): # Update instance context if is not the same - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, host_name, ) - asset_name = get_asset_name_identifier(asset_doc) - current_instance["folderPath"] = asset_name + current_instance["folderPath"] = folder_entity["path"] current_instance["task"] = task_name current_instance["productName"] = product_name diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_frame_range.py b/client/ayon_core/hosts/max/plugins/publish/validate_frame_range.py index 22fda37e61..2f4ec5f86c 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_frame_range.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_frame_range.py @@ -18,11 +18,11 @@ class ValidateFrameRange(pyblish.api.InstancePlugin, """Validates the frame ranges. This is an optional validator checking if the frame range on instance - matches the frame range specified for the asset. + matches the frame range specified for the folder. It also validates render frame ranges of render layers. - Repair action will change everything to match the asset frame range. + Repair action will change everything to match the folder frame range. This can be turned off by the artist to allow custom ranges. """ @@ -42,7 +42,7 @@ class ValidateFrameRange(pyblish.api.InstancePlugin, return frame_range = get_frame_range( - asset_doc=instance.data["assetEntity"]) + instance.data["folderEntity"]) inst_frame_start = instance.data.get("frameStartHandle") inst_frame_end = instance.data.get("frameEndHandle") @@ -57,12 +57,12 @@ class ValidateFrameRange(pyblish.api.InstancePlugin, if frame_start_handle != inst_frame_start: errors.append( f"Start frame ({inst_frame_start}) on instance does not match " # noqa - f"with the start frame ({frame_start_handle}) set on the asset data. ") # noqa + f"with the start frame ({frame_start_handle}) set on the folder attributes. ") # noqa if frame_end_handle != inst_frame_end: errors.append( f"End frame ({inst_frame_end}) on instance does not match " f"with the end frame ({frame_end_handle}) " - "from the asset data. ") + "from the folder attributes. ") if errors: bullet_point_errors = "\n".join( diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_resolution_setting.py b/client/ayon_core/hosts/max/plugins/publish/validate_resolution_setting.py index 1913e77210..f499f851f1 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_resolution_setting.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_resolution_setting.py @@ -24,7 +24,7 @@ class ValidateResolutionSetting(pyblish.api.InstancePlugin, def process(self, instance): if not self.is_active(instance.data): return - width, height = self.get_db_resolution(instance) + width, height = self.get_folder_resolution(instance) current_width = rt.renderWidth current_height = rt.renderHeight if current_width != width and current_height != height: @@ -41,16 +41,15 @@ class ValidateResolutionSetting(pyblish.api.InstancePlugin, "not matching resolution set " "on asset or shot.") - def get_db_resolution(self, instance): - asset_doc = instance.data["assetEntity"] - project_entity = instance.context.data["projectEntity"] - for data in [asset_doc["data"], project_entity["attrib"]]: - if "resolutionWidth" in data and "resolutionHeight" in data: - width = data["resolutionWidth"] - height = data["resolutionHeight"] - return int(width), int(height) + def get_folder_resolution(self, instance): + folder_entity = instance.data["folderEntity"] + if folder_entity: + folder_attributes = folder_entity["attrib"] + width = folder_attributes["resolutionWidth"] + height = folder_attributes["resolutionHeight"] + return int(width), int(height) - # Defaults if not found in asset document or project document + # Defaults if not found in folder entity return 1920, 1080 @classmethod From f98e67f81a33ead863c0cb9f0e3f872cd4ac0a5b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 16:14:10 +0100 Subject: [PATCH 412/573] maya use folder and task entities --- client/ayon_core/hosts/maya/api/action.py | 24 +- client/ayon_core/hosts/maya/api/commands.py | 15 +- client/ayon_core/hosts/maya/api/lib.py | 187 +++++++------ .../hosts/maya/api/lib_rendersettings.py | 9 +- client/ayon_core/hosts/maya/api/pipeline.py | 2 +- client/ayon_core/hosts/maya/api/plugin.py | 42 +-- client/ayon_core/hosts/maya/api/setdress.py | 252 ++++++++++-------- .../maya/api/workfile_template_builder.py | 6 +- .../maya/plugins/create/convert_legacy.py | 21 +- .../plugins/create/create_multishot_layout.py | 45 ++-- .../maya/plugins/create/create_review.py | 21 +- .../create/create_unreal_skeletalmesh.py | 17 +- .../create/create_unreal_staticmesh.py | 17 +- .../maya/plugins/create/create_workfile.py | 45 ++-- .../maya/plugins/load/load_arnold_standin.py | 6 +- .../hosts/maya/plugins/load/load_assembly.py | 7 +- .../hosts/maya/plugins/load/load_audio.py | 6 +- .../hosts/maya/plugins/load/load_gpucache.py | 7 +- .../hosts/maya/plugins/load/load_image.py | 7 +- .../maya/plugins/load/load_image_plane.py | 18 +- .../hosts/maya/plugins/load/load_maya_usd.py | 6 +- .../maya/plugins/load/load_multiverse_usd.py | 6 +- .../maya/plugins/load/load_redshift_proxy.py | 6 +- .../maya/plugins/load/load_rendersetup.py | 6 +- .../maya/plugins/load/load_vdb_to_arnold.py | 7 +- .../maya/plugins/load/load_vdb_to_redshift.py | 8 +- .../maya/plugins/load/load_vdb_to_vray.py | 7 +- .../hosts/maya/plugins/load/load_vrayproxy.py | 6 +- .../hosts/maya/plugins/load/load_vrayscene.py | 6 +- .../maya/plugins/load/load_yeti_cache.py | 10 +- .../maya/plugins/publish/collect_review.py | 4 +- .../plugins/publish/validate_frame_range.py | 8 +- .../publish/validate_instance_in_context.py | 24 +- .../plugins/publish/validate_maya_units.py | 17 +- .../publish/validate_node_ids_in_database.py | 20 +- .../publish/validate_node_ids_related.py | 18 +- .../publish/validate_node_ids_unique.py | 4 +- .../publish/validate_renderlayer_aovs.py | 10 +- .../plugins/publish/validate_resolution.py | 30 +-- .../plugins/publish/validate_shader_name.py | 8 +- .../validate_unreal_staticmesh_naming.py | 4 +- .../tools/mayalookassigner/arnold_standin.py | 14 +- .../maya/tools/mayalookassigner/commands.py | 67 ++--- .../maya/tools/mayalookassigner/models.py | 10 +- .../maya/tools/mayalookassigner/widgets.py | 33 ++- 45 files changed, 596 insertions(+), 497 deletions(-) diff --git a/client/ayon_core/hosts/maya/api/action.py b/client/ayon_core/hosts/maya/api/action.py index 4beb1e3e5b..baf558036e 100644 --- a/client/ayon_core/hosts/maya/api/action.py +++ b/client/ayon_core/hosts/maya/api/action.py @@ -2,8 +2,8 @@ from __future__ import absolute_import import pyblish.api +import ayon_api -from ayon_core.client import get_asset_by_name from ayon_core.pipeline.publish import get_errored_instances_from_context @@ -74,21 +74,23 @@ class GenerateUUIDsOnInvalidAction(pyblish.api.Action): from . import lib - # Expecting this is called on validators in which case 'assetEntity' + # Expecting this is called on validators in which case 'folderEntity' # should be always available, but kept a way to query it by name. - asset_doc = instance.data.get("assetEntity") - if not asset_doc: - asset_name = instance.data["folderPath"] + folder_entity = instance.data.get("folderEntity") + if not folder_entity: + folder_path = instance.data["folderPath"] project_name = instance.context.data["projectName"] self.log.info(( - "Asset is not stored on instance." - " Querying by name \"{}\" from project \"{}\"" - ).format(asset_name, project_name)) - asset_doc = get_asset_by_name( - project_name, asset_name, fields=["_id"] + "Folder is not stored on instance." + " Querying by path \"{}\" from project \"{}\"" + ).format(folder_path, project_name)) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path, fields={"id"} ) - for node, _id in lib.generate_ids(nodes, asset_id=asset_doc["_id"]): + for node, _id in lib.generate_ids( + nodes, folder_id=folder_entity["id"] + ): lib.set_id(node, _id, overwrite=True) diff --git a/client/ayon_core/hosts/maya/api/commands.py b/client/ayon_core/hosts/maya/api/commands.py index 75931e301a..aa01d91369 100644 --- a/client/ayon_core/hosts/maya/api/commands.py +++ b/client/ayon_core/hosts/maya/api/commands.py @@ -2,9 +2,8 @@ """OpenPype script commands to be used directly in Maya.""" from maya import cmds -from ayon_api import get_project +from ayon_api import get_project, get_folder_by_path -from ayon_core.client import get_asset_by_name from ayon_core.pipeline import get_current_project_name, get_current_folder_path @@ -73,18 +72,18 @@ def reset_resolution(): resolution_width = 1920 resolution_height = 1080 - # Get resolution from asset + # Get resolution from folder project_name = get_current_project_name() - asset_name = get_current_folder_path() - asset_doc = get_asset_by_name(project_name, asset_name) - resolution = _resolution_from_document(asset_doc) + folder_path = get_current_folder_path() + folder_entity = get_folder_by_path(project_name, folder_path) + resolution = _resolution_from_document(folder_entity) # Try get resolution from project if resolution is None: # TODO go through visualParents print(( - "Asset \"{}\" does not have set resolution." + "Folder '{}' does not have set resolution." " Trying to get resolution from project" - ).format(asset_name)) + ).format(folder_path)) project_entity = get_project(project_name) resolution = _resolution_from_document(project_entity) diff --git a/client/ayon_core/hosts/maya/api/lib.py b/client/ayon_core/hosts/maya/api/lib.py index fc084d9e23..8b8d83a155 100644 --- a/client/ayon_core/hosts/maya/api/lib.py +++ b/client/ayon_core/hosts/maya/api/lib.py @@ -22,11 +22,9 @@ from maya.api import OpenMaya import ayon_api from ayon_core.client import ( - get_asset_by_name, get_subsets, get_last_versions, get_representation_by_name, - get_asset_name_identifier, ) from ayon_core.settings import get_project_settings from ayon_core.pipeline import ( @@ -44,7 +42,7 @@ from ayon_core.pipeline import ( AYON_CONTAINER_ID, ) from ayon_core.lib import NumberDef -from ayon_core.pipeline.context_tools import get_current_project_asset +from ayon_core.pipeline.context_tools import get_current_project_folder from ayon_core.pipeline.create import CreateContext from ayon_core.lib.profiles_filtering import filter_profiles @@ -285,16 +283,16 @@ def generate_capture_preset(instance, camera, path, width_preset = capture_preset["Resolution"]["width"] height_preset = capture_preset["Resolution"]["height"] - # Set resolution variables from asset values - asset_data = instance.data["assetEntity"]["data"] - asset_width = asset_data.get("resolutionWidth") - asset_height = asset_data.get("resolutionHeight") + # Set resolution variables from folder values + folder_attributes = instance.data["folderEntity"]["attrib"] + folder_width = folder_attributes.get("resolutionWidth") + folder_height = folder_attributes.get("resolutionHeight") review_instance_width = instance.data.get("review_width") review_instance_height = instance.data.get("review_height") # Use resolution from instance if review width/height is set # Otherwise use the resolution from preset if it has non-zero values - # Otherwise fall back to asset width x height + # Otherwise fall back to folder width x height # Else define no width, then `capture.capture` will use render resolution if review_instance_width and review_instance_height: preset["width"] = review_instance_width @@ -302,9 +300,9 @@ def generate_capture_preset(instance, camera, path, elif width_preset and height_preset: preset["width"] = width_preset preset["height"] = height_preset - elif asset_width and asset_height: - preset["width"] = asset_width - preset["height"] = asset_height + elif folder_width and folder_height: + preset["width"] = folder_width + preset["height"] = folder_height # Isolate view is requested by having objects in the set besides a # camera. If there is only 1 member it'll be the camera because we @@ -1639,7 +1637,7 @@ def get_id(node): return -def generate_ids(nodes, asset_id=None): +def generate_ids(nodes, folder_id=None): """Returns new unique ids for the given nodes. Note: This does not assign the new ids, it only generates the values. @@ -1656,27 +1654,33 @@ def generate_ids(nodes, asset_id=None): Args: nodes (list): List of nodes. - asset_id (str or bson.ObjectId): The database id for the *asset* to - generate for. When None provided the current asset in the - active session is used. + folder_id (Optional[str]): Folder id to generate id for. When None + provided current folder is used. Returns: list: A list of (node, id) tuples. """ - if asset_id is None: - # Get the asset ID from the database for the asset of current context + if folder_id is None: + # Get the folder id based on current context folder project_name = get_current_project_name() - asset_name = get_current_folder_path() - asset_doc = get_asset_by_name(project_name, asset_name, fields=["_id"]) - assert asset_doc, "No current asset found in Session" - asset_id = asset_doc['_id'] + folder_path = get_current_folder_path() + if not folder_path: + raise ValueError("Current folder path is not set") + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path, fields=["id"] + ) + if not folder_entity: + raise ValueError(( + "Current folder '{}' was not found on the server" + ).format(folder_path)) + folder_id = folder_entity["id"] node_ids = [] for node in nodes: _, uid = str(uuid.uuid4()).rsplit("-", 1) - unique_id = "{}:{}".format(asset_id, uid) + unique_id = "{}:{}".format(folder_id, uid) node_ids.append((node, unique_id)) return node_ids @@ -1868,16 +1872,20 @@ def get_container_members(container): # region LOOKDEV -def list_looks(project_name, asset_id): - """Return all look products for the given asset +def list_looks(project_name, folder_id): + """Return all look products for the given folder. This assumes all look products start with "look*" in their names. + + Returns: + list[dict[str, Any]]: List of look products. + """ # # get all products with look leading in # the name associated with the asset # TODO this should probably look for product type 'look' instead of # checking product name that can not start with product type - subset_docs = get_subsets(project_name, asset_ids=[asset_id]) + subset_docs = get_subsets(project_name, asset_ids=[folder_id]) return [ subset_doc for subset_doc in subset_docs @@ -1946,7 +1954,7 @@ def assign_look_by_version(nodes, version_id): def assign_look(nodes, product_name="lookDefault"): """Assigns a look to a node. - Optimizes the nodes by grouping by asset id and finding + Optimizes the nodes by grouping by folder id and finding related product by name. Args: @@ -1954,27 +1962,27 @@ def assign_look(nodes, product_name="lookDefault"): product_name (str): name of the product to find """ - # Group all nodes per asset id + # Group all nodes per folder id grouped = defaultdict(list) for node in nodes: - pype_id = get_id(node) - if not pype_id: + hash_id = get_id(node) + if not hash_id: continue - parts = pype_id.split(":", 1) + parts = hash_id.split(":", 1) grouped[parts[0]].append(node) project_name = get_current_project_name() subset_docs = get_subsets( project_name, subset_names=[product_name], asset_ids=grouped.keys() ) - subset_docs_by_asset_id = { + subset_docs_by_folder_id = { str(subset_doc["parent"]): subset_doc for subset_doc in subset_docs } product_ids = { subset_doc["_id"] - for subset_doc in subset_docs_by_asset_id.values() + for subset_doc in subset_docs_by_folder_id.values() } last_version_docs = get_last_versions( project_name, @@ -1986,28 +1994,28 @@ def assign_look(nodes, product_name="lookDefault"): for last_version_doc in last_version_docs } - for asset_id, asset_nodes in grouped.items(): + for folder_id, asset_nodes in grouped.items(): # create objectId for database - subset_doc = subset_docs_by_asset_id.get(asset_id) + subset_doc = subset_docs_by_folder_id.get(folder_id) if not subset_doc: log.warning(( "No product '{}' found for {}" - ).format(product_name, asset_id)) + ).format(product_name, folder_id)) continue last_version = last_version_docs_by_product_id.get(subset_doc["_id"]) if not last_version: log.warning(( - "Not found last version for product '{}' on asset with id {}" - ).format(product_name, asset_id)) + "Not found last version for product '{}' on folder with id {}" + ).format(product_name, folder_id)) continue families = last_version.get("data", {}).get("families") or [] if "look" not in families: log.warning(( - "Last version for product '{}' on asset with id {}" + "Last version for product '{}' on folder with id {}" " does not have look product type" - ).format(product_name, asset_id)) + ).format(product_name, folder_id)) continue log.debug("Assigning look '{}' ".format( @@ -2499,11 +2507,11 @@ def get_fps_for_current_context(): """ project_name = get_current_project_name() - asset_name = get_current_folder_path() - asset_doc = get_asset_by_name( - project_name, asset_name, fields=["data.fps"] + folder_path = get_current_folder_path() + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path, fields={"attrib.fps"} ) or {} - fps = asset_doc.get("data", {}).get("fps") + fps = folder_entity.get("attrib", {}).get("fps") if not fps: project_entity = ayon_api.get_project( project_name, fields=["attrib.fps"] @@ -2517,7 +2525,7 @@ def get_fps_for_current_context(): def get_frame_range(include_animation_range=False): - """Get the current assets frame range and handles. + """Get the current folder frame range and handles. Args: include_animation_range (bool, optional): Whether to include @@ -2525,24 +2533,25 @@ def get_frame_range(include_animation_range=False): range of the timeline. It is excluded by default. Returns: - dict: Asset's expected frame range values. + dict: Folder's expected frame range values. """ # Set frame start/end project_name = get_current_project_name() - asset_name = get_current_folder_path() - asset = get_asset_by_name(project_name, asset_name) + folder_path = get_current_folder_path() + folder_entity = ayon_api.get_folder_by_path(project_name, folder_path) + folder_attributes = folder_entity["attrib"] - frame_start = asset["data"].get("frameStart") - frame_end = asset["data"].get("frameEnd") + frame_start = folder_attributes.get("frameStart") + frame_end = folder_attributes.get("frameEnd") if frame_start is None or frame_end is None: - cmds.warning("No edit information found for %s" % asset_name) + cmds.warning("No edit information found for '{}'".format(folder_path)) return - handle_start = asset["data"].get("handleStart") or 0 - handle_end = asset["data"].get("handleEnd") or 0 + handle_start = folder_attributes.get("handleStart") or 0 + handle_end = folder_attributes.get("handleEnd") or 0 frame_range = { "frameStart": frame_start, @@ -2558,15 +2567,21 @@ def get_frame_range(include_animation_range=False): # keys. That is why these are excluded by default. task_name = get_current_task_name() settings = get_project_settings(project_name) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) + task_type = None + if task_entity: + task_type = task_entity["taskType"] + include_handles_settings = settings["maya"]["include_handles"] - current_task = asset.get("data").get("tasks").get(task_name) animation_start = frame_start animation_end = frame_end include_handles = include_handles_settings["include_handles_default"] for item in include_handles_settings["per_task_type"]: - if current_task["type"] in item["task_type"]: + if task_type in item["task_type"]: include_handles = item["include_handles"] break if include_handles: @@ -2580,7 +2595,7 @@ def get_frame_range(include_animation_range=False): def reset_frame_range(playback=True, render=True, fps=True): - """Set frame range to current asset + """Set frame range to current folder. Args: playback (bool, Optional): Whether to set the maya timeline playback @@ -2594,7 +2609,7 @@ def reset_frame_range(playback=True, render=True, fps=True): frame_range = get_frame_range(include_animation_range=True) if not frame_range: - # No frame range data found for asset + # No frame range data found for folder return frame_start = frame_range["frameStart"] @@ -2619,27 +2634,19 @@ def reset_frame_range(playback=True, render=True, fps=True): def reset_scene_resolution(): """Apply the scene resolution from the project definition - scene resolution can be overwritten by an asset if the asset.data contains - any information regarding scene resolution . + scene resolution can be overwritten by an folder if the folder.attrib + contains any information regarding scene resolution . Returns: None """ - project_name = get_current_project_name() - project_entity = ayon_api.get_project(project_name) - project_attribs = project_entity["attrib"] - asset_data = get_current_project_asset()["data"] + folder_attributes = get_current_project_folder()["attrib"] - # Set project resolution - width_key = "resolutionWidth" - height_key = "resolutionHeight" - pixelAspect_key = "pixelAspect" - - width = asset_data.get(width_key, project_attribs.get(width_key, 1920)) - height = asset_data.get(height_key, project_attribs.get(height_key, 1080)) - pixelAspect = asset_data.get(pixelAspect_key, - project_attribs.get(pixelAspect_key, 1)) + # Set resolution + width = folder_attributes.get("resolutionWidth", 1920) + height = folder_attributes.get("resolutionHeight", 1080) + pixelAspect = folder_attributes.get("pixelAspect", 1) set_scene_resolution(width, height, pixelAspect) @@ -2647,7 +2654,7 @@ def reset_scene_resolution(): def set_context_settings(): """Apply the project settings from the project definition - Settings can be overwritten by an asset if the asset.data contains + Settings can be overwritten by an folder if the folder.attrib contains any information regarding those settings. Examples of settings: @@ -2657,9 +2664,8 @@ def set_context_settings(): Returns: None + """ - - # Set project fps set_scene_fps(get_fps_for_current_context()) @@ -3161,9 +3167,9 @@ def update_content_on_context_change(): This will update scene content to match new folder on context change """ scene_sets = cmds.listSets(allSets=True) - asset_doc = get_current_project_asset() - new_folder_path = get_asset_name_identifier(asset_doc) - new_data = asset_doc["data"] + folder_entity = get_current_project_folder() + folder_attributes = folder_entity["attrib"] + new_folder_path = folder_entity["path"] for s in scene_sets: try: if cmds.getAttr("{}.id".format(s)) in { @@ -3181,10 +3187,10 @@ def update_content_on_context_change(): ) if "frameStart" in attr: cmds.setAttr("{}.frameStart".format(s), - new_data["frameStart"]) + folder_attributes["frameStart"]) if "frameEnd" in attr: cmds.setAttr("{}.frameEnd".format(s), - new_data["frameEnd"],) + folder_attributes["frameEnd"],) except ValueError: pass @@ -4122,15 +4128,26 @@ def create_rig_animation_instance( ) assert roots, "No root nodes in rig, this is a bug." + folder_entity = context["folder"] + product_type = ( + context["subset"]["data"].get("family") + or context["subset"]["data"]["families"][0] + ) + product_name = context["subset"]["name"] + custom_product_name = options.get("animationProductName") if custom_product_name: formatting_data = { - "asset": context["asset"], - "subset": context['subset']['name'], - "family": ( - context['subset']['data'].get('family') or - context['subset']['data']['families'][0] - ) + "folder": { + "name": folder_entity["name"] + }, + "product": { + "type": product_type, + "name": product_name, + }, + "asset": folder_entity["name"], + "subset": product_name, + "family": product_type } namespace = get_custom_namespace( custom_product_name.format(**formatting_data) diff --git a/client/ayon_core/hosts/maya/api/lib_rendersettings.py b/client/ayon_core/hosts/maya/api/lib_rendersettings.py index b8a4d04a10..905e8c69af 100644 --- a/client/ayon_core/hosts/maya/api/lib_rendersettings.py +++ b/client/ayon_core/hosts/maya/api/lib_rendersettings.py @@ -7,7 +7,7 @@ from ayon_core.lib import Logger from ayon_core.settings import get_project_settings from ayon_core.pipeline import CreatorError, get_current_project_name -from ayon_core.pipeline.context_tools import get_current_project_asset +from ayon_core.pipeline.context_tools import get_current_project_folder from ayon_core.hosts.maya.api.lib import reset_frame_range @@ -77,7 +77,8 @@ class RenderSettings(object): renderer = cmds.getAttr( 'defaultRenderGlobals.currentRenderer').lower() - asset_doc = get_current_project_asset() + folder_entity = get_current_project_folder() + folder_attributes = folder_entity["attrib"] # project_settings/maya/create/CreateRender/aov_separator try: aov_separator = self._aov_chars[( @@ -101,8 +102,8 @@ class RenderSettings(object): else: print("{0} isn't a supported renderer to autoset settings.".format(renderer)) # noqa # TODO: handle not having res values in the doc - width = asset_doc["data"].get("resolutionWidth") - height = asset_doc["data"].get("resolutionHeight") + width = folder_attributes.get("resolutionWidth") + height = folder_attributes.get("resolutionHeight") if renderer == "arnold": # set renderer settings for Arnold from project settings diff --git a/client/ayon_core/hosts/maya/api/pipeline.py b/client/ayon_core/hosts/maya/api/pipeline.py index 90fb2e5888..49b27c43eb 100644 --- a/client/ayon_core/hosts/maya/api/pipeline.py +++ b/client/ayon_core/hosts/maya/api/pipeline.py @@ -588,7 +588,7 @@ def on_open(): from ayon_core.tools.utils import SimplePopup # Validate FPS after update_task_from_path to - # ensure it is using correct FPS for the asset + # ensure it is using correct FPS for the folder lib.validate_fps() lib.fix_incompatible_containers() diff --git a/client/ayon_core/hosts/maya/api/plugin.py b/client/ayon_core/hosts/maya/api/plugin.py index 5d6ffe0384..af3546ac24 100644 --- a/client/ayon_core/hosts/maya/api/plugin.py +++ b/client/ayon_core/hosts/maya/api/plugin.py @@ -4,6 +4,7 @@ from abc import ABCMeta import qargparse import six +import ayon_api from maya import cmds from maya.app.renderSetup.model import renderSetup @@ -28,7 +29,6 @@ from ayon_core.pipeline import ( get_current_project_name, ) from ayon_core.pipeline.load import LoadError -from ayon_core.client import get_asset_by_name from ayon_core.pipeline.create import get_product_name from . import lib @@ -454,17 +454,23 @@ class RenderlayerCreator(NewCreator, MayaCreatorBase): # this instance will not have the `instance_node` data yet # until it's been saved/persisted at least once. project_name = self.create_context.get_current_project_name() - asset_name = self.create_context.get_current_folder_path() + folder_path = self.create_context.get_current_folder_path() + task_name = self.create_context.get_current_task_name() instance_data = { - "folderPath": asset_name, - "task": self.create_context.get_current_task_name(), + "folderPath": folder_path, + "task": task_name, "variant": layer.name(), } - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - instance_data["task"], + folder_entity, + task_entity, layer.name(), host_name, ) @@ -578,8 +584,8 @@ class RenderlayerCreator(NewCreator, MayaCreatorBase): def get_product_name( self, project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, host_name=None, instance=None @@ -587,13 +593,17 @@ class RenderlayerCreator(NewCreator, MayaCreatorBase): if host_name is None: host_name = self.create_context.host_name dynamic_data = self.get_dynamic_data( - project_name, asset_doc, task_name, variant, host_name, instance + project_name, + folder_entity, + task_entity, + variant, + host_name, + instance ) # creator.product_type != 'render' as expected return get_product_name( project_name, - asset_doc, - task_name, + task_entity, host_name, self.layer_instance_prefix or self.product_type, variant, @@ -668,17 +678,17 @@ class Loader(LoaderPlugin): self.log.debug("No custom group_name, no group will be created.") options["attach_to_root"] = False - asset_doc = context["asset"] + folder_entity = context["folder"] subset_doc = context["subset"] product_type = ( subset_doc["data"].get("family") or subset_doc["data"]["families"][0] ) formatting_data = { - "asset_name": asset_doc["name"], - "asset_type": asset_doc["type"], + "asset_name": folder_entity["name"], + "asset_type": "asset", "folder": { - "name": asset_doc["name"], + "name": folder_entity["name"], }, "subset": subset_doc["name"], "product": { diff --git a/client/ayon_core/hosts/maya/api/setdress.py b/client/ayon_core/hosts/maya/api/setdress.py index 812457a486..c1f900110d 100644 --- a/client/ayon_core/hosts/maya/api/setdress.py +++ b/client/ayon_core/hosts/maya/api/setdress.py @@ -6,6 +6,7 @@ import contextlib import copy import six +import ayon_api from maya import cmds @@ -296,9 +297,12 @@ def update_package_version(container, version): assert current_representation is not None, "This is a bug" - version_doc, subset_doc, asset_doc, project_entity = ( - get_representation_parents(project_name, current_representation) - ) + ( + version_doc, + subset_doc, + folder_entity, + project_entity + ) = get_representation_parents(project_name, current_representation) if version == -1: new_version = get_last_version_by_subset_id( @@ -318,11 +322,8 @@ def update_package_version(container, version): # TODO there is 'get_representation_context' to get the context which # could be possible to use here new_context = { - "project": { - "name": project_doc["name"], - "code": project_doc["data"].get("code", "") - }, - "asset": asset_doc, + "project": project_entity, + "folder": folder_entity, "subset": subset_doc, "version": version_doc, "representation": new_representation, @@ -413,6 +414,8 @@ def update_scene(set_container, containers, current_data, new_data, new_file): new_lookup = _instances_by_namespace(new_data) old_lookup = _instances_by_namespace(current_data) + repre_ids = set() + containers_for_repre_compare = [] for container in containers: container_ns = container['namespace'] @@ -421,98 +424,121 @@ def update_scene(set_container, containers, current_data, new_data, new_file): processed_namespaces.add(container_ns) processed_containers.append(container['objectName']) - if container_ns in new_lookup: - root = get_container_transforms(container, root=True) - if not root: - log.error("Can't find root for %s", container['objectName']) - continue - - old_instance = old_lookup.get(container_ns, {}) - new_instance = new_lookup[container_ns] - - # Update the matrix - # check matrix against old_data matrix to find local overrides - current_matrix = cmds.xform(root, - query=True, - matrix=True, - objectSpace=True) - - original_matrix = old_instance.get("matrix", identity) - has_matrix_override = not matrix_equals(current_matrix, - original_matrix) - - if has_matrix_override: - log.warning("Matrix override preserved on %s", container_ns) - else: - new_matrix = new_instance.get("matrix", identity) - cmds.xform(root, matrix=new_matrix, objectSpace=True) - - # Update the parenting - if old_instance.get("parent", None) != new_instance["parent"]: - - parent = to_namespace(new_instance['parent'], set_namespace) - if not cmds.objExists(parent): - log.error("Can't find parent %s", parent) - continue - - # Set the new parent - cmds.lockNode(root, lock=False) - root = cmds.parent(root, parent, relative=True) - cmds.lockNode(root, lock=True) - - # Update the representation - representation_current = container['representation'] - representation_old = old_instance['representation'] - representation_new = new_instance['representation'] - has_representation_override = (representation_current != - representation_old) - - if representation_new != representation_current: - - if has_representation_override: - log.warning("Your scene had local representation " - "overrides within the set. New " - "representations not loaded for %s.", - container_ns) - continue - - # We check it against the current 'loader' in the scene instead - # of the original data of the package that was loaded because - # an Artist might have made scene local overrides - if new_instance['loader'] != container['loader']: - log.warning("Loader is switched - local edits will be " - "lost. Removing: %s", - container_ns) - - # Remove this from the "has been processed" list so it's - # considered as new element and added afterwards. - processed_containers.pop() - processed_namespaces.remove(container_ns) - remove_container(container) - continue - - # Check whether the conversion can be done by the Loader. - # They *must* use the same asset, product and Loader for - # `update_container` to make sense. - old = get_representation_by_id( - project_name, representation_current - ) - new = get_representation_by_id( - project_name, representation_new - ) - is_valid = compare_representations(old=old, new=new) - if not is_valid: - log.error("Skipping: %s. See log for details.", - container_ns) - continue - - new_version = new["context"]["version"] - update_container(container, version=new_version) - - else: + if container_ns not in new_lookup: # Remove this container because it's not in the new data log.warning("Removing content: %s", container_ns) remove_container(container) + continue + + root = get_container_transforms(container, root=True) + if not root: + log.error("Can't find root for %s", container['objectName']) + continue + + old_instance = old_lookup.get(container_ns, {}) + new_instance = new_lookup[container_ns] + + # Update the matrix + # check matrix against old_data matrix to find local overrides + current_matrix = cmds.xform(root, + query=True, + matrix=True, + objectSpace=True) + + original_matrix = old_instance.get("matrix", identity) + has_matrix_override = not matrix_equals(current_matrix, + original_matrix) + + if has_matrix_override: + log.warning("Matrix override preserved on %s", container_ns) + else: + new_matrix = new_instance.get("matrix", identity) + cmds.xform(root, matrix=new_matrix, objectSpace=True) + + # Update the parenting + if old_instance.get("parent", None) != new_instance["parent"]: + + parent = to_namespace(new_instance['parent'], set_namespace) + if not cmds.objExists(parent): + log.error("Can't find parent %s", parent) + continue + + # Set the new parent + cmds.lockNode(root, lock=False) + root = cmds.parent(root, parent, relative=True) + cmds.lockNode(root, lock=True) + + # Update the representation + representation_current = container['representation'] + representation_old = old_instance['representation'] + representation_new = new_instance['representation'] + has_representation_override = (representation_current != + representation_old) + + if representation_new == representation_current: + continue + + if has_representation_override: + log.warning("Your scene had local representation " + "overrides within the set. New " + "representations not loaded for %s.", + container_ns) + continue + + # We check it against the current 'loader' in the scene instead + # of the original data of the package that was loaded because + # an Artist might have made scene local overrides + if new_instance['loader'] != container['loader']: + log.warning("Loader is switched - local edits will be " + "lost. Removing: %s", + container_ns) + + # Remove this from the "has been processed" list so it's + # considered as new element and added afterwards. + processed_containers.pop() + processed_namespaces.remove(container_ns) + remove_container(container) + continue + + # Check whether the conversion can be done by the Loader. + # They *must* use the same folder, product and Loader for + # `update_container` to make sense. + repre_ids.add(representation_current) + repre_ids.add(representation_new) + + containers_for_repre_compare.append( + (container, representation_current, representation_new) + ) + + repre_entities_by_id = { + repre_entity["id"]: repre_entity + for repre_entity in ayon_api.get_representations( + project_name, representation_ids=repre_ids + ) + } + repre_parents_by_id = ayon_api.get_representations_parents( + project_name, repre_ids + ) + for ( + container, + repre_current_id, + repre_new_id + ) in containers_for_repre_compare: + current_repre = repre_entities_by_id[repre_current_id] + current_parents = repre_parents_by_id[repre_current_id] + new_repre = repre_entities_by_id[repre_new_id] + new_parents = repre_parents_by_id[repre_new_id] + + is_valid = compare_representations( + current_repre, current_parents, new_repre, new_parents + ) + if not is_valid: + log.error("Skipping: %s. See log for details.", + container["namespace"]) + continue + + new_version = new_parents.version["version"] + update_container(container, version=new_version) # Add new assets all_loaders = discover_loader_plugins() @@ -543,43 +569,43 @@ def update_scene(set_container, containers, current_data, new_data, new_file): return processed_containers -def compare_representations(old, new): +def compare_representations( + current_repre, current_parents, new_repre, new_parents +): """Check if the old representation given can be updated Due to limitations of the `update_container` function we cannot allow differences in the following data: * Representation name (extension) - * Asset name - * Subset name (variation) + * Folder id + * Product id If any of those data values differs, the function will raise an RuntimeError Args: - old(dict): representation data from the database - new(dict): representation data from the database + current_repre (dict[str, Any]): Current representation entity. + current_parents (RepresentationParents): Current + representation parents. + new_repre (dict[str, Any]): New representation entity. + new_parents (RepresentationParents): New representation parents. Returns: bool: False if the representation is not invalid else True - """ - if new["name"] != old["name"]: + """ + if current_repre["name"] != new_repre["name"]: log.error("Cannot switch extensions") return False - new_context = new["context"] - old_context = old["context"] - # TODO add better validation e.g. based on parent ids - if new_context["asset"] != old_context["asset"]: - log.error("Changing assets between updates is " - "not supported.") + if current_parents.folder["id"] != new_parents.folder["id"]: + log.error("Changing folders between updates is not supported.") return False - if new_context["subset"] != old_context["subset"]: - log.error("Changing products between updates is " - "not supported.") + if current_parents.product["id"] != new_parents.product["id"]: + log.error("Changing products between updates is not supported.") return False return True diff --git a/client/ayon_core/hosts/maya/api/workfile_template_builder.py b/client/ayon_core/hosts/maya/api/workfile_template_builder.py index abe8f458dc..cb25a722f0 100644 --- a/client/ayon_core/hosts/maya/api/workfile_template_builder.py +++ b/client/ayon_core/hosts/maya/api/workfile_template_builder.py @@ -74,7 +74,7 @@ class MayaTemplateBuilder(AbstractTemplateBuilder): return True # update imported sets information - asset_name = get_current_folder_path() + folder_path = get_current_folder_path() for node in imported_sets: if not cmds.attributeQuery("id", node=node, exists=True): continue @@ -82,11 +82,11 @@ class MayaTemplateBuilder(AbstractTemplateBuilder): AYON_INSTANCE_ID, AVALON_INSTANCE_ID }: continue - if not cmds.attributeQuery("asset", node=node, exists=True): + if not cmds.attributeQuery("folderPath", node=node, exists=True): continue cmds.setAttr( - "{}.asset".format(node), asset_name, type="string") + "{}.folderPath".format(node), folder_path, type="string") return True diff --git a/client/ayon_core/hosts/maya/plugins/create/convert_legacy.py b/client/ayon_core/hosts/maya/plugins/create/convert_legacy.py index b23c56fc5b..9622504244 100644 --- a/client/ayon_core/hosts/maya/plugins/create/convert_legacy.py +++ b/client/ayon_core/hosts/maya/plugins/create/convert_legacy.py @@ -1,9 +1,9 @@ +import ayon_api + from ayon_core.pipeline.create.creator_plugins import SubsetConvertorPlugin from ayon_core.hosts.maya.api import plugin from ayon_core.hosts.maya.api.lib import read -from ayon_core.client import get_asset_by_name - from maya import cmds from maya.app.renderSetup.model import renderSetup @@ -142,12 +142,21 @@ class MayaLegacyConvertor(SubsetConvertorPlugin, # recreate product name as without it would be # `renderingMain` vs correct `renderMain` project_name = self.create_context.get_current_project_name() - asset_doc = get_asset_by_name(project_name, - original_data["asset"]) + folder_entities = list(ayon_api.get_folders( + project_name, folder_names=[original_data["asset"]] + )) + if not folder_entities: + cmds.delete(instance_node) + continue + folder_entity = folder_entities[0] + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], data["task"] + ) + product_name = creator.get_product_name( project_name, - asset_doc, - data["task"], + folder_entity, + task_entity, original_data["variant"], ) original_data["productName"] = product_name diff --git a/client/ayon_core/hosts/maya/plugins/create/create_multishot_layout.py b/client/ayon_core/hosts/maya/plugins/create/create_multishot_layout.py index c640c9f52a..7216236719 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_multishot_layout.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_multishot_layout.py @@ -1,11 +1,13 @@ +import collections + from ayon_api import ( get_folder_by_name, get_folder_by_path, get_folders, + get_tasks, ) from maya import cmds # noqa: F401 -from ayon_core.client import get_assets from ayon_core.hosts.maya.api import plugin from ayon_core.lib import BoolDef, EnumDef, TextDef from ayon_core.pipeline import ( @@ -39,7 +41,7 @@ class CreateMultishotLayout(plugin.MayaCreator): Todo: `get_folder_by_name` should be switched to `get_folder_by_path` once the fork to pure AYON is done. - Warning: this will not work for projects where the asset name + Warning: this will not work for projects where the folder name is not unique across the project until the switch mentioned above is done. """ @@ -128,10 +130,18 @@ class CreateMultishotLayout(plugin.MayaCreator): raise CreatorError( f"Creator {layout_creator_id} not found.") - # Get OpenPype style asset documents for the shots - op_asset_docs = get_assets( - self.project_name, [s["id"] for s in shots]) - asset_docs_by_id = {doc["_id"]: doc for doc in op_asset_docs} + folder_ids = {s["id"] for s in shots} + folder_entities = get_folders(self.project_name, folder_ids) + task_entities = get_tasks( + self.project_name, folder_ids=folder_ids + ) + task_entities_by_folder_id = collections.defaultdict(dict) + for task_entity in task_entities: + folder_id = task_entity["folderId"] + task_name = task_entity["name"] + task_entities_by_folder_id[folder_id][task_name] = task_entity + + folder_entities_by_id = {fe["id"]: fe for fe in folder_entities} for shot in shots: # we are setting shot name to be displayed in the sequencer to # `shot name (shot label)` if the label is set, otherwise just @@ -141,12 +151,15 @@ class CreateMultishotLayout(plugin.MayaCreator): continue # get task for shot - asset_doc = asset_docs_by_id[shot["id"]] + folder_id = shot["id"] + folder_entity = folder_entities_by_id[folder_id] + task_entities = task_entities_by_folder_id[folder_id] - tasks = asset_doc.get("data").get("tasks").keys() - layout_task = None - if pre_create_data["taskName"] in tasks: - layout_task = pre_create_data["taskName"] + layout_task_name = None + layout_task_entity = None + if pre_create_data["taskName"] in task_entities: + layout_task_name = pre_create_data["taskName"] + layout_task_entity = task_entities[layout_task_name] shot_name = f"{shot['name']}%s" % ( f" ({shot['label']})" if shot["label"] else "") @@ -160,14 +173,14 @@ class CreateMultishotLayout(plugin.MayaCreator): "folderPath": shot["path"], "variant": layout_creator.get_default_variant() } - if layout_task: - instance_data["task"] = layout_task + if layout_task_name: + instance_data["task"] = layout_task_name layout_creator.create( product_name=layout_creator.get_product_name( self.project_name, - asset_doc, - self.create_context.get_current_task_name(), + folder_entity, + layout_task_entity, layout_creator.get_default_variant(), ), instance_data=instance_data, @@ -177,7 +190,7 @@ class CreateMultishotLayout(plugin.MayaCreator): ) def get_related_shots(self, folder_path: str): - """Get all shots related to the current asset. + """Get all shots related to the current folder. Get all folders of type Shot under specified folder. diff --git a/client/ayon_core/hosts/maya/plugins/create/create_review.py b/client/ayon_core/hosts/maya/plugins/create/create_review.py index c4fa045427..8a2f2df745 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_review.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_review.py @@ -1,6 +1,7 @@ import json from maya import cmds +import ayon_api from ayon_core.hosts.maya.api import ( lib, @@ -12,7 +13,6 @@ from ayon_core.lib import ( EnumDef ) from ayon_core.pipeline import CreatedInstance -from ayon_core.client import get_asset_by_name TRANSPARENCIES = [ "preset", @@ -43,12 +43,17 @@ class CreateReview(plugin.MayaCreator): members = cmds.ls(selection=True) project_name = self.project_name - asset_name = instance_data["folderPath"] - asset_doc = get_asset_by_name(project_name, asset_name) + folder_path = instance_data["folderPath"] task_name = instance_data["task"] + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path, fields={"id"} + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name, fields={"taskType"} + ) preset = lib.get_capture_preset( task_name, - asset_doc["data"]["tasks"][task_name]["type"], + task_entity["taskType"], product_name, self.project_settings, self.log @@ -91,9 +96,9 @@ class CreateReview(plugin.MayaCreator): defs = lib.collect_animation_defs() - # Option for using Maya or asset frame range in settings. + # Option for using Maya or folder frame range in settings. if not self.useMayaTimeline: - # Update the defaults to be the asset frame range + # Update the defaults to be the folder frame range frame_range = lib.get_frame_range() defs_by_key = {attr_def.key: attr_def for attr_def in defs} for key, value in frame_range.items(): @@ -106,13 +111,13 @@ class CreateReview(plugin.MayaCreator): defs.extend([ NumberDef("review_width", label="Review width", - tooltip="A value of zero will use the asset resolution.", + tooltip="A value of zero will use the folder resolution.", decimals=0, minimum=0, default=0), NumberDef("review_height", label="Review height", - tooltip="A value of zero will use the asset resolution.", + tooltip="A value of zero will use the folder resolution.", decimals=0, minimum=0, default=0), diff --git a/client/ayon_core/hosts/maya/plugins/create/create_unreal_skeletalmesh.py b/client/ayon_core/hosts/maya/plugins/create/create_unreal_skeletalmesh.py index 8815c4d23d..9ded28b812 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_unreal_skeletalmesh.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_unreal_skeletalmesh.py @@ -28,16 +28,27 @@ class CreateUnrealSkeletalMesh(plugin.MayaCreator): self.joint_hints = set(settings.get("joint_hints", [])) def get_dynamic_data( - self, project_name, asset_doc, task_name, variant, host_name, instance + self, + project_name, + folder_entity, + task_entity, + variant, + host_name, + instance ): """ The default product name templates for Unreal include {asset} and thus we should pass that along as dynamic data. """ dynamic_data = super(CreateUnrealSkeletalMesh, self).get_dynamic_data( - project_name, asset_doc, task_name, variant, host_name, instance + project_name, + folder_entity, + task_entity, + variant, + host_name, + instance ) - dynamic_data["asset"] = asset_doc["name"] + dynamic_data["asset"] = folder_entity["name"] return dynamic_data def create(self, product_name, instance_data, pre_create_data): diff --git a/client/ayon_core/hosts/maya/plugins/create/create_unreal_staticmesh.py b/client/ayon_core/hosts/maya/plugins/create/create_unreal_staticmesh.py index 58ad1e4133..1991f92915 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_unreal_staticmesh.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_unreal_staticmesh.py @@ -21,16 +21,27 @@ class CreateUnrealStaticMesh(plugin.MayaCreator): self.collision_prefixes = settings["collision_prefixes"] def get_dynamic_data( - self, project_name, asset_doc, task_name, variant, host_name, instance + self, + project_name, + folder_entity, + task_entity, + variant, + host_name, + instance ): """ The default product name templates for Unreal include {asset} and thus we should pass that along as dynamic data. """ dynamic_data = super(CreateUnrealStaticMesh, self).get_dynamic_data( - project_name, asset_doc, task_name, variant, host_name, instance + project_name, + folder_entity, + task_entity, + variant, + host_name, + instance ) - dynamic_data["asset"] = asset_doc["name"] + dynamic_data["asset"] = folder_entity["name"] return dynamic_data def create(self, product_name, instance_data, pre_create_data): diff --git a/client/ayon_core/hosts/maya/plugins/create/create_workfile.py b/client/ayon_core/hosts/maya/plugins/create/create_workfile.py index 1c6922f942..f636ed7b74 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_workfile.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- """Creator plugin for creating workfiles.""" +import ayon_api + from ayon_core.pipeline import CreatedInstance, AutoCreator -from ayon_core.client import get_asset_by_name, get_asset_name_identifier from ayon_core.hosts.maya.api import plugin from maya import cmds @@ -25,34 +26,38 @@ class CreateWorkfile(plugin.MayaCreatorBase, AutoCreator): ), None) project_name = self.project_name - asset_name = self.create_context.get_current_folder_path() + folder_path = self.create_context.get_current_folder_path() task_name = self.create_context.get_current_task_name() host_name = self.create_context.host_name - if current_instance is None: - current_instance_asset = None - else: - current_instance_asset = current_instance["folderPath"] + current_folder_path = None + if current_instance is not None: + current_folder_path = current_instance["folderPath"] if current_instance is None: - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, host_name, ) data = { - "folderPath": asset_name, + "folderPath": folder_path, "task": task_name, "variant": variant } data.update( self.get_dynamic_data( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, host_name, current_instance) @@ -63,21 +68,25 @@ class CreateWorkfile(plugin.MayaCreatorBase, AutoCreator): ) self._add_instance_to_context(current_instance) elif ( - current_instance_asset != asset_name + current_folder_path != folder_path or current_instance["task"] != task_name ): # Update instance context if is not the same - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, host_name, ) - asset_name = get_asset_name_identifier(asset_doc) - current_instance["folderPath"] = asset_name + current_instance["folderPath"] = folder_entity["path"] current_instance["task"] = task_name current_instance["productName"] = product_name diff --git a/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py b/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py index 8fd3ad4979..6a3d488dc6 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py @@ -54,10 +54,10 @@ class ArnoldStandinLoader(load.LoaderPlugin): self.log.info("version_data: {}\n".format(version_data)) - asset = context['asset']['name'] + folder_name = context["folder"]["name"] namespace = namespace or unique_namespace( - asset + "_", - prefix="_" if asset[0].isdigit() else "", + folder_name + "_", + prefix="_" if folder_name[0].isdigit() else "", suffix="_", ) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_assembly.py b/client/ayon_core/hosts/maya/plugins/load/load_assembly.py index 1f06655dad..7ab7fc4207 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_assembly.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_assembly.py @@ -21,11 +21,10 @@ class AssemblyLoader(load.LoaderPlugin): color = "orange" def load(self, context, name, namespace, data): - - asset = context['asset']['name'] + folder_name = context["folder"]["name"] namespace = namespace or unique_namespace( - asset + "_", - prefix="_" if asset[0].isdigit() else "", + folder_name + "_", + prefix="_" if folder_name[0].isdigit() else "", suffix="_", ) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_audio.py b/client/ayon_core/hosts/maya/plugins/load/load_audio.py index df811a585c..df17214d34 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_audio.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_audio.py @@ -30,10 +30,10 @@ class AudioLoader(load.LoaderPlugin): displaySound=True ) - asset = context["asset"]["name"] + folder_name = context["folder"]["name"] namespace = namespace or unique_namespace( - asset + "_", - prefix="_" if asset[0].isdigit() else "", + folder_name + "_", + prefix="_" if folder_name[0].isdigit() else "", suffix="_", ) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_gpucache.py b/client/ayon_core/hosts/maya/plugins/load/load_gpucache.py index 9453c9c9c6..aacbf2db7c 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_gpucache.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_gpucache.py @@ -24,11 +24,10 @@ class GpuCacheLoader(load.LoaderPlugin): color = "orange" def load(self, context, name, namespace, data): - - asset = context['asset']['name'] + folder_name = context["folder"]["name"] namespace = namespace or unique_namespace( - asset + "_", - prefix="_" if asset[0].isdigit() else "", + folder_name + "_", + prefix="_" if folder_name[0].isdigit() else "", suffix="_", ) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_image.py b/client/ayon_core/hosts/maya/plugins/load/load_image.py index 7b324986f0..b55fb76a4d 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_image.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_image.py @@ -114,11 +114,10 @@ class FileNodeLoader(load.LoaderPlugin): ] def load(self, context, name, namespace, data): - - asset = context['asset']['name'] + folder_name = context["folder"]["name"] namespace = namespace or unique_namespace( - asset + "_", - prefix="_" if asset[0].isdigit() else "", + folder_name + "_", + prefix="_" if folder_name[0].isdigit() else "", suffix="_", ) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_image_plane.py b/client/ayon_core/hosts/maya/plugins/load/load_image_plane.py index 2366f6edd7..43cca4f35d 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_image_plane.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_image_plane.py @@ -1,14 +1,8 @@ from qtpy import QtWidgets, QtCore -from ayon_core.client import ( - get_asset_by_id, - get_subset_by_id, - get_version_by_id, -) from ayon_core.pipeline import ( load, get_representation_path, - get_current_project_name, ) from ayon_core.hosts.maya.api.pipeline import containerise from ayon_core.hosts.maya.api.lib import ( @@ -102,10 +96,10 @@ class ImagePlaneLoader(load.LoaderPlugin): def load(self, context, name, namespace, data, options=None): image_plane_depth = 1000 - asset = context['asset']['name'] + folder_name = context["folder"]["name"] namespace = namespace or unique_namespace( - asset + "_", - prefix="_" if asset[0].isdigit() else "", + folder_name + "_", + prefix="_" if folder_name[0].isdigit() else "", suffix="_", ) @@ -206,7 +200,7 @@ class ImagePlaneLoader(load.LoaderPlugin): ) def update(self, container, context): - asset_doc = context["asset"] + folder_entity = context["folder"] repre_doc = context["representation"] members = get_container_members(container) @@ -223,8 +217,8 @@ class ImagePlaneLoader(load.LoaderPlugin): type="string") # Set frame range. - start_frame = asset_doc["data"]["frameStart"] - end_frame = asset_doc["data"]["frameEnd"] + start_frame = folder_entity["attrib"]["frameStart"] + end_frame = folder_entity["attrib"]["frameEnd"] for attr, value in { "frameOffset": 0, diff --git a/client/ayon_core/hosts/maya/plugins/load/load_maya_usd.py b/client/ayon_core/hosts/maya/plugins/load/load_maya_usd.py index cb9fde7b33..5bd4afb3af 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_maya_usd.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_maya_usd.py @@ -25,10 +25,10 @@ class MayaUsdLoader(load.LoaderPlugin): color = "orange" def load(self, context, name=None, namespace=None, options=None): - asset = context['asset']['name'] + folder_name = context["folder"]["name"] namespace = namespace or unique_namespace( - asset + "_", - prefix="_" if asset[0].isdigit() else "", + folder_name + "_", + prefix="_" if folder_name[0].isdigit() else "", suffix="_", ) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd.py b/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd.py index 64e6048c31..a482b406e1 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd.py @@ -29,10 +29,10 @@ class MultiverseUsdLoader(load.LoaderPlugin): color = "orange" def load(self, context, name=None, namespace=None, options=None): - asset = context['asset']['name'] + folder_name = context["folder"]["name"] namespace = namespace or unique_namespace( - asset + "_", - prefix="_" if asset[0].isdigit() else "", + folder_name + "_", + prefix="_" if folder_name[0].isdigit() else "", suffix="_", ) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_redshift_proxy.py b/client/ayon_core/hosts/maya/plugins/load/load_redshift_proxy.py index feb63ae4be..4ec4dc2b2d 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_redshift_proxy.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_redshift_proxy.py @@ -37,10 +37,10 @@ class RedshiftProxyLoader(load.LoaderPlugin): except ValueError: product_type = "redshiftproxy" - asset_name = context['asset']["name"] + folder_name = context["folder"]["name"] namespace = namespace or unique_namespace( - asset_name + "_", - prefix="_" if asset_name[0].isdigit() else "", + folder_name + "_", + prefix="_" if folder_name[0].isdigit() else "", suffix="_", ) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_rendersetup.py b/client/ayon_core/hosts/maya/plugins/load/load_rendersetup.py index 58f161afc1..167f8aa833 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_rendersetup.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_rendersetup.py @@ -37,10 +37,10 @@ class RenderSetupLoader(load.LoaderPlugin): # from ayon_core.hosts.maya.api.lib import namespaced - asset = context['asset']['name'] + folder_name = context["folder"]["name"] namespace = namespace or lib.unique_namespace( - asset + "_", - prefix="_" if asset[0].isdigit() else "", + folder_name + "_", + prefix="_" if folder_name[0].isdigit() else "", suffix="_", ) path = self.filepath_from_context(context) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py index 3eec09eb7d..516fd7f496 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py @@ -37,11 +37,10 @@ class LoadVDBtoArnold(load.LoaderPlugin): self.log.error("Encountered exception:\n%s" % exc) return - asset = context['asset'] - asset_name = asset["name"] + folder_name = context["folder"]["name"] namespace = namespace or unique_namespace( - asset_name + "_", - prefix="_" if asset_name[0].isdigit() else "", + folder_name + "_", + prefix="_" if folder_name[0].isdigit() else "", suffix="_", ) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_redshift.py b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_redshift.py index f58d9e5565..c5eadb48e6 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_redshift.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_redshift.py @@ -55,12 +55,10 @@ class LoadVDBtoRedShift(load.LoaderPlugin): "set the render engine to '%s'" % compatible) - asset = context['asset'] - - asset_name = asset["name"] + folder_name = context["folder"]["name"] namespace = namespace or unique_namespace( - asset_name + "_", - prefix="_" if asset_name[0].isdigit() else "", + folder_name + "_", + prefix="_" if folder_name[0].isdigit() else "", suffix="_", ) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py index 6e82bbd5e2..590cb6b975 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py @@ -116,11 +116,10 @@ class LoadVDBtoVRay(load.LoaderPlugin): "See Preferences > Display > Viewport 2.0 to " "set the render engine to '%s'" % compatible) - asset = context['asset'] - asset_name = asset["name"] + folder_name = context["folder"]["name"] namespace = namespace or unique_namespace( - asset_name + "_", - prefix="_" if asset_name[0].isdigit() else "", + folder_name + "_", + prefix="_" if folder_name[0].isdigit() else "", suffix="_", ) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py b/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py index 86ea8004ba..97f45bbaa3 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py @@ -58,10 +58,10 @@ class VRayProxyLoader(load.LoaderPlugin): if not filename: filename = self.filepath_from_context(context) - asset_name = context['asset']["name"] + folder_name = context["folder"]["name"] namespace = namespace or unique_namespace( - asset_name + "_", - prefix="_" if asset_name[0].isdigit() else "", + folder_name + "_", + prefix="_" if folder_name[0].isdigit() else "", suffix="_", ) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py b/client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py index a5bfd9dcc3..da10aaf719 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py @@ -31,10 +31,10 @@ class VRaySceneLoader(load.LoaderPlugin): except ValueError: product_type = "vrayscene_layer" - asset_name = context['asset']["name"] + folder_name = context["folder"]["name"] namespace = namespace or unique_namespace( - asset_name + "_", - prefix="_" if asset_name[0].isdigit() else "", + folder_name + "_", + prefix="_" if folder_name[0].isdigit() else "", suffix="_", ) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py b/client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py index 1c6423a8d7..671230583d 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py @@ -62,9 +62,9 @@ class YetiCacheLoader(load.LoaderPlugin): product_type = "yeticache" # Build namespace - asset = context["asset"] + folder_name = context["folder"]["name"] if namespace is None: - namespace = self.create_namespace(asset["name"]) + namespace = self.create_namespace(folder_name) # Ensure Yeti is loaded if not cmds.pluginInfo("pgYetiMaya", query=True, loaded=True): @@ -223,15 +223,15 @@ class YetiCacheLoader(load.LoaderPlugin): self.update(container, context) # helper functions - def create_namespace(self, asset): + def create_namespace(self, folder_name): """Create a unique namespace Args: asset (dict): asset information """ - asset_name = "{}_".format(asset) - prefix = "_" if asset_name[0].isdigit()else "" + asset_name = "{}_".format(folder_name) + prefix = "_" if asset_name[0].isdigit() else "" namespace = lib.unique_namespace( asset_name, prefix=prefix, diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_review.py b/client/ayon_core/hosts/maya/plugins/publish/collect_review.py index 58d02294c5..588ed862e1 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/collect_review.py +++ b/client/ayon_core/hosts/maya/plugins/publish/collect_review.py @@ -117,13 +117,13 @@ class CollectReview(pyblish.api.InstancePlugin): else: project_name = instance.context.data["projectName"] - asset_doc = instance.context.data['assetEntity'] + folder_entity = instance.context.data["folderEntity"] task = instance.context.data["task"] legacy_product_name = task + 'Review' subset_doc = get_subset_by_name( project_name, legacy_product_name, - asset_doc["_id"], + folder_entity["id"], fields=["_id"] ) if subset_doc: diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_frame_range.py b/client/ayon_core/hosts/maya/plugins/publish/validate_frame_range.py index 5c5b691f9d..5736e726e9 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_frame_range.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_frame_range.py @@ -80,7 +80,7 @@ class ValidateFrameRange(pyblish.api.InstancePlugin, return if (inst_start != frame_start_handle): errors.append("Instance start frame [ {} ] doesn't " - "match the one set on asset [ {} ]: " + "match the one set on folder [ {} ]: " "{}/{}/{}/{} (handle/start/end/handle)".format( inst_start, frame_start_handle, @@ -89,7 +89,7 @@ class ValidateFrameRange(pyblish.api.InstancePlugin, if (inst_end != frame_end_handle): errors.append("Instance end frame [ {} ] doesn't " - "match the one set on asset [ {} ]: " + "match the one set on folder [ {} ]: " "{}/{}/{}/{} (handle/start/end/handle)".format( inst_end, frame_end_handle, @@ -105,7 +105,7 @@ class ValidateFrameRange(pyblish.api.InstancePlugin, for label, values in checks.items(): if values[0] != values[1]: errors.append( - "{} on instance ({}) does not match with the asset " + "{} on instance ({}) does not match with the folder " "({}).".format(label.title(), values[1], values[0]) ) @@ -119,7 +119,7 @@ class ValidateFrameRange(pyblish.api.InstancePlugin, @classmethod def repair(cls, instance): """ - Repair instance container to match asset data. + Repair instance container to match folder data. """ if "renderlayer" in instance.data.get("families"): diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_instance_in_context.py b/client/ayon_core/hosts/maya/plugins/publish/validate_instance_in_context.py index 43b4f06e3f..c5a3b1659d 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_instance_in_context.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_instance_in_context.py @@ -37,22 +37,22 @@ class ValidateInstanceInContext(pyblish.api.InstancePlugin, if not self.is_active(instance.data): return - asset = instance.data.get("folderPath") - context_asset = self.get_context_asset(instance) - if asset != context_asset: + folder_path = instance.data.get("folderPath") + context_folder_path = self.get_context_folder_path(instance) + if folder_path != context_folder_path: raise PublishValidationError( message=( - "Instance '{}' publishes to different asset than current " - "context: {}. Current context: {}".format( - instance.name, asset, context_asset + "Instance '{}' publishes to different folder than current" + " context: {}. Current context: {}".format( + instance.name, folder_path, context_folder_path ) ), description=( - "## Publishing to a different asset\n" + "## Publishing to a different folder\n" "There are publish instances present which are publishing " - "into a different asset than your current context.\n\n" + "into a different folder than your current context.\n\n" "Usually this is not what you want but there can be cases " - "where you might want to publish into another asset or " + "where you might want to publish into another folder or " "shot. If that's the case you can disable the validation " "on the instance to ignore it." ) @@ -64,14 +64,14 @@ class ValidateInstanceInContext(pyblish.api.InstancePlugin, @classmethod def repair(cls, instance): - context_asset = cls.get_context_asset(instance) + context_folder_path = cls.get_context_folder_path(instance) instance_node = instance.data["instance_node"] cmds.setAttr( "{}.folderPath".format(instance_node), - context_asset, + context_folder_path, type="string" ) @staticmethod - def get_context_asset(instance): + def get_context_folder_path(instance): return instance.context.data["folderPath"] diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_maya_units.py b/client/ayon_core/hosts/maya/plugins/publish/validate_maya_units.py index eca27d95da..1c00d86868 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_maya_units.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_maya_units.py @@ -3,7 +3,7 @@ import maya.cmds as cmds import pyblish.api import ayon_core.hosts.maya.api.lib as mayalib -from ayon_core.pipeline.context_tools import get_current_project_asset +from ayon_core.pipeline.context_tools import get_current_project_folder from ayon_core.pipeline.publish import ( RepairContextAction, ValidateSceneOrder, @@ -59,8 +59,8 @@ class ValidateMayaUnits(pyblish.api.ContextPlugin): fps = context.data.get('fps') - asset_doc = context.data["assetEntity"] - asset_fps = mayalib.convert_to_maya_fps(asset_doc["data"]["fps"]) + folder_attributes = context.data["folderEntity"]["attrib"] + folder_fps = mayalib.convert_to_maya_fps(folder_attributes["fps"]) self.log.info('Units (linear): {0}'.format(linearunits)) self.log.info('Units (angular): {0}'.format(angularunits)) @@ -91,10 +91,10 @@ class ValidateMayaUnits(pyblish.api.ContextPlugin): "current_value": angularunits }) - if self.validate_fps and fps and fps != asset_fps: + if self.validate_fps and fps and fps != folder_fps: invalid.append({ "setting": "FPS", - "required_value": asset_fps, + "required_value": folder_fps, "current_value": fps }) @@ -127,7 +127,6 @@ class ValidateMayaUnits(pyblish.api.ContextPlugin): cls.log.debug(current_linear) cls.log.info("Setting time unit to match project") - # TODO replace query with using 'context.data["assetEntity"]' - asset_doc = get_current_project_asset() - asset_fps = asset_doc["data"]["fps"] - mayalib.set_scene_fps(asset_fps) + # TODO replace query with using 'context.data["folderEntity"]' + folder_entity = get_current_project_folder() + mayalib.set_scene_fps(folder_entity["attrib"]["fps"]) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_in_database.py b/client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_in_database.py index de86ffe575..65a779f3f0 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_in_database.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_in_database.py @@ -1,14 +1,14 @@ import pyblish.api +import ayon_api import ayon_core.hosts.maya.api.action -from ayon_core.client import get_assets from ayon_core.hosts.maya.api import lib from ayon_core.pipeline.publish import ( PublishValidationError, ValidatePipelineOrder) class ValidateNodeIdsInDatabase(pyblish.api.InstancePlugin): - """Validate if the CB Id is related to an asset in the database + """Validate if the CB Id is related to an folder in the database All nodes with the `cbId` attribute will be validated to ensure that the loaded asset in the scene is related to the current project. @@ -30,7 +30,7 @@ class ValidateNodeIdsInDatabase(pyblish.api.InstancePlugin): invalid = self.get_invalid(instance) if invalid: raise PublishValidationError( - ("Found asset IDs which are not related to " + ("Found folder ids which are not related to " "current project in instance: `{}`").format(instance.name)) @classmethod @@ -44,10 +44,10 @@ class ValidateNodeIdsInDatabase(pyblish.api.InstancePlugin): # check ids against database ids project_name = instance.context.data["projectName"] - asset_docs = get_assets(project_name, fields=["_id"]) - db_asset_ids = { - str(asset_doc["_id"]) - for asset_doc in asset_docs + folder_entities = ayon_api.get_folders(project_name, fields={"id"}) + folder_ids = { + folder_entity["id"] + for folder_entity in folder_entities } # Get all asset IDs @@ -58,9 +58,9 @@ class ValidateNodeIdsInDatabase(pyblish.api.InstancePlugin): if not cb_id: continue - asset_id = cb_id.split(":", 1)[0] - if asset_id not in db_asset_ids: - cls.log.error("`%s` has unassociated asset ID" % node) + folder_id = cb_id.split(":", 1)[0] + if folder_id not in folder_ids: + cls.log.error("`%s` has unassociated folder id" % node) invalid.append(node) return invalid diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_related.py b/client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_related.py index b2db535fa6..606abee3d2 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_related.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_related.py @@ -8,7 +8,8 @@ from ayon_core.pipeline.publish import ( class ValidateNodeIDsRelated(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): - """Validate nodes have a related Colorbleed Id to the instance.data[asset] + """Validate nodes have a related Colorbleed Id to the + instance.data[folderPath] """ @@ -31,27 +32,28 @@ class ValidateNodeIDsRelated(pyblish.api.InstancePlugin, # Ensure all nodes have a cbId invalid = self.get_invalid(instance) if invalid: - raise PublishValidationError( - ("Nodes IDs found that are not related to asset " - "'{}' : {}").format(instance.data['asset'], invalid)) + raise PublishValidationError(( + "Nodes IDs found that are not related to folder '{}' : {}" + ).format( + instance.data["folderPath"], invalid + )) @classmethod def get_invalid(cls, instance): """Return the member nodes that are invalid""" invalid = list() - asset_id = str(instance.data['assetEntity']["_id"]) + folder_id = instance.data["folderEntity"]["id"] # We do want to check the referenced nodes as we it might be # part of the end product for node in instance: - _id = lib.get_id(node) if not _id: continue - node_asset_id = _id.split(":", 1)[0] - if node_asset_id != asset_id: + node_folder_id = _id.split(":", 1)[0] + if node_folder_id != folder_id: invalid.append(node) return invalid diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_unique.py b/client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_unique.py index eeede82caf..6c5cd26259 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_unique.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_unique.py @@ -32,10 +32,10 @@ class ValidateNodeIdsUnique(pyblish.api.InstancePlugin): # Ensure all nodes have a cbId invalid = self.get_invalid(instance) if invalid: - label = "Nodes found with non-unique asset IDs" + label = "Nodes found with non-unique folder ids" raise PublishValidationError( message="{}: {}".format(label, invalid), - title="Non-unique asset ids on nodes", + title="Non-unique folder ids on nodes", description="{}\n- {}".format(label, "\n- ".join(sorted(invalid))) ) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_renderlayer_aovs.py b/client/ayon_core/hosts/maya/plugins/publish/validate_renderlayer_aovs.py index 900e5444a9..ba7d674e00 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_renderlayer_aovs.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_renderlayer_aovs.py @@ -37,11 +37,11 @@ class ValidateRenderLayerAOVs(pyblish.api.InstancePlugin): invalid = [] project_name = instance.context.data["projectName"] - asset_doc = instance.data["assetEntity"] + folder_entity = instance.data["folderEntity"] render_passes = instance.data.get("renderPasses", []) for render_pass in render_passes: is_valid = self.validate_product_registered( - project_name, asset_doc, render_pass + project_name, folder_entity, render_pass ) if not is_valid: invalid.append(render_pass) @@ -49,10 +49,10 @@ class ValidateRenderLayerAOVs(pyblish.api.InstancePlugin): return invalid def validate_product_registered( - self, project_name, asset_doc, product_name + self, project_name, folder_entity, product_name ): - """Check if product is registered in the database under the asset""" + """Check if product is registered in the database under the folder""" return get_subset_by_name( - project_name, product_name, asset_doc["_id"], fields=["_id"] + project_name, product_name, folder_entity["id"], fields=["_id"] ) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_resolution.py b/client/ayon_core/hosts/maya/plugins/publish/validate_resolution.py index 1a35f1b764..1e5a9a944c 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_resolution.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_resolution.py @@ -37,7 +37,7 @@ class ValidateResolution(pyblish.api.InstancePlugin, @classmethod def get_invalid_resolution(cls, instance): - width, height, pixelAspect = cls.get_db_resolution(instance) + width, height, pixelAspect = cls.get_folder_resolution(instance) current_renderer = instance.data["renderer"] layer = instance.data["renderlayer"] invalid = False @@ -68,7 +68,7 @@ class ValidateResolution(pyblish.api.InstancePlugin, if current_width != width or current_height != height: cls.log.error( "Render resolution {}x{} does not match " - "asset resolution {}x{}".format( + "folder resolution {}x{}".format( current_width, current_height, width, height )) @@ -76,26 +76,24 @@ class ValidateResolution(pyblish.api.InstancePlugin, if current_pixelAspect != pixelAspect: cls.log.error( "Render pixel aspect {} does not match " - "asset pixel aspect {}".format( + "folder pixel aspect {}".format( current_pixelAspect, pixelAspect )) invalid = True return invalid @classmethod - def get_db_resolution(cls, instance): - asset_doc = instance.data["assetEntity"] - project_entity = instance.context.data["projectEntity"] - for data in [asset_doc["data"], project_entity["attrib"]]: - if ( - "resolutionWidth" in data and - "resolutionHeight" in data and - "pixelAspect" in data - ): - width = data["resolutionWidth"] - height = data["resolutionHeight"] - pixelAspect = data["pixelAspect"] - return int(width), int(height), float(pixelAspect) + def get_folder_resolution(cls, instance): + folder_attributes = instance.data["folderEntity"]["attrib"] + if ( + "resolutionWidth" in folder_attributes + and "resolutionHeight" in folder_attributes + and "pixelAspect" in folder_attributes + ): + width = folder_attributes["resolutionWidth"] + height = folder_attributes["resolutionHeight"] + pixelAspect = folder_attributes["pixelAspect"] + return int(width), int(height), float(pixelAspect) # Defaults if not found in asset document or project document return 1920, 1080, 1.0 diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_shader_name.py b/client/ayon_core/hosts/maya/plugins/publish/validate_shader_name.py index 86ca0ca400..09c17202c5 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_shader_name.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_shader_name.py @@ -51,7 +51,7 @@ class ValidateShaderName(pyblish.api.InstancePlugin, descendants = cmds.ls(descendants, noIntermediate=True, long=True) shapes = cmds.ls(descendants, type=["nurbsSurface", "mesh"], long=True) - asset_name = instance.data.get("folderPath") + folder_path = instance.data.get("folderPath") # Check the number of connected shadingEngines per shape regex_compile = re.compile(cls.regex) @@ -71,12 +71,12 @@ class ValidateShaderName(pyblish.api.InstancePlugin, cls.log.error(error_message.format(shape, shader)) else: if 'asset' in regex_compile.groupindex: - if m.group('asset') != asset_name: + if m.group('asset') != folder_path: invalid.append(shape) message = error_message - message += " with missing asset name \"{2}\"" + message += " with missing folder path \"{2}\"" cls.log.error( - message.format(shape, shader, asset_name) + message.format(shape, shader, folder_path) ) return invalid diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py b/client/ayon_core/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py index c9860d27a0..88b0ff0e71 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py @@ -100,8 +100,8 @@ class ValidateUnrealStaticMeshName(pyblish.api.InstancePlugin, cl_r = re.compile(regex_collision) - asset_name = instance.data["assetEntity"]["name"] - mesh_name = "{}{}".format(asset_name, + folder_name = instance.data["folderEntity"]["name"] + mesh_name = "{}{}".format(folder_name, instance.data.get("variant", [])) for obj in collision_set: diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/arnold_standin.py b/client/ayon_core/hosts/maya/tools/mayalookassigner/arnold_standin.py index 810e1fc88c..bd2f0baddf 100644 --- a/client/ayon_core/hosts/maya/tools/mayalookassigner/arnold_standin.py +++ b/client/ayon_core/hosts/maya/tools/mayalookassigner/arnold_standin.py @@ -136,20 +136,20 @@ def assign_look(standin, product_name): nodes_by_id = get_nodes_by_id(standin) - # Group by asset id so we run over the look per asset - node_ids_by_asset_id = defaultdict(set) + # Group by folder id so we run over the look per folder + node_ids_by_folder_id = defaultdict(set) for node_id in nodes_by_id: - asset_id = node_id.split(":", 1)[0] - node_ids_by_asset_id[asset_id].add(node_id) + folder_id = node_id.split(":", 1)[0] + node_ids_by_folder_id[folder_id].add(node_id) project_name = get_current_project_name() - for asset_id, node_ids in node_ids_by_asset_id.items(): + for folder_id, node_ids in node_ids_by_folder_id.items(): # Get latest look version version = get_last_version_by_subset_name( project_name, subset_name=product_name, - asset_id=asset_id, + asset_id=folder_id, fields=["_id"] ) if not version: @@ -162,7 +162,7 @@ def assign_look(standin, product_name): shader_nodes, container_node = lib.load_look(version["_id"]) namespace = shader_nodes[0].split(":")[0] - # Get only the node ids and paths related to this asset + # Get only the node ids and paths related to this folder # And get the shader edits the look supplies asset_nodes_by_id = { node_id: nodes_by_id[node_id] for node_id in node_ids diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/commands.py b/client/ayon_core/hosts/maya/tools/mayalookassigner/commands.py index 4375d38316..75c82164c2 100644 --- a/client/ayon_core/hosts/maya/tools/mayalookassigner/commands.py +++ b/client/ayon_core/hosts/maya/tools/mayalookassigner/commands.py @@ -2,9 +2,9 @@ import os import logging from collections import defaultdict +import ayon_api import maya.cmds as cmds -from ayon_core.client import get_assets, get_asset_name_identifier from ayon_core.pipeline import ( remove_container, registered_host, @@ -62,7 +62,7 @@ def get_all_asset_nodes(): return cmds.ls(dag=True, noIntermediate=True, long=True) -def create_asset_id_hash(nodes): +def create_folder_id_hash(nodes): """Create a hash based on cbId attribute value Args: nodes (list): a list of nodes @@ -74,10 +74,10 @@ def create_asset_id_hash(nodes): for node in nodes: # iterate over content of reference node if cmds.nodeType(node) == "reference": - ref_hashes = create_asset_id_hash( + ref_hashes = create_folder_id_hash( list(set(cmds.referenceQuery(node, nodes=True, dp=True)))) - for asset_id, ref_nodes in ref_hashes.items(): - node_id_hash[asset_id] += ref_nodes + for folder_id, ref_nodes in ref_hashes.items(): + node_id_hash[folder_id] += ref_nodes elif cmds.pluginInfo('vrayformaya', query=True, loaded=True) and cmds.nodeType( node) == "VRayProxy": @@ -95,8 +95,8 @@ def create_asset_id_hash(nodes): if value is None: continue - asset_id = value.split(":")[0] - node_id_hash[asset_id].append(node) + folder_id = value.split(":")[0] + node_id_hash[folder_id].append(node) return dict(node_id_hash) @@ -104,10 +104,10 @@ def create_asset_id_hash(nodes): def create_items_from_nodes(nodes): """Create an item for the view based the container and content of it - It fetches the look document based on the asset ID found in the content. + It fetches the look document based on the folder id found in the content. The item will contain all important information for the tool to work. - If there is an asset ID which is not registered in the project's collection + If there is an folder id which is not registered in the project's collection it will log a warning message. Args: @@ -118,54 +118,55 @@ def create_items_from_nodes(nodes): """ - asset_view_items = [] + folder_view_items = [] - id_hashes = create_asset_id_hash(nodes) + id_hashes = create_folder_id_hash(nodes) if not id_hashes: log.warning("No id hashes") - return asset_view_items + return folder_view_items project_name = get_current_project_name() - asset_ids = set(id_hashes.keys()) - fields = {"_id", "name", "data.parents"} - asset_docs = get_assets(project_name, asset_ids, fields=fields) - asset_docs_by_id = { - str(asset_doc["_id"]): asset_doc - for asset_doc in asset_docs + folder_ids = set(id_hashes.keys()) + + folder_entities = ayon_api.get_folders( + project_name, folder_ids, fields={"id", "path"} + ) + folder_entities_by_id = { + folder_entity["id"]: folder_entity + for folder_entity in folder_entities } - for asset_id, id_nodes in id_hashes.items(): - asset_doc = asset_docs_by_id.get(asset_id) - # Skip if asset id is not found - if not asset_doc: + for folder_id, id_nodes in id_hashes.items(): + folder_entity = folder_entities_by_id.get(folder_id) + # Skip if folder id is not found + if not folder_entity: log.warning( - "Id found on {num} nodes for which no asset is found database," - " skipping '{asset_id}'".format( + "Id found on {num} nodes for which no folder is found database," + " skipping '{folder_id}'".format( num=len(nodes), - asset_id=asset_id + folder_id=folder_id ) ) continue - # Collect available look products for this asset - looks = lib.list_looks(project_name, asset_doc["_id"]) + # Collect available look products for this folder + looks = lib.list_looks(project_name, folder_entity["id"]) - # Collect namespaces the asset is found in + # Collect namespaces the folder is found in namespaces = set() for node in id_nodes: namespace = get_namespace_from_node(node) namespaces.add(namespace) - label = get_asset_name_identifier(asset_doc) - asset_view_items.append({ - "label": label, - "asset": asset_doc, + folder_view_items.append({ + "label": folder_entity["path"], + "folder_entity": folder_entity, "looks": looks, "namespaces": namespaces }) - return asset_view_items + return folder_view_items def remove_unused_looks(): diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/models.py b/client/ayon_core/hosts/maya/tools/mayalookassigner/models.py index 4892125954..f5dad25ff0 100644 --- a/client/ayon_core/hosts/maya/tools/mayalookassigner/models.py +++ b/client/ayon_core/hosts/maya/tools/mayalookassigner/models.py @@ -104,12 +104,12 @@ class LookModel(models.TreeModel): # Collect the assets per look name (from the items of the AssetModel) look_products = defaultdict(list) for asset_item in items: - asset = asset_item["asset"] + folder_entity = asset_item["folder_entity"] for look in asset_item["looks"]: - look_products[look["name"]].append(asset) + look_products[look["name"]].append(folder_entity) for product_name in sorted(look_products.keys()): - assets = look_products[product_name] + folder_entities = look_products[product_name] # Define nice label without "look" prefix for readability label = ( @@ -123,10 +123,10 @@ class LookModel(models.TreeModel): item_node["product"] = product_name # Amount of matching assets for this look - item_node["match"] = len(assets) + item_node["match"] = len(folder_entities) # Store the assets that have this product available - item_node["assets"] = assets + item_node["folder_entities"] = folder_entities self.add_child(item_node) diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/widgets.py b/client/ayon_core/hosts/maya/tools/mayalookassigner/widgets.py index 234a1c149e..6cc5f156e3 100644 --- a/client/ayon_core/hosts/maya/tools/mayalookassigner/widgets.py +++ b/client/ayon_core/hosts/maya/tools/mayalookassigner/widgets.py @@ -3,7 +3,6 @@ from collections import defaultdict from qtpy import QtWidgets, QtCore -from ayon_core.client import get_asset_name_identifier from ayon_core.tools.utils.models import TreeModel from ayon_core.tools.utils.lib import ( preserve_expanded_rows, @@ -110,7 +109,7 @@ class AssetOutliner(QtWidgets.QWidget): self.add_items(items) def get_nodes(self, selection=False): - """Find the nodes in the current scene per asset.""" + """Find the nodes in the current scene per folder.""" items = self.get_selected_items() @@ -119,26 +118,26 @@ class AssetOutliner(QtWidgets.QWidget): nodes = cmds.ls(dag=True, long=True) else: nodes = commands.get_selected_nodes() - id_nodes = commands.create_asset_id_hash(nodes) + id_nodes = commands.create_folder_id_hash(nodes) - # Collect the asset item entries per asset + # Collect the asset item entries per folder # and collect the namespaces we'd like to apply - assets = {} - asset_namespaces = defaultdict(set) + folder_items = {} + namespaces_by_folder_path = defaultdict(set) for item in items: - asset_id = str(item["asset"]["_id"]) - asset_name = get_asset_name_identifier(item["asset"]) - asset_namespaces[asset_name].add(item.get("namespace")) + folder_id = item["folder"]["id"] + folder_path = item["folder"]["path"] + namespaces_by_folder_path[folder_path].add(item.get("namespace")) - if asset_name in assets: + if folder_path in folder_items: continue - assets[asset_name] = item - assets[asset_name]["nodes"] = id_nodes.get(asset_id, []) + folder_items[folder_path] = item + folder_items[folder_path]["nodes"] = id_nodes.get(folder_id, []) # Filter nodes to namespace (if only namespaces were selected) - for asset_name in assets: - namespaces = asset_namespaces[asset_name] + for folder_path in folder_items: + namespaces = namespaces_by_folder_path[folder_path] # When None is present there should be no filtering if None in namespaces: @@ -146,12 +145,12 @@ class AssetOutliner(QtWidgets.QWidget): # Else only namespaces are selected and *not* the top entry so # we should filter to only those namespaces. - nodes = assets[asset_name]["nodes"] + nodes = folder_items[folder_path]["nodes"] nodes = [node for node in nodes if commands.get_namespace_from_node(node) in namespaces] - assets[asset_name]["nodes"] = nodes + folder_items[folder_path]["nodes"] = nodes - return assets + return folder_items def select_asset_from_items(self): """Select nodes from listed asset""" From 7a25e2022f669bbde5668c7f44a3ae69d4362863 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 16:31:17 +0100 Subject: [PATCH 413/573] nuke use folder and task entity --- client/ayon_core/hosts/nuke/api/lib.py | 77 +++++++++---------- .../nuke/plugins/create/workfile_creator.py | 22 ++++-- .../hosts/nuke/plugins/load/load_backdrop.py | 4 +- .../nuke/plugins/load/load_camera_abc.py | 2 +- .../hosts/nuke/plugins/load/load_clip.py | 29 ++++--- .../hosts/nuke/plugins/load/load_effects.py | 10 +-- .../nuke/plugins/load/load_effects_ip.py | 4 +- .../hosts/nuke/plugins/load/load_gizmo.py | 4 +- .../hosts/nuke/plugins/load/load_gizmo_ip.py | 4 +- .../hosts/nuke/plugins/load/load_image.py | 29 ++++--- .../hosts/nuke/plugins/load/load_model.py | 2 +- .../hosts/nuke/plugins/load/load_ociolook.py | 4 +- .../nuke/plugins/load/load_script_precomp.py | 4 +- .../publish/help/validate_asset_context.xml | 8 +- .../plugins/publish/validate_asset_context.py | 6 +- .../publish/validate_script_attributes.py | 24 +++--- 16 files changed, 130 insertions(+), 103 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index 01ddf52dad..3f2621fa6f 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -15,7 +15,6 @@ from qtpy import QtCore, QtWidgets import ayon_api from ayon_core.client import ( - get_asset_by_name, get_versions, get_last_versions, get_representations, @@ -40,7 +39,6 @@ from ayon_core.settings import ( from ayon_core.addon import AddonsManager from ayon_core.pipeline.template_data import get_template_data_with_names from ayon_core.pipeline import ( - discover_legacy_creator_plugins, Anatomy, get_current_host_name, get_current_project_name, @@ -1005,11 +1003,11 @@ def format_anatomy(data): file = script_name() data["version"] = get_version_from_path(file) - asset_name = data["folderPath"] + folder_path = data["folderPath"] task_name = data["task"] host_name = get_current_host_name() context_data = get_template_data_with_names( - project_name, asset_name, task_name, host_name + project_name, folder_path, task_name, host_name ) data.update(context_data) data.update({ @@ -1462,8 +1460,10 @@ class WorkfileSettings(object): Context._project_entity = project_entity self._project_name = project_name - self._asset = get_current_folder_path() - self._asset_entity = get_asset_by_name(project_name, self._asset) + self._folder_path = get_current_folder_path() + self._folder_entity = ayon_api.get_folder_by_path( + project_name, self._folder_path + ) self._root_node = root_node or nuke.root() self._nodes = self.get_nodes(nodes=nodes) @@ -1957,39 +1957,43 @@ Reopening Nuke should synchronize these paths and resolve any discrepancies. self.set_reads_colorspace(read_clrs_inputs) def reset_frame_range_handles(self): - """Set frame range to current asset""" + """Set frame range to current folder.""" - if "data" not in self._asset_entity: - msg = "Asset {} don't have set any 'data'".format(self._asset) + if "attrib" not in self._folder_entity: + msg = "Folder {} don't have set any 'attrib'".format( + self._folder_path + ) log.warning(msg) nuke.message(msg) return - asset_data = self._asset_entity["data"] + folder_attributes = self._folder_entity["attrib"] missing_cols = [] check_cols = ["fps", "frameStart", "frameEnd", "handleStart", "handleEnd"] for col in check_cols: - if col not in asset_data: + if col not in folder_attributes: missing_cols.append(col) if len(missing_cols) > 0: missing = ", ".join(missing_cols) - msg = "'{}' are not set for asset '{}'!".format( - missing, self._asset) + msg = "'{}' are not set for folder '{}'!".format( + missing, self._folder_path) log.warning(msg) nuke.message(msg) return # get handles values - handle_start = asset_data["handleStart"] - handle_end = asset_data["handleEnd"] + handle_start = folder_attributes["handleStart"] + handle_end = folder_attributes["handleEnd"] + frame_start = folder_attributes["frameStart"] + frame_end = folder_attributes["frameEnd"] - fps = float(asset_data["fps"]) - frame_start_handle = int(asset_data["frameStart"]) - handle_start - frame_end_handle = int(asset_data["frameEnd"]) + handle_end + fps = float(folder_attributes["fps"]) + frame_start_handle = frame_start - handle_start + frame_end_handle = frame_end + handle_end self._root_node["lock_range"].setValue(False) self._root_node["fps"].setValue(fps) @@ -2000,10 +2004,7 @@ Reopening Nuke should synchronize these paths and resolve any discrepancies. # update node graph so knobs are updated update_node_graph() - frame_range = '{0}-{1}'.format( - int(asset_data["frameStart"]), - int(asset_data["frameEnd"]) - ) + frame_range = '{0}-{1}'.format(frame_start, frame_end) for node in nuke.allNodes(filter="Viewer"): node['frame_range'].setValue(frame_range) @@ -2030,18 +2031,12 @@ Reopening Nuke should synchronize these paths and resolve any discrepancies. """Set resolution to project resolution.""" log.info("Resetting resolution") project_name = get_current_project_name() - asset_data = self._asset_entity["data"] + folder_attributes = self._folder_entity["attrib"] format_data = { - "width": int(asset_data.get( - 'resolutionWidth', - asset_data.get('resolution_width'))), - "height": int(asset_data.get( - 'resolutionHeight', - asset_data.get('resolution_height'))), - "pixel_aspect": asset_data.get( - 'pixelAspect', - asset_data.get('pixel_aspect', 1)), + "width": folder_attributes["resolutionWidth"], + "height": folder_attributes["resolutionHeight"], + "pixel_aspect": folder_attributes["pixelAspect"], "name": project_name } @@ -2108,7 +2103,11 @@ Reopening Nuke should synchronize these paths and resolve any discrepancies. from .utils import set_context_favorites work_dir = os.getenv("AYON_WORKDIR") - asset = get_current_folder_path() + # TODO validate functionality + # - does expect the structure is '{root}/{project}/{folder}' + # - this used asset name expecting it is unique in project + folder_path = get_current_folder_path() + folder_name = folder_path.split("/")[-1] favorite_items = OrderedDict() # project @@ -2120,13 +2119,13 @@ Reopening Nuke should synchronize these paths and resolve any discrepancies. # add to favorites favorite_items.update({"Project dir": project_dir.replace("\\", "/")}) - # asset - asset_root = os.path.normpath(work_dir.split( - asset)[0]) - # add asset name - asset_dir = os.path.join(asset_root, asset) + "/" + # folder + folder_root = os.path.normpath(work_dir.split( + folder_name)[0]) + # add folder name + folder_dir = os.path.join(folder_root, folder_name) + "/" # add to favorites - favorite_items.update({"Shot dir": asset_dir.replace("\\", "/")}) + favorite_items.update({"Shot dir": folder_dir.replace("\\", "/")}) # workdir favorite_items.update({"Work dir": work_dir.replace("\\", "/")}) diff --git a/client/ayon_core/hosts/nuke/plugins/create/workfile_creator.py b/client/ayon_core/hosts/nuke/plugins/create/workfile_creator.py index de46e02b37..b9d83a2b48 100644 --- a/client/ayon_core/hosts/nuke/plugins/create/workfile_creator.py +++ b/client/ayon_core/hosts/nuke/plugins/create/workfile_creator.py @@ -1,5 +1,6 @@ +import ayon_api + import ayon_core.hosts.nuke.api as api -from ayon_core.client import get_asset_by_name from ayon_core.pipeline import ( AutoCreator, CreatedInstance, @@ -27,27 +28,32 @@ class WorkfileCreator(AutoCreator): ) project_name = self.create_context.get_current_project_name() - asset_name = self.create_context.get_current_folder_path() + folder_path = self.create_context.get_current_folder_path() task_name = self.create_context.get_current_task_name() host_name = self.create_context.host_name - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, self.default_variant, host_name, ) instance_data.update({ - "asset": asset_name, + "folderPath": folder_path, "task": task_name, "variant": self.default_variant }) instance_data.update(self.get_dynamic_data( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, self.default_variant, host_name, instance_data diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py b/client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py index 642e20c979..b97943bd63 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py @@ -43,7 +43,7 @@ class LoadBackdropNodes(load.LoaderPlugin): Arguments: context (dict): context of version name (str): name of the version - namespace (str): asset name + namespace (str): namespace name data (dict): compulsory attribute > not used Returns: @@ -54,7 +54,7 @@ class LoadBackdropNodes(load.LoaderPlugin): version = context['version'] version_data = version.get("data", {}) vname = version.get("name", None) - namespace = namespace or context['asset']['name'] + namespace = namespace or context["folder"]["name"] colorspace = version_data.get("colorspace", None) object_name = "{}_{}".format(name, namespace) diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py b/client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py index e3511a4e8b..4dc68e187b 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py @@ -41,7 +41,7 @@ class AlembicCameraLoader(load.LoaderPlugin): first = version_data.get("frameStart", None) last = version_data.get("frameEnd", None) fps = version_data.get("fps") or nuke.root()["fps"].getValue() - namespace = namespace or context['asset']['name'] + namespace = namespace or context["folder"]["name"] object_name = "{}_{}".format(name, namespace) # prepare data for imprinting diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py index e9e71baa76..32d67910df 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py @@ -131,16 +131,16 @@ class LoadClip(plugin.NukeLoader): first = 1 last = first + duration - # Fallback to asset name when namespace is None + # Fallback to folder name when namespace is None if namespace is None: - namespace = context['asset']['name'] + namespace = context["folder"]["name"] if not filepath: self.log.warning( "Representation id `{}` is failing to load".format(repre_id)) return - read_name = self._get_node_name(representation) + read_name = self._get_node_name(context) # Create the Loader with the filename path set read_node = nuke.createNode( @@ -438,15 +438,26 @@ class LoadClip(plugin.NukeLoader): read_node['frame_mode'].setValue("start at") read_node['frame'].setValue(str(self.script_start)) - def _get_node_name(self, representation): + def _get_node_name(self, context): + folder_entity = context["folder"] + subset_doc = context["subset"] + repre_doc = context["representation"] - repre_cont = representation["context"] + folder_name = folder_entity["name"] + product_name = subset_doc["name"] + repre_cont = repre_doc["context"] name_data = { - "asset": repre_cont["asset"], - "subset": repre_cont["subset"], - "representation": representation["name"], + "folder": { + "name": folder_name, + }, + "product": { + "name": product_name, + }, + "asset": folder_name, + "subset": product_name, + "representation": repre_doc["name"], "ext": repre_cont["representation"], - "id": representation["_id"], + "id": repre_doc["_id"], "class_name": self.__class__.__name__ } diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_effects.py b/client/ayon_core/hosts/nuke/plugins/load/load_effects.py index 3e87c9cf60..389ad7d057 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_effects.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_effects.py @@ -40,7 +40,7 @@ class LoadEffects(load.LoaderPlugin): Arguments: context (dict): context of version name (str): name of the version - namespace (str): asset name + namespace (str): namespace name data (dict): compulsory attribute > not used Returns: @@ -53,7 +53,7 @@ class LoadEffects(load.LoaderPlugin): first = version_data.get("frameStart", None) last = version_data.get("frameEnd", None) workfile_first_frame = int(nuke.root()["first_frame"].getValue()) - namespace = namespace or context['asset']['name'] + namespace = namespace or context["folder"]["name"] colorspace = version_data.get("colorspace", None) object_name = "{}_{}".format(name, namespace) @@ -266,18 +266,18 @@ class LoadEffects(load.LoaderPlugin): self.log.info("updated to version: {}".format(version_doc.get("name"))) - def connect_read_node(self, group_node, asset, subset): + def connect_read_node(self, group_node, namespace, subset): """ Finds read node and selects it Arguments: - asset (str): asset name + namespace (str): namespace name Returns: nuke node: node is selected None: if nothing found """ - search_name = "{0}_{1}".format(asset, subset) + search_name = "{0}_{1}".format(namespace, subset) node = [ n for n in nuke.allNodes(filter="Read") diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py b/client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py index 5c363cddc4..d391cc6c9c 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py @@ -40,7 +40,7 @@ class LoadEffectsInputProcess(load.LoaderPlugin): Arguments: context (dict): context of version name (str): name of the version - namespace (str): asset name + namespace (str): namespace name data (dict): compulsory attribute > not used Returns: @@ -54,7 +54,7 @@ class LoadEffectsInputProcess(load.LoaderPlugin): first = version_data.get("frameStart", None) last = version_data.get("frameEnd", None) workfile_first_frame = int(nuke.root()["first_frame"].getValue()) - namespace = namespace or context['asset']['name'] + namespace = namespace or context["folder"]["name"] colorspace = version_data.get("colorspace", None) object_name = "{}_{}".format(name, namespace) diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py b/client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py index 058228a145..39c52293e2 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py @@ -42,7 +42,7 @@ class LoadGizmo(load.LoaderPlugin): Arguments: context (dict): context of version name (str): name of the version - namespace (str): asset name + namespace (str): namespace name data (dict): compulsory attribute > not used Returns: @@ -55,7 +55,7 @@ class LoadGizmo(load.LoaderPlugin): vname = version.get("name", None) first = version_data.get("frameStart", None) last = version_data.get("frameEnd", None) - namespace = namespace or context['asset']['name'] + namespace = namespace or context["folder"]["name"] colorspace = version_data.get("colorspace", None) object_name = "{}_{}".format(name, namespace) diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py b/client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py index 61e1c34028..061cb9e4f9 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py @@ -44,7 +44,7 @@ class LoadGizmoInputProcess(load.LoaderPlugin): Arguments: context (dict): context of version name (str): name of the version - namespace (str): asset name + namespace (str): namespace name data (dict): compulsory attribute > not used Returns: @@ -57,7 +57,7 @@ class LoadGizmoInputProcess(load.LoaderPlugin): vname = version.get("name", None) first = version_data.get("frameStart", None) last = version_data.get("frameEnd", None) - namespace = namespace or context['asset']['name'] + namespace = namespace or context["folder"]["name"] colorspace = version_data.get("colorspace", None) object_name = "{}_{}".format(name, namespace) diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_image.py b/client/ayon_core/hosts/nuke/plugins/load/load_image.py index 4f7a5ccc27..71c957fc6b 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_image.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_image.py @@ -82,9 +82,9 @@ class LoadImage(load.LoaderPlugin): last = first = int(frame_number) - # Fallback to asset name when namespace is None + # Fallback to folder name when namespace is None if namespace is None: - namespace = context['asset']['name'] + namespace = context["folder"]["name"] file = self.filepath_from_context(context) @@ -105,7 +105,7 @@ class LoadImage(load.LoaderPlugin): frame, format(frame_number, "0{}".format(padding))) - read_name = self._get_node_name(representation) + read_name = self._get_node_name(context) # Create the Loader with the filename path set with viewer_update_and_undo_stop(): @@ -243,15 +243,26 @@ class LoadImage(load.LoaderPlugin): with viewer_update_and_undo_stop(): nuke.delete(node) - def _get_node_name(self, representation): + def _get_node_name(self, context): + folder_entity = context["folder"] + subset_doc = context["subset"] + repre_doc = context["representation"] - repre_cont = representation["context"] + folder_name = folder_entity["name"] + product_name = subset_doc["name"] + repre_cont = repre_doc["context"] name_data = { - "asset": repre_cont["asset"], - "subset": repre_cont["subset"], - "representation": representation["name"], + "folder": { + "name": folder_name, + }, + "product": { + "name": product_name, + }, + "asset": folder_name, + "subset": product_name, + "representation": repre_doc["name"], "ext": repre_cont["representation"], - "id": representation["_id"], + "id": repre_doc["_id"], "class_name": self.__class__.__name__ } diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_model.py b/client/ayon_core/hosts/nuke/plugins/load/load_model.py index cd4b72df91..f68591ac50 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_model.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_model.py @@ -39,7 +39,7 @@ class AlembicModelLoader(load.LoaderPlugin): first = version_data.get("frameStart", None) last = version_data.get("frameEnd", None) fps = version_data.get("fps") or nuke.root()["fps"].getValue() - namespace = namespace or context['asset']['name'] + namespace = namespace or context["folder"]["name"] object_name = "{}_{}".format(name, namespace) # prepare data for imprinting diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_ociolook.py b/client/ayon_core/hosts/nuke/plugins/load/load_ociolook.py index e2e7cd3262..f3151bd389 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_ociolook.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_ociolook.py @@ -47,13 +47,13 @@ class LoadOcioLookNodes(load.LoaderPlugin): Arguments: context (dict): context of version name (str): name of the version - namespace (str): asset name + namespace (str): namespace name data (dict): compulsory attribute > not used Returns: nuke.Node: containerized nuke.Node object """ - namespace = namespace or context['asset']['name'] + namespace = namespace or context["folder"]["name"] suffix = secrets.token_hex(nbytes=4) node_name = "{}_{}_{}".format( name, namespace, suffix) diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_script_precomp.py b/client/ayon_core/hosts/nuke/plugins/load/load_script_precomp.py index 5d62a7ca0f..96a4e65d49 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_script_precomp.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_script_precomp.py @@ -39,9 +39,9 @@ class LinkAsGroup(load.LoaderPlugin): first = version_data.get("frameStart", None) last = version_data.get("frameEnd", None) - # Fallback to asset name when namespace is None + # Fallback to folder name when namespace is None if namespace is None: - namespace = context['asset']['name'] + namespace = context["folder"]["name"] file = self.filepath_from_context(context).replace("\\", "/") self.log.info("file: {}\n".format(file)) diff --git a/client/ayon_core/hosts/nuke/plugins/publish/help/validate_asset_context.xml b/client/ayon_core/hosts/nuke/plugins/publish/help/validate_asset_context.xml index d9394ae510..1e7d340a13 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/help/validate_asset_context.xml +++ b/client/ayon_core/hosts/nuke/plugins/publish/help/validate_asset_context.xml @@ -1,13 +1,13 @@ - Shot/Asset name + Folder path -## Publishing to a different asset context +## Publishing to a different folder context -There are publish instances present which are publishing into a different asset than your current context. +There are publish instances present which are publishing into a different folder than your current context. -Usually this is not what you want but there can be cases where you might want to publish into another asset/shot or task. +Usually this is not what you want but there can be cases where you might want to publish into another folder/shot or task. If that's the case you can disable the validation on the instance to ignore it. diff --git a/client/ayon_core/hosts/nuke/plugins/publish/validate_asset_context.py b/client/ayon_core/hosts/nuke/plugins/publish/validate_asset_context.py index 52ef4a58d4..93a30aa438 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/validate_asset_context.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/validate_asset_context.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""Validate if instance asset is the same as context asset.""" +"""Validate if instance folder is the same as context folder.""" from __future__ import absolute_import import pyblish.api @@ -17,10 +17,10 @@ class ValidateCorrectAssetContext( pyblish.api.InstancePlugin, OptionalPyblishPluginMixin ): - """Validator to check if instance asset context match context asset. + """Validator to check if instance folder context match context folder. When working in per-shot style you always publish data in context of - current asset (shot). This validator checks if this is so. It is optional + current folder (shot). This validator checks if this is so. It is optional so it can be disabled when needed. Checking `folderPath` and `task` keys. diff --git a/client/ayon_core/hosts/nuke/plugins/publish/validate_script_attributes.py b/client/ayon_core/hosts/nuke/plugins/publish/validate_script_attributes.py index c4974817bd..2bd2034079 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/validate_script_attributes.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/validate_script_attributes.py @@ -29,7 +29,7 @@ class ValidateScriptAttributes( script_data = deepcopy(instance.context.data["scriptData"]) - asset = instance.data["assetEntity"] + src_folder_attributes = instance.data["folderEntity"]["attrib"] # These attributes will be checked attributes = [ @@ -42,32 +42,32 @@ class ValidateScriptAttributes( "handleEnd" ] - # get only defined attributes from asset data - asset_attributes = { - attr: asset["data"][attr] + # get only defined attributes from folder data + folder_attributes = { + attr: src_folder_attributes[attr] for attr in attributes - if attr in asset["data"] + if attr in src_folder_attributes } # fix frame values to include handles - asset_attributes["fps"] = float("{0:.4f}".format( - asset_attributes["fps"])) + folder_attributes["fps"] = float("{0:.4f}".format( + folder_attributes["fps"])) script_data["fps"] = float("{0:.4f}".format( script_data["fps"])) - # Compare asset's values Nukescript X Database + # Compare folder's values Nukescript X Database not_matching = [] for attr in attributes: self.log.debug( - "Asset vs Script attribute \"{}\": {}, {}".format( + "Folder vs Script attribute \"{}\": {}, {}".format( attr, - asset_attributes[attr], + folder_attributes[attr], script_data[attr] ) ) - if asset_attributes[attr] != script_data[attr]: + if folder_attributes[attr] != script_data[attr]: not_matching.append({ "name": attr, - "expected": asset_attributes[attr], + "expected": folder_attributes[attr], "actual": script_data[attr] }) From 512086cfa65a8c4ce8d8e808feda81252af42b6f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 16:32:16 +0100 Subject: [PATCH 414/573] photoshop use folder and task entity --- .../photoshop/api/extension/host/index.jsx | 2 +- .../hosts/photoshop/api/launch_logic.py | 27 ++++++------ .../ayon_core/hosts/photoshop/api/plugin.py | 8 ++-- client/ayon_core/hosts/photoshop/lib.py | 41 +++++++++++------- .../plugins/create/create_flatten_image.py | 43 +++++++++---------- .../photoshop/plugins/create/create_image.py | 4 +- .../photoshop/plugins/load/load_image.py | 8 ++-- .../plugins/load/load_image_from_sequence.py | 2 +- .../photoshop/plugins/load/load_reference.py | 11 ++--- .../plugins/publish/collect_auto_image.py | 11 ++--- .../plugins/publish/collect_auto_review.py | 12 ++---- .../plugins/publish/collect_auto_workfile.py | 10 ++--- .../plugins/publish/collect_batch_data.py | 8 ++-- .../publish/collect_color_coded_instances.py | 6 +-- .../publish/collect_published_version.py | 7 ++- .../publish/help/validate_instance_asset.xml | 8 ++-- .../publish/validate_instance_asset.py | 38 ++++++++-------- .../plugins/publish/validate_naming.py | 9 ++-- 18 files changed, 132 insertions(+), 123 deletions(-) diff --git a/client/ayon_core/hosts/photoshop/api/extension/host/index.jsx b/client/ayon_core/hosts/photoshop/api/extension/host/index.jsx index e2711fb960..b697ee65ab 100644 --- a/client/ayon_core/hosts/photoshop/api/extension/host/index.jsx +++ b/client/ayon_core/hosts/photoshop/api/extension/host/index.jsx @@ -213,7 +213,7 @@ function getActiveDocumentFullName(){ function imprint(payload){ /** * Sets headline content of current document with metadata. Stores - * information about assets created through Avalon. + * information about assets created through AYON. * Content accessible in PS through File > File Info * **/ diff --git a/client/ayon_core/hosts/photoshop/api/launch_logic.py b/client/ayon_core/hosts/photoshop/api/launch_logic.py index adf90be311..17fe7d5920 100644 --- a/client/ayon_core/hosts/photoshop/api/launch_logic.py +++ b/client/ayon_core/hosts/photoshop/api/launch_logic.py @@ -8,6 +8,7 @@ from wsrpc_aiohttp import ( WebSocketAsync ) +import ayon_api from qtpy import QtCore from ayon_core.lib import Logger, StringTemplate @@ -23,7 +24,6 @@ from ayon_core.pipeline.template_data import get_template_data_with_names from ayon_core.tools.utils import host_tools from ayon_core.tools.adobe_webserver.app import WebServerTool from ayon_core.pipeline.context_tools import change_current_context -from ayon_core.client import get_asset_by_name from .ws_stub import PhotoshopServerStub @@ -318,25 +318,28 @@ class PhotoshopRoute(WebSocketRoute): # This method calls function on the client side # client functions - async def set_context(self, project, asset, task): + async def set_context(self, project, folder, task): """ - Sets 'project' and 'asset' to envs, eg. setting context. + Sets 'project' and 'folder' to envs, eg. setting context. Opens last workile from that context if exists. Args: project (str) - asset (str) + folder (str) task (str """ log.info("Setting context change") - log.info(f"project {project} asset {asset} task {task}") + log.info(f"project {project} folder {folder} task {task}") - asset_doc = get_asset_by_name(project, asset) - change_current_context(asset_doc, task) + folder_entity = ayon_api.get_folder_by_path(project, folder) + task_entity = ayon_api.get_task_by_name( + project, folder_entity["id"], task + ) + change_current_context(folder_entity, task_entity) last_workfile_path = self._get_last_workfile_path(project, - asset, + folder, task) if last_workfile_path and os.path.exists(last_workfile_path): ProcessLauncher.execute_in_main_thread( @@ -372,20 +375,20 @@ class PhotoshopRoute(WebSocketRoute): # Required return statement. return "nothing" - def _get_last_workfile_path(self, project_name, asset_name, task_name): + def _get_last_workfile_path(self, project_name, folder_path, task_name): """Returns last workfile path if exists""" host = registered_host() host_name = "photoshop" template_key = get_workfile_template_key_from_context( - asset_name, + project_name, + folder_path, task_name, host_name, - project_name=project_name ) anatomy = Anatomy(project_name) data = get_template_data_with_names( - project_name, asset_name, task_name, host_name + project_name, folder_path, task_name, host_name ) data["root"] = anatomy.roots diff --git a/client/ayon_core/hosts/photoshop/api/plugin.py b/client/ayon_core/hosts/photoshop/api/plugin.py index d4eb38300f..c11a206834 100644 --- a/client/ayon_core/hosts/photoshop/api/plugin.py +++ b/client/ayon_core/hosts/photoshop/api/plugin.py @@ -4,21 +4,21 @@ from ayon_core.pipeline import LoaderPlugin from .launch_logic import stub -def get_unique_layer_name(layers, asset_name, product_name): +def get_unique_layer_name(layers, container_name, product_name): """Prepare unique layer name. - Gets all layer names and if '_' is present, + Gets all layer names and if '_' is present, it adds suffix '1', or increases the suffix by 1. Args: layers (list) of dict with layers info (name, id etc.) - asset_name (str): + container_name (str): product_name (str): Returns: str: name_00X (without version) """ - name = "{}_{}".format(asset_name, product_name) + name = "{}_{}".format(container_name, product_name) names = {} for layer in layers: layer_name = re.sub(r'_\d{3}$', '', layer.name) diff --git a/client/ayon_core/hosts/photoshop/lib.py b/client/ayon_core/hosts/photoshop/lib.py index de3bc2ea6e..dd227c5d81 100644 --- a/client/ayon_core/hosts/photoshop/lib.py +++ b/client/ayon_core/hosts/photoshop/lib.py @@ -1,7 +1,8 @@ import re +import ayon_api + import ayon_core.hosts.photoshop.api as api -from ayon_core.client import get_asset_by_name from ayon_core.lib import prepare_template_data from ayon_core.pipeline import ( AutoCreator, @@ -40,33 +41,38 @@ class PSAutoCreator(AutoCreator): context = self.create_context project_name = context.get_current_project_name() - asset_name = context.get_current_folder_path() + folder_path = context.get_current_folder_path() task_name = context.get_current_task_name() host_name = context.host_name if existing_instance is None: - existing_instance_asset = None + existing_instance_folder = None else: - existing_instance_asset = existing_instance["folderPath"] + existing_instance_folder = existing_instance["folderPath"] if existing_instance is None: - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, self.default_variant, host_name, ) data = { - "folderPath": asset_name, + "folderPath": folder_path, "task": task_name, "variant": self.default_variant } data.update(self.get_dynamic_data( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, self.default_variant, host_name, None @@ -83,18 +89,23 @@ class PSAutoCreator(AutoCreator): new_instance.data_to_store()) elif ( - existing_instance_asset != asset_name + existing_instance_folder != folder_path or existing_instance["task"] != task_name ): - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, self.default_variant, host_name, ) - existing_instance["folderPath"] = asset_name + existing_instance["folderPath"] = folder_path existing_instance["task"] = task_name existing_instance["productName"] = product_name diff --git a/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py b/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py index 36b5e1577d..84c5548e7b 100644 --- a/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py @@ -1,11 +1,9 @@ -from ayon_core.pipeline import CreatedInstance +import ayon_api -from ayon_core.lib import BoolDef import ayon_core.hosts.photoshop.api as api from ayon_core.hosts.photoshop.lib import PSAutoCreator, clean_product_name -from ayon_core.pipeline.create import get_product_name -from ayon_core.lib import prepare_template_data -from ayon_core.client import get_asset_by_name +from ayon_core.lib import BoolDef, prepare_template_data +from ayon_core.pipeline.create import get_product_name, CreatedInstance class AutoImageCreator(PSAutoCreator): @@ -32,27 +30,29 @@ class AutoImageCreator(PSAutoCreator): context = self.create_context project_name = context.get_current_project_name() - asset_name = context.get_current_folder_path() + folder_path = context.get_current_folder_path() task_name = context.get_current_task_name() host_name = context.host_name - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path(project_name, folder_path) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) - if existing_instance is None: - existing_instance_asset = None - else: - existing_instance_asset = existing_instance["folderPath"] + existing_folder_path = None + if existing_instance is not None: + existing_folder_path = existing_instance["folderPath"] if existing_instance is None: product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, self.default_variant, host_name, ) data = { - "folderPath": asset_name, + "folderPath": folder_path, "task": task_name, } @@ -70,17 +70,17 @@ class AutoImageCreator(PSAutoCreator): new_instance.data_to_store()) elif ( # existing instance from different context - existing_instance_asset != asset_name + existing_folder_path != folder_path or existing_instance["task"] != task_name ): product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, self.default_variant, host_name, ) - existing_instance["folderPath"] = asset_name + existing_instance["folderPath"] = folder_path existing_instance["task"] = task_name existing_instance["productName"] = product_name @@ -128,8 +128,8 @@ class AutoImageCreator(PSAutoCreator): def get_product_name( self, project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, host_name=None, instance=None @@ -139,8 +139,7 @@ class AutoImageCreator(PSAutoCreator): dynamic_data = prepare_template_data({"layer": "{layer}"}) product_name = get_product_name( project_name, - asset_doc, - task_name, + task_entity, host_name, self.product_type, variant, diff --git a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py b/client/ayon_core/hosts/photoshop/plugins/create/create_image.py index 8806aad33c..26f2469844 100644 --- a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/create/create_image.py @@ -247,8 +247,8 @@ class ImageCreator(Creator): def get_dynamic_data( self, project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, host_name, instance diff --git a/client/ayon_core/hosts/photoshop/plugins/load/load_image.py b/client/ayon_core/hosts/photoshop/plugins/load/load_image.py index ec6392bade..9e935e238c 100644 --- a/client/ayon_core/hosts/photoshop/plugins/load/load_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/load/load_image.py @@ -18,7 +18,7 @@ class ImageLoader(photoshop.PhotoshopLoader): stub = self.get_stub() layer_name = get_unique_layer_name( stub.get_layers(), - context["asset"]["name"], + context["folder"]["name"], name ) with photoshop.maintained_selection(): @@ -43,14 +43,16 @@ class ImageLoader(photoshop.PhotoshopLoader): layer = container.pop("layer") repre_doc = context["representation"] + folder_name = context["folder"]["name"] + product_name = context["subset"]["name"] namespace_from_container = re.sub(r'_\d{3}$', '', container["namespace"]) - layer_name = "{}_{}".format(context["asset"], context["subset"]) + layer_name = "{}_{}".format(folder_name, product_name) # switching assets if namespace_from_container != layer_name: layer_name = get_unique_layer_name( - stub.get_layers(), context["asset"], context["subset"] + stub.get_layers(), folder_name, product_name ) else: # switching version - keep same name layer_name = container["namespace"] diff --git a/client/ayon_core/hosts/photoshop/plugins/load/load_image_from_sequence.py b/client/ayon_core/hosts/photoshop/plugins/load/load_image_from_sequence.py index 49ca513bd2..e20c9a5138 100644 --- a/client/ayon_core/hosts/photoshop/plugins/load/load_image_from_sequence.py +++ b/client/ayon_core/hosts/photoshop/plugins/load/load_image_from_sequence.py @@ -40,7 +40,7 @@ class ImageFromSequenceLoader(photoshop.PhotoshopLoader): stub = self.get_stub() layer_name = get_unique_layer_name( - stub.get_layers(), context["asset"]["name"], name + stub.get_layers(), context["folder"]["name"], name ) with photoshop.maintained_selection(): diff --git a/client/ayon_core/hosts/photoshop/plugins/load/load_reference.py b/client/ayon_core/hosts/photoshop/plugins/load/load_reference.py index f83272f97d..80c4a4e514 100644 --- a/client/ayon_core/hosts/photoshop/plugins/load/load_reference.py +++ b/client/ayon_core/hosts/photoshop/plugins/load/load_reference.py @@ -20,7 +20,7 @@ class ReferenceLoader(photoshop.PhotoshopLoader): def load(self, context, name=None, namespace=None, data=None): stub = self.get_stub() layer_name = get_unique_layer_name( - stub.get_layers(), context["asset"]["name"], name + stub.get_layers(), context["folder"]["name"], name ) with photoshop.maintained_selection(): path = self.filepath_from_context(context) @@ -38,17 +38,14 @@ class ReferenceLoader(photoshop.PhotoshopLoader): ) def update(self, container, context): - """ Switch asset or change version """ + """ Switch asset or change version.""" stub = self.get_stub() layer = container.pop("layer") - asset_doc = context["asset"] - subset_doc = context["subset"] + folder_name = context["folder"]["name"] + product_name = context["subset"]["name"] repre_doc = context["representation"] - folder_name = asset_doc["name"] - product_name = subset_doc["name"] - namespace_from_container = re.sub(r'_\d{3}$', '', container["namespace"]) layer_name = "{}_{}".format(folder_name, product_name) diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image.py index 7773b444d2..ab8254d747 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image.py @@ -1,6 +1,5 @@ import pyblish.api -from ayon_core.client import get_asset_name_identifier from ayon_core.hosts.photoshop import api as photoshop from ayon_core.pipeline.create import get_product_name @@ -25,10 +24,9 @@ class CollectAutoImage(pyblish.api.ContextPlugin): project_name = context.data["projectName"] proj_settings = context.data["project_settings"] - task_name = context.data["task"] host_name = context.data["hostName"] - asset_doc = context.data["assetEntity"] - folder_path = get_asset_name_identifier(asset_doc) + folder_entity = context.data["folderEntity"] + task_entity = context.data["taskEntity"] auto_creator = proj_settings.get( "photoshop", {}).get( @@ -81,15 +79,14 @@ class CollectAutoImage(pyblish.api.ContextPlugin): product_name = get_product_name( project_name, - asset_doc, - task_name, + task_entity, host_name, product_type, variant, ) instance = context.create_instance(product_name) - instance.data["folderPath"] = folder_path + instance.data["folderPath"] = folder_entity["path"] instance.data["productType"] = product_type instance.data["productName"] = product_name instance.data["ids"] = publishable_ids diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_review.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_review.py index 14f2f23985..49eac1cbd2 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_review.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_review.py @@ -7,7 +7,6 @@ Provides: """ import pyblish.api -from ayon_core.client import get_asset_name_identifier from ayon_core.hosts.photoshop import api as photoshop from ayon_core.pipeline.create import get_product_name @@ -63,16 +62,13 @@ class CollectAutoReview(pyblish.api.ContextPlugin): project_name = context.data["projectName"] proj_settings = context.data["project_settings"] - task_name = context.data["task"] host_name = context.data["hostName"] - asset_doc = context.data["assetEntity"] - - folder_path = get_asset_name_identifier(asset_doc) + folder_entity = context.data["folderEntity"] + task_entity = context.data["taskEntity"] product_name = get_product_name( project_name, - asset_doc, - task_name, + task_entity, host_name, product_type, variant, @@ -88,7 +84,7 @@ class CollectAutoReview(pyblish.api.ContextPlugin): "family": product_type, "families": [product_type], "representations": [], - "folderPath": folder_path, + "folderPath": folder_entity["path"], "publish": self.publish }) diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_workfile.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_workfile.py index 0b12195603..fd68529437 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_workfile.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_workfile.py @@ -1,7 +1,6 @@ import os import pyblish.api -from ayon_core.client import get_asset_name_identifier from ayon_core.hosts.photoshop import api as photoshop from ayon_core.pipeline.create import get_product_name @@ -69,13 +68,12 @@ class CollectAutoWorkfile(pyblish.api.ContextPlugin): task_name = context.data["task"] host_name = context.data["hostName"] - asset_doc = context.data["assetEntity"] + folder_entity = context.data["folderEntity"] + task_entity = context.data["taskEntity"] - folder_path = get_asset_name_identifier(asset_doc) product_name = get_product_name( project_name, - asset_doc, - task_name, + task_entity, host_name, product_type, variant, @@ -92,7 +90,7 @@ class CollectAutoWorkfile(pyblish.api.ContextPlugin): "family": product_type, "families": [product_type], "representations": [], - "folderPath": folder_path + "folderPath": folder_entity["path"] }) # creating representation diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_batch_data.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_batch_data.py index a32b5f8fa5..c43a957576 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_batch_data.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_batch_data.py @@ -18,7 +18,7 @@ import os import pyblish.api from openpype_modules.webpublisher.lib import ( - get_batch_asset_task_info, + get_batch_context_info, parse_json ) from ayon_core.lib import is_in_tests @@ -64,14 +64,14 @@ class CollectBatchData(pyblish.api.ContextPlugin): context.data["batchDir"] = batch_dir context.data["batchData"] = batch_data - asset_name, task_name, task_type = get_batch_asset_task_info( + folder_path, task_name, task_type = get_batch_context_info( batch_data["context"] ) - os.environ["AYON_FOLDER_PATH"] = asset_name + os.environ["AYON_FOLDER_PATH"] = folder_path os.environ["AYON_TASK_NAME"] = task_name - context.data["folderPath"] = asset_name + context.data["folderPath"] = folder_path context.data["task"] = task_name context.data["taskType"] = task_type context.data["project_name"] = project_name diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_color_coded_instances.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_color_coded_instances.py index f11ba4383a..e8f7c7e3df 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_color_coded_instances.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_color_coded_instances.py @@ -57,7 +57,7 @@ class CollectColorCodedInstances(pyblish.api.ContextPlugin): existing_product_names = self._get_existing_product_names(context) # from CollectBatchData - asset_name = context.data["folderPath"] + folder_path = context.data["folderPath"] task_name = context.data["task"] variant = context.data["variant"] project_name = context.data["projectEntity"]["name"] @@ -120,7 +120,7 @@ class CollectColorCodedInstances(pyblish.api.ContextPlugin): context, layer, resolved_product_type, - asset_name, + folder_path, product_name, task_name ) @@ -146,7 +146,7 @@ class CollectColorCodedInstances(pyblish.api.ContextPlugin): context, first_layer, product_type, - asset_name, + folder_path, product_name, task_name ) diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_published_version.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_published_version.py index 53b2503ba2..5ea8276dea 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_published_version.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_published_version.py @@ -9,7 +9,7 @@ product is used instead. This plugin runs only in remote publishing (eg. Webpublisher). Requires: - context.data["assetEntity"] + context.data["folderEntity"] Provides: context["version"] - incremented latest published workfile version @@ -42,11 +42,10 @@ class CollectPublishedVersion(pyblish.api.ContextPlugin): return project_name = context.data["projectName"] - asset_doc = context.data["assetEntity"] - asset_id = asset_doc["_id"] + folder_id = context.data["folderEntity"]["id"] version_doc = get_last_version_by_subset_name( - project_name, workfile_product_name, asset_id + project_name, workfile_product_name, folder_id ) if version_doc: diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/help/validate_instance_asset.xml b/client/ayon_core/hosts/photoshop/plugins/publish/help/validate_instance_asset.xml index e05ac92182..c033f922c6 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/help/validate_instance_asset.xml +++ b/client/ayon_core/hosts/photoshop/plugins/publish/help/validate_instance_asset.xml @@ -1,9 +1,9 @@ -Asset does not match +Folder does not match -## Collected asset name is not same as in context +## Collected folder path is not same as in context {msg} ### How to repair? @@ -11,10 +11,10 @@ Refresh Publish afterwards (circle arrow at the bottom right). If that's not correct value, close workfile and reopen via Workfiles to get - proper context asset name OR disable this validator and publish again + proper context folder path OR disable this validator and publish again if you are publishing to different context deliberately. - (Context means combination of project, asset name and task name.) + (Context means combination of project, folder path and task name.) \ No newline at end of file diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/validate_instance_asset.py b/client/ayon_core/hosts/photoshop/plugins/publish/validate_instance_asset.py index 937d68eeff..c3a6822f32 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/validate_instance_asset.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/validate_instance_asset.py @@ -9,8 +9,8 @@ from ayon_core.pipeline.publish import ( from ayon_core.hosts.photoshop import api as photoshop -class ValidateInstanceAssetRepair(pyblish.api.Action): - """Repair the instance asset.""" +class ValidateInstanceFolderRepair(pyblish.api.Action): + """Repair the instance folder.""" label = "Repair" icon = "wrench" @@ -21,50 +21,54 @@ class ValidateInstanceAssetRepair(pyblish.api.Action): # Get the errored instances failed = [] for result in context.data["results"]: - if (result["error"] is not None and result["instance"] is not None - and result["instance"] not in failed): + if ( + result["error"] is not None + and result["instance"] is not None + and result["instance"] not in failed + ): failed.append(result["instance"]) # Apply pyblish.logic to get the instances for the plug-in instances = pyblish.api.instances_by_plugin(failed, plugin) stub = photoshop.stub() - current_asset_name = get_current_folder_path() + current_folder_path = get_current_folder_path() for instance in instances: data = stub.read(instance[0]) - data["folderPath"] = current_asset_name + data["folderPath"] = current_folder_path stub.imprint(instance[0], data) class ValidateInstanceAsset(OptionalPyblishPluginMixin, pyblish.api.InstancePlugin): - """Validate the instance asset is the current selected context asset. + """Validate the instance folder is the current selected context folder. As it might happen that multiple worfiles are opened, switching between them would mess with selected context. - In that case outputs might be output under wrong asset! + In that case outputs might be output under wrong folder! - Repair action will use Context asset value (from Workfiles or Launcher) + Repair action will use Context folder value (from Workfiles or Launcher) Closing and reopening with Workfiles will refresh Context value. """ - label = "Validate Instance Asset" + label = "Validate Instance Folder" hosts = ["photoshop"] optional = True - actions = [ValidateInstanceAssetRepair] + actions = [ValidateInstanceFolderRepair] order = ValidateContentsOrder def process(self, instance): - instance_asset = instance.data["folderPath"] - current_asset = get_current_folder_path() + instance_folder_path = instance.data["folderPath"] + current_folder_path = get_current_folder_path() - if instance_asset != current_asset: + if instance_folder_path != current_folder_path: msg = ( - f"Instance asset {instance_asset} is not the same " - f"as current context {current_asset}." + f"Instance folder {instance_folder_path} is not the same" + f" as current context {current_folder_path}." ) repair_msg = ( - f"Repair with 'Repair' button to use '{current_asset}'.\n" + "Repair with 'Repair' button" + f" to use '{current_folder_path}'.\n" ) formatting_data = {"msg": msg, "repair_msg": repair_msg} diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/validate_naming.py b/client/ayon_core/hosts/photoshop/plugins/publish/validate_naming.py index ce940c47ce..13c6a54fd2 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/validate_naming.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/validate_naming.py @@ -11,7 +11,7 @@ from ayon_core.pipeline.publish import ( class ValidateNamingRepair(pyblish.api.Action): - """Repair the instance asset.""" + """Repair the instance folder.""" label = "Repair" icon = "wrench" @@ -22,8 +22,11 @@ class ValidateNamingRepair(pyblish.api.Action): # Get the errored instances failed = [] for result in context.data["results"]: - if (result["error"] is not None and result["instance"] is not None - and result["instance"] not in failed): + if ( + result["error"] is not None + and result["instance"] is not None + and result["instance"] not in failed + ): failed.append(result["instance"]) invalid_chars, replace_char = plugin.get_replace_chars() From 095b880bd5f4ef7105d69912c414caa1362a8f4c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 16:32:59 +0100 Subject: [PATCH 415/573] fusion uses folder and task entity --- client/ayon_core/hosts/resolve/api/plugin.py | 31 ++++++++++--------- .../plugins/publish/precollect_instances.py | 20 ++++++------ .../plugins/publish/precollect_workfile.py | 10 +++--- 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/client/ayon_core/hosts/resolve/api/plugin.py b/client/ayon_core/hosts/resolve/api/plugin.py index 157b8de363..5ac0fc76fb 100644 --- a/client/ayon_core/hosts/resolve/api/plugin.py +++ b/client/ayon_core/hosts/resolve/api/plugin.py @@ -325,7 +325,7 @@ class ClipLoader: "or call your supervisor") # inject asset data to representation dict - self._get_asset_data() + self._get_folder_attributes() # add active components to class if self.new_timeline: @@ -355,13 +355,16 @@ class ClipLoader: } """ # create name - representation = self.context["representation"] - representation_context = representation["context"] - asset = str(representation_context["asset"]) - product_name = str(representation_context["subset"]) - representation_name = str(representation_context["representation"]) + folder_entity = self.context["folder"] + product_name = self.context["subset"]["name"] + repre_doc = self.context["representation"] + + folder_name = folder_entity["name"] + folder_path = folder_entity["path"] + representation_name = repre_doc["name"] + self.data["clip_name"] = "_".join([ - asset, + folder_name, product_name, representation_name ]) @@ -371,24 +374,22 @@ class ClipLoader: product_name, representation_name) # solve project bin structure path - hierarchy = str("/".join(( - "Loader", - representation_context["hierarchy"].replace("\\", "/"), - asset - ))) + hierarchy = "Loader{}".format(folder_path) self.data["binPath"] = hierarchy return True - def _get_asset_data(self): + def _get_folder_attributes(self): """ Get all available asset data joint `data` key with asset.data dict into the representation """ - self.data["assetData"] = copy.deepcopy(self.context["asset"]["data"]) + self.data["folderAttributes"] = copy.deepcopy( + self.context["folder"]["attrib"] + ) def load(self, files): """Load clip into timeline @@ -451,7 +452,7 @@ class ClipLoader: else: # set timeline start frame + original clip in frame timeline_in = int( - timeline_start + self.data["assetData"]["clipIn"]) + timeline_start + self.data["folderAttributes"]["clipIn"]) # make track item from source in bin as item timeline_item = lib.create_timeline_item( diff --git a/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py b/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py index b1374859e3..72ecd3669d 100644 --- a/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py +++ b/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py @@ -63,7 +63,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin): if k not in ("id", "applieswhole", "label") }) - asset = tag_data["folder_path"] + folder_path = tag_data["folder_path"] # TODO: remove backward compatibility product_name = tag_data.get("productName") @@ -74,7 +74,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin): # backward compatibility: product_name should not be missing if not product_name: self.log.error( - "Product name is not defined for: {}".format(asset)) + "Product name is not defined for: {}".format(folder_path)) # TODO: remove backward compatibility product_type = tag_data.get("productType") @@ -85,12 +85,12 @@ class PrecollectInstances(pyblish.api.ContextPlugin): # backward compatibility: product_type should not be missing if not product_type: self.log.error( - "Product type is not defined for: {}".format(asset)) + "Product type is not defined for: {}".format(folder_path)) data.update({ - "name": "{}_{}".format(asset, product_name), - "label": "{} {}".format(asset, product_name), - "folderPath": asset, + "name": "{}_{}".format(folder_path, product_name), + "label": "{} {}".format(folder_path, product_name), + "folderPath": folder_path, "item": timeline_item, "publish": get_publish_attribute(timeline_item), "fps": context.data["fps"], @@ -151,16 +151,16 @@ class PrecollectInstances(pyblish.api.ContextPlugin): if not hierarchy_data: return - asset = data["folderPath"] + folder_path = data["folderPath"] product_name = "shotMain" # insert family into families product_type = "shot" data.update({ - "name": "{}_{}".format(asset, product_name), - "label": "{} {}".format(asset, product_name), - "folderPath": asset, + "name": "{}_{}".format(folder_path, product_name), + "label": "{} {}".format(folder_path, product_name), + "folderPath": folder_path, "productName": product_name, "productType": product_type, "family": product_type, diff --git a/client/ayon_core/hosts/resolve/plugins/publish/precollect_workfile.py b/client/ayon_core/hosts/resolve/plugins/publish/precollect_workfile.py index 71c3aa4571..6158cf1d61 100644 --- a/client/ayon_core/hosts/resolve/plugins/publish/precollect_workfile.py +++ b/client/ayon_core/hosts/resolve/plugins/publish/precollect_workfile.py @@ -14,8 +14,8 @@ class PrecollectWorkfile(pyblish.api.ContextPlugin): order = pyblish.api.CollectorOrder - 0.5 def process(self, context): - current_asset_name = get_current_folder_path() - asset_name = current_asset_name.split("/")[-1] + current_folder_path = get_current_folder_path() + folder_name = current_folder_path.split("/")[-1] product_name = "workfileMain" project = rapi.get_current_project() @@ -26,10 +26,10 @@ class PrecollectWorkfile(pyblish.api.ContextPlugin): otio_timeline = davinci_export.create_otio_timeline(project) instance_data = { - "name": "{}_{}".format(asset_name, product_name), - "label": "{} {}".format(current_asset_name, product_name), + "name": "{}_{}".format(folder_name, product_name), + "label": "{} {}".format(current_folder_path, product_name), "item": project, - "folderPath": current_asset_name, + "folderPath": current_folder_path, "productName": product_name, "productType": "workfile", "family": "workfile", From 007b9aa3dae140a6e4f117a7e0e53778bf276ff4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 16:34:00 +0100 Subject: [PATCH 416/573] substancepainter use folder and task entity --- .../hosts/substancepainter/api/pipeline.py | 7 ++-- .../plugins/create/create_workfile.py | 40 ++++++++++++------- .../publish/collect_textureset_images.py | 24 ++++++----- 3 files changed, 43 insertions(+), 28 deletions(-) diff --git a/client/ayon_core/hosts/substancepainter/api/pipeline.py b/client/ayon_core/hosts/substancepainter/api/pipeline.py index a14d398808..fdea26e6f9 100644 --- a/client/ayon_core/hosts/substancepainter/api/pipeline.py +++ b/client/ayon_core/hosts/substancepainter/api/pipeline.py @@ -245,16 +245,15 @@ class SubstanceHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): return # Prepare formatting data if we detect any path which might have - # template tokens like {asset} in there. + # template tokens like {folder[name]} in there. formatting_data = {} has_formatting_entries = any("{" in item["value"] for item in shelves) if has_formatting_entries: project_name = self.get_current_project_name() - asset_name = self.get_current_folder_path() + folder_path = self.get_current_folder_path() task_name = self.get_current_task_name() - project_settings = get_project_settings(project_name) formatting_data = get_template_data_with_names( - project_name, asset_name, task_name, project_settings + project_name, folder_path, task_name, project_settings ) anatomy = Anatomy(project_name) formatting_data["root"] = anatomy.roots diff --git a/client/ayon_core/hosts/substancepainter/plugins/create/create_workfile.py b/client/ayon_core/hosts/substancepainter/plugins/create/create_workfile.py index 23811dfd29..63b1c6c7da 100644 --- a/client/ayon_core/hosts/substancepainter/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/substancepainter/plugins/create/create_workfile.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- """Creator plugin for creating workfiles.""" +import ayon_api + from ayon_core.pipeline import CreatedInstance, AutoCreator -from ayon_core.client import get_asset_by_name from ayon_core.hosts.substancepainter.api.pipeline import ( set_instances, @@ -29,7 +30,7 @@ class CreateWorkfile(AutoCreator): variant = self.default_variant project_name = self.project_name - asset_name = self.create_context.get_current_asset_name() + folder_path = self.create_context.get_current_folder_path() task_name = self.create_context.get_current_task_name() host_name = self.create_context.host_name @@ -41,42 +42,51 @@ class CreateWorkfile(AutoCreator): if instance.creator_identifier == self.identifier ), None) - if current_instance is None: - current_instance_asset = None - else: - current_instance_asset = current_instance["folderPath"] + current_folder_path = None + if current_instance is not None: + current_folder_path = current_instance["folderPath"] if current_instance is None: self.log.info("Auto-creating workfile instance...") - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, host_name, ) data = { - "folderPath": asset_name, + "folderPath": folder_path, "task": task_name, "variant": variant } current_instance = self.create_instance_in_context(product_name, data) elif ( - current_instance_asset != asset_name + current_folder_path != folder_path or current_instance["task"] != task_name ): # Update instance context if is not the same - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, host_name, ) - current_instance["folderPath"] = asset_name + current_instance["folderPath"] = folder_path current_instance["task"] = task_name current_instance["productName"] = product_name diff --git a/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py b/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py index 9cd77e8f90..859a941882 100644 --- a/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py +++ b/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py @@ -1,16 +1,16 @@ import os import copy -import pyblish.api -from ayon_core.pipeline import publish +import pyblish.api +import ayon_api import substance_painter.textureset +from ayon_core.pipeline import publish from ayon_core.hosts.substancepainter.api.lib import ( get_parsed_export_maps, strip_template ) from ayon_core.pipeline.create import get_product_name -from ayon_core.client import get_asset_by_name class CollectTextureSet(pyblish.api.InstancePlugin): @@ -26,10 +26,17 @@ class CollectTextureSet(pyblish.api.InstancePlugin): def process(self, instance): config = self.get_export_config(instance) - asset_doc = get_asset_by_name( - instance.context.data["projectName"], + project_name = instance.context.data["projectName"] + folder_entity = ayon_api.get_folder_by_path( + project_name, instance.data["folderPath"] ) + task_name = instance.data.get("task") + task_entity = None + if folder_entity and task_name: + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) instance.data["exportConfig"] = config maps = get_parsed_export_maps(config) @@ -41,12 +48,12 @@ class CollectTextureSet(pyblish.api.InstancePlugin): for template, outputs in template_maps.items(): self.log.info(f"Processing {template}") self.create_image_instance(instance, template, outputs, - asset_doc=asset_doc, + task_entity=task_entity, texture_set_name=texture_set_name, stack_name=stack_name) def create_image_instance(self, instance, template, outputs, - asset_doc, texture_set_name, stack_name): + task_entity, texture_set_name, stack_name): """Create a new instance per image or UDIM sequence. The new instances will be of product type `image`. @@ -84,8 +91,7 @@ class CollectTextureSet(pyblish.api.InstancePlugin): # for now this is only done so the product name starts with # 'texture' project_name=context.data["projectName"], - asset_doc=asset_doc, - task_name=instance.data.get("task"), + task_entity=task_entity, host_name=context.data["hostName"], product_type="texture", variant=instance.data["variant"] + suffix, From 6c1304cda639cfc0f3d8d5782d105b83919dba55 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 16:35:02 +0100 Subject: [PATCH 417/573] tvpaint use folder and task entity --- .../ayon_core/hosts/tvpaint/api/pipeline.py | 44 ++++--- client/ayon_core/hosts/tvpaint/api/plugin.py | 34 ++--- .../tvpaint/plugins/create/create_render.py | 117 +++++++++++------- .../tvpaint/plugins/create/create_review.py | 40 +++--- .../tvpaint/plugins/create/create_workfile.py | 40 +++--- .../hosts/tvpaint/plugins/load/load_image.py | 4 +- .../plugins/load/load_reference_image.py | 4 +- .../tvpaint/plugins/load/load_workfile.py | 22 ++-- .../publish/collect_instance_frames.py | 8 +- .../plugins/publish/collect_workfile_data.py | 12 +- .../publish/help/validate_asset_name.xml | 2 +- .../plugins/publish/validate_asset_name.py | 40 +++--- .../publish/validate_scene_settings.py | 12 +- 13 files changed, 225 insertions(+), 154 deletions(-) diff --git a/client/ayon_core/hosts/tvpaint/api/pipeline.py b/client/ayon_core/hosts/tvpaint/api/pipeline.py index 1b0227e89c..27f2981a2e 100644 --- a/client/ayon_core/hosts/tvpaint/api/pipeline.py +++ b/client/ayon_core/hosts/tvpaint/api/pipeline.py @@ -4,10 +4,9 @@ import tempfile import logging import requests - +import ayon_api import pyblish.api -from ayon_core.client import get_asset_by_name from ayon_core.host import HostBase, IWorkfileHost, ILoadHost, IPublishHost from ayon_core.hosts.tvpaint import TVPAINT_ROOT_DIR from ayon_core.settings import get_current_project_settings @@ -93,10 +92,10 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): return self.get_current_context().get("project_name") - def get_current_asset_name(self): + def get_current_folder_path(self): """ Returns: - Union[str, None]: Current asset name. + Union[str, None]: Current folder path. """ return self.get_current_context().get("folder_path") @@ -183,13 +182,13 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): log.info("Setting up project...") global_context = get_global_context() project_name = global_context.get("project_name") - asset_name = global_context.get("aset_name") - if not project_name or not asset_name: + folder_path = global_context.get("folder_path") + if not project_name or not folder_path: return - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path(project_name, folder_path) - set_context_settings(project_name, asset_doc) + set_context_settings(project_name, folder_entity) def application_exit(self): """Logic related to TimerManager. @@ -466,17 +465,24 @@ def get_containers(): return output -def set_context_settings(project_name, asset_doc): - """Set workfile settings by asset document data. +def set_context_settings(project_name, folder_entity): + """Set workfile settings by folder entity attributes. Change fps, resolution and frame start/end. + + Args: + project_name (str): Project name. + folder_entity (dict[str, Any]): Folder entity. + """ - width_key = "resolutionWidth" - height_key = "resolutionHeight" + if not folder_entity: + return - width = asset_doc["data"].get(width_key) - height = asset_doc["data"].get(height_key) + folder_attributes = folder_entity["attrib"] + + width = folder_attributes.get("resolutionWidth") + height = folder_attributes.get("resolutionHeight") if width is None or height is None: print("Resolution was not found!") else: @@ -484,7 +490,7 @@ def set_context_settings(project_name, asset_doc): "tv_resizepage {} {} 0".format(width, height) ) - framerate = asset_doc["data"].get("fps") + framerate = folder_attributes.get("fps") if framerate is not None: execute_george( @@ -493,15 +499,15 @@ def set_context_settings(project_name, asset_doc): else: print("Framerate was not found!") - frame_start = asset_doc["data"].get("frameStart") - frame_end = asset_doc["data"].get("frameEnd") + frame_start = folder_attributes.get("frameStart") + frame_end = folder_attributes.get("frameEnd") if frame_start is None or frame_end is None: print("Frame range was not found!") return - handle_start = asset_doc["data"].get("handleStart") - handle_end = asset_doc["data"].get("handleEnd") + handle_start = folder_attributes.get("handleStart") + handle_end = folder_attributes.get("handleEnd") # Always start from 0 Mark In and set only Mark Out mark_in = 0 diff --git a/client/ayon_core/hosts/tvpaint/api/plugin.py b/client/ayon_core/hosts/tvpaint/api/plugin.py index ef9f82b783..c198f62583 100644 --- a/client/ayon_core/hosts/tvpaint/api/plugin.py +++ b/client/ayon_core/hosts/tvpaint/api/plugin.py @@ -56,20 +56,24 @@ class TVPaintCreatorCommon: def _custom_get_product_name( self, project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, host_name=None, instance=None ): dynamic_data = self.get_dynamic_data( - project_name, asset_doc, task_name, variant, host_name, instance + project_name, + folder_entity, + task_entity, + variant, + host_name, + instance ) return get_product_name( project_name, - asset_doc, - task_name, + task_entity, host_name, self.product_type, variant, @@ -107,13 +111,15 @@ class TVPaintCreator(Creator, TVPaintCreatorCommon): self._remove_instance_from_context(instance) def get_dynamic_data(self, *args, **kwargs): - # Change asset and name by current workfile context + # Change folder and name by current workfile context create_context = self.create_context - asset_name = create_context.get_current_asset_name() + folder_path = create_context.get_current_folder_path() task_name = create_context.get_current_task_name() output = {} - if asset_name: - output["asset"] = asset_name + if folder_path: + folder_name = folder_path.rsplit("/")[-1] + output["asset"] = folder_name + output["folder"] = {"name": folder_name} if task_name: output["task"] = task_name return output @@ -152,22 +158,22 @@ class Loader(LoaderPlugin): ] return container["members"] - def get_unique_layer_name(self, asset_name, name): + def get_unique_layer_name(self, namespace, name): """Layer name with counter as suffix. Find higher 3 digit suffix from all layer names in scene matching regex - `{asset_name}_{name}_{suffix}`. Higher 3 digit suffix is used + `{namespace}_{name}_{suffix}`. Higher 3 digit suffix is used as base for next number if scene does not contain layer matching regex `0` is used ase base. Args: - asset_name (str): Name of product's parent asset document. + namespace (str): Usually folder name. name (str): Name of loaded product. Returns: - (str): `{asset_name}_{name}_{higher suffix + 1}` + str: `{namespace}_{name}_{higher suffix + 1}` """ - layer_name_base = "{}_{}".format(asset_name, name) + layer_name_base = "{}_{}".format(namespace, name) counter_regex = re.compile(r"_(\d{3})$") diff --git a/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py b/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py index 28d21d69e6..9714dc8b8f 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py +++ b/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py @@ -37,7 +37,8 @@ Todos: import collections from typing import Any, Optional, Union -from ayon_core.client import get_asset_by_name, get_asset_name_identifier +import ayon_api + from ayon_core.lib import ( prepare_template_data, AbstractAttrDef, @@ -149,10 +150,21 @@ class CreateRenderlayer(TVPaintCreator): self.mark_for_review = plugin_settings["mark_for_review"] def get_dynamic_data( - self, project_name, asset_doc, task_name, variant, host_name, instance + self, + project_name, + folder_entity, + task_entity, + variant, + host_name, + instance ): dynamic_data = super().get_dynamic_data( - project_name, asset_doc, task_name, variant, host_name, instance + project_name, + folder_entity, + task_entity, + variant, + host_name, + instance ) dynamic_data["renderpass"] = self.default_pass_name dynamic_data["renderlayer"] = variant @@ -425,10 +437,21 @@ class CreateRenderPass(TVPaintCreator): self._add_instance_to_context(instance) def get_dynamic_data( - self, project_name, asset_doc, task_name, variant, host_name, instance + self, + project_name, + folder_entity, + task_entity, + variant, + host_name, + instance ): dynamic_data = super().get_dynamic_data( - project_name, asset_doc, task_name, variant, host_name, instance + project_name, + folder_entity, + task_entity, + variant, + host_name, + instance ) dynamic_data["renderpass"] = variant dynamic_data["renderlayer"] = "{renderlayer}" @@ -754,8 +777,8 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): def _prepare_render_layer( self, project_name: str, - asset_doc: dict[str, Any], - task_name: str, + folder_entity: dict[str, Any], + task_entity: dict[str, Any], group_id: int, groups: list[dict[str, Any]], mark_for_review: bool, @@ -772,6 +795,7 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): if not match_group: return None + task_name = task_entity["name"] variant: str = match_group["name"] creator: CreateRenderlayer = ( self.create_context.creators[CreateRenderlayer.identifier] @@ -779,20 +803,19 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): product_name: str = creator.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, host_name=self.create_context.host_name, ) - asset_name = get_asset_name_identifier(asset_doc) if existing_instance is not None: - existing_instance["folderPath"] = asset_name + existing_instance["folderPath"] = folder_entity["path"] existing_instance["task"] = task_name existing_instance["productName"] = product_name return existing_instance instance_data: dict[str, str] = { - "folderPath": asset_name, + "folderPath": folder_entity["path"], "task": task_name, "productType": creator.product_type, "variant": variant, @@ -806,13 +829,14 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): def _prepare_render_passes( self, project_name: str, - asset_doc: dict[str, Any], - task_name: str, + folder_entity: dict[str, Any], + task_entity: dict[str, Any], render_layer_instance: CreatedInstance, layers: list[dict[str, Any]], mark_for_review: bool, existing_render_passes: list[CreatedInstance] ): + task_name = task_entity["name"] creator: CreateRenderPass = ( self.create_context.creators[CreateRenderPass.identifier] ) @@ -821,8 +845,6 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): for layer_name in render_pass["layer_names"]: render_pass_by_layer_name[layer_name] = render_pass - asset_name = get_asset_name_identifier(asset_doc) - for layer in layers: layer_name = layer["name"] variant = layer_name @@ -833,21 +855,21 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): product_name = creator.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, host_name=self.create_context.host_name, instance=render_pass ) if render_pass is not None: - render_pass["folderPath"] = asset_name + render_pass["folderPath"] = folder_entity["path"] render_pass["task"] = task_name render_pass["productName"] = product_name continue instance_data: dict[str, str] = { - "folderPath": asset_name, + "folderPath": folder_entity["path"], "task": task_name, "productType": creator.product_type, "variant": variant @@ -886,10 +908,13 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): def create(self, product_name, instance_data, pre_create_data): project_name: str = self.create_context.get_current_project_name() - asset_name: str = instance_data["folderPath"] + folder_path: str = instance_data["folderPath"] task_name: str = instance_data["task"] - asset_doc: dict[str, Any] = get_asset_by_name( - project_name, asset_name) + folder_entity: dict[str, Any] = ayon_api.get_folder_by_path( + project_name, folder_path) + task_entity: dict[str, Any] = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) render_layers_by_group_id: dict[int, CreatedInstance] = {} render_passes_by_render_layer_id: dict[int, list[CreatedInstance]] = ( @@ -951,8 +976,8 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): instance: Union[CreatedInstance, None] = ( self._prepare_render_layer( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, group_id, scene_groups, mark_layers_for_review, @@ -972,8 +997,8 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): self._prepare_render_passes( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, render_layer_instance, layers, mark_passes_for_review, @@ -1047,16 +1072,16 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): def get_dynamic_data( self, project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, host_name, instance ): dynamic_data = super().get_dynamic_data( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, host_name, instance @@ -1069,19 +1094,22 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): create_context = self.create_context host_name = create_context.host_name project_name = create_context.get_current_project_name() - asset_name = create_context.get_current_folder_path() + folder_path = create_context.get_current_folder_path() task_name = create_context.get_current_task_name() - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path(project_name, folder_path) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, self.default_variant, host_name, ) data = { - "folderPath": asset_name, + "folderPath": folder_path, "task": task_name, "variant": self.default_variant, "creator_attributes": { @@ -1118,24 +1146,29 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): create_context = self.create_context host_name = create_context.host_name project_name = create_context.get_current_project_name() - asset_name = create_context.get_current_folder_path() + folder_path = create_context.get_current_folder_path() task_name = create_context.get_current_task_name() existing_name = existing_instance.get("folderPath") if ( - existing_name != asset_name + existing_name != folder_path or existing_instance["task"] != task_name ): - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, existing_instance["variant"], host_name, existing_instance ) - existing_instance["folderPath"] = asset_name + existing_instance["folderPath"] = folder_path existing_instance["task"] = task_name existing_instance["productName"] = product_name diff --git a/client/ayon_core/hosts/tvpaint/plugins/create/create_review.py b/client/ayon_core/hosts/tvpaint/plugins/create/create_review.py index 1837726cab..acb4f0f8d6 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/create/create_review.py +++ b/client/ayon_core/hosts/tvpaint/plugins/create/create_review.py @@ -1,4 +1,5 @@ -from ayon_core.client import get_asset_by_name +import ayon_api + from ayon_core.pipeline import CreatedInstance from ayon_core.hosts.tvpaint.api.plugin import TVPaintAutoCreator @@ -30,25 +31,29 @@ class TVPaintReviewCreator(TVPaintAutoCreator): create_context = self.create_context host_name = create_context.host_name project_name = create_context.get_current_project_name() - asset_name = create_context.get_current_asset_name() + folder_path = create_context.get_current_folder_path() task_name = create_context.get_current_task_name() - if existing_instance is None: - existing_asset_name = None - else: - existing_asset_name = existing_instance["folderPath"] + existing_folder_path = None + if existing_instance is not None: + existing_folder_path = existing_instance["folderPath"] if existing_instance is None: - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, self.default_variant, host_name ) data = { - "folderPath": asset_name, + "folderPath": folder_path, "task": task_name, "variant": self.default_variant, } @@ -65,18 +70,23 @@ class TVPaintReviewCreator(TVPaintAutoCreator): self._add_instance_to_context(new_instance) elif ( - existing_asset_name != asset_name + existing_folder_path != folder_path or existing_instance["task"] != task_name ): - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, existing_instance["variant"], host_name, existing_instance ) - existing_instance["folderPath"] = asset_name + existing_instance["folderPath"] = folder_path existing_instance["task"] = task_name existing_instance["productName"] = product_name diff --git a/client/ayon_core/hosts/tvpaint/plugins/create/create_workfile.py b/client/ayon_core/hosts/tvpaint/plugins/create/create_workfile.py index 14a11750a5..f21f41439e 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/tvpaint/plugins/create/create_workfile.py @@ -1,4 +1,5 @@ -from ayon_core.client import get_asset_by_name +import ayon_api + from ayon_core.pipeline import CreatedInstance from ayon_core.hosts.tvpaint.api.plugin import TVPaintAutoCreator @@ -26,25 +27,29 @@ class TVPaintWorkfileCreator(TVPaintAutoCreator): create_context = self.create_context host_name = create_context.host_name project_name = create_context.get_current_project_name() - asset_name = create_context.get_current_asset_name() + folder_path = create_context.get_current_folder_path() task_name = create_context.get_current_task_name() - if existing_instance is None: - existing_asset_name = None - else: - existing_asset_name = existing_instance["folderPath"] + existing_folder_path = None + if existing_instance is not None: + existing_folder_path = existing_instance["folderPath"] if existing_instance is None: - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, self.default_variant, host_name ) data = { - "folderPath": asset_name, + "folderPath": folder_path, "task": task_name, "variant": self.default_variant } @@ -58,18 +63,23 @@ class TVPaintWorkfileCreator(TVPaintAutoCreator): self._add_instance_to_context(new_instance) elif ( - existing_asset_name != asset_name + existing_folder_path != folder_path or existing_instance["task"] != task_name ): - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name, - asset_doc, - task_name, + folder_entity, + task_entity, existing_instance["variant"], host_name, existing_instance ) - existing_instance["folderPath"] = asset_name + existing_instance["folderPath"] = folder_path existing_instance["task"] = task_name existing_instance["productName"] = product_name diff --git a/client/ayon_core/hosts/tvpaint/plugins/load/load_image.py b/client/ayon_core/hosts/tvpaint/plugins/load/load_image.py index 924c0f2835..21bbe56a54 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/load/load_image.py +++ b/client/ayon_core/hosts/tvpaint/plugins/load/load_image.py @@ -68,10 +68,10 @@ class ImportImage(plugin.Loader): load_options_str += (load_option + " ") # Prepare layer name - asset_name = context["asset"]["name"] + folder_name = context["folder"]["name"] version_name = context["version"]["name"] layer_name = "{}_{}_v{:0>3}".format( - asset_name, + folder_name, name, version_name ) diff --git a/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py b/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py index 5e629f7b7f..08356dde75 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py +++ b/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py @@ -82,9 +82,9 @@ class LoadImage(plugin.Loader): load_options_str += (load_option + " ") # Prepare layer name - asset_name = context["asset"]["name"] + folder_name = context["folder"]["name"] product_name = context["subset"]["name"] - layer_name = self.get_unique_layer_name(asset_name, product_name) + layer_name = self.get_unique_layer_name(folder_name, product_name) path = self.filepath_from_context(context) diff --git a/client/ayon_core/hosts/tvpaint/plugins/load/load_workfile.py b/client/ayon_core/hosts/tvpaint/plugins/load/load_workfile.py index 49ef9fc37b..4bb34089bd 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/load/load_workfile.py +++ b/client/ayon_core/hosts/tvpaint/plugins/load/load_workfile.py @@ -51,26 +51,32 @@ class LoadWorkfile(plugin.Loader): # Save workfile. host_name = "tvpaint" - project_name = work_context.get("project") - asset_name = work_context.get("asset") - task_name = work_context.get("task") + if "project_name" in work_context: + project_name = context["project_name"] + folder_path = context["folder_path"] + task_name = context["task_name"] + else: + project_name = work_context.get("project") + folder_path = work_context.get("asset") + task_name = work_context.get("task") + # Far cases when there is workfile without work_context - if not asset_name: + if not folder_path: context = get_current_context() project_name = context["project_name"] - asset_name = context["folder_path"] + folder_path = context["folder_path"] task_name = context["task_name"] template_key = get_workfile_template_key_from_context( - asset_name, + project_name, + folder_path, task_name, host_name, - project_name=project_name ) anatomy = Anatomy(project_name) data = get_template_data_with_names( - project_name, asset_name, task_name, host_name + project_name, folder_path, task_name, host_name ) data["root"] = anatomy.roots diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_instance_frames.py b/client/ayon_core/hosts/tvpaint/plugins/publish/collect_instance_frames.py index e7b7b2cad1..5f134a0cd0 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_instance_frames.py +++ b/client/ayon_core/hosts/tvpaint/plugins/publish/collect_instance_frames.py @@ -15,14 +15,14 @@ class CollectOutputFrameRange(pyblish.api.InstancePlugin): families = ["review", "render"] def process(self, instance): - asset_doc = instance.data.get("assetEntity") - if not asset_doc: + folder_entity = instance.data.get("folderEntity") + if not folder_entity: return context = instance.context - frame_start = asset_doc["data"]["frameStart"] - fps = asset_doc["data"]["fps"] + frame_start = folder_entity["attrib"]["frameStart"] + fps = folder_entity["attrib"]["fps"] frame_end = frame_start + ( context.data["sceneMarkOut"] - context.data["sceneMarkIn"] ) diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile_data.py b/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile_data.py index 414b09c123..3155773bda 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile_data.py +++ b/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile_data.py @@ -92,11 +92,11 @@ class CollectWorkfileData(pyblish.api.ContextPlugin): os.environ[env_key] = workfile_context[key] self.log.info("Context changed to: {}".format(workfile_context)) - asset_name = workfile_context["folder_path"] + folder_path = workfile_context["folder_path"] task_name = workfile_context["task_name"] else: - asset_name = current_context["folder_path"] + folder_path = current_context["folder_path"] task_name = current_context["task_name"] # Handle older workfiles or workfiles without metadata self.log.warning(( @@ -104,12 +104,12 @@ class CollectWorkfileData(pyblish.api.ContextPlugin): " Using current Session context." )) - # Store context asset name - context.data["folderPath"] = asset_name + # Store context folder path + context.data["folderPath"] = folder_path context.data["task"] = task_name self.log.info( - "Context is set to Asset: \"{}\" and Task: \"{}\"".format( - asset_name, task_name + "Context is set to Folder: \"{}\" and Task: \"{}\"".format( + folder_path, task_name ) ) diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml b/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml index 83753b3410..5ac8d7fdcb 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml +++ b/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml @@ -8,7 +8,7 @@ Context of the given product doesn't match your current scene. ### How to repair? -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. +Yout can fix this with "Repair" button on the right. This will use '{expected_folder}' folder path and overwrite '{found_folder}' folder path in scene metadata. After that restart publishing with Reload button. diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_asset_name.py b/client/ayon_core/hosts/tvpaint/plugins/publish/validate_asset_name.py index 927d601e34..764c090720 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_asset_name.py +++ b/client/ayon_core/hosts/tvpaint/plugins/publish/validate_asset_name.py @@ -9,8 +9,8 @@ from ayon_core.hosts.tvpaint.api.pipeline import ( ) -class FixAssetNames(pyblish.api.Action): - """Repair the asset names. +class FixFolderPaths(pyblish.api.Action): + """Repair the folder paths. Change instanace metadata in the workfile. """ @@ -20,16 +20,16 @@ class FixAssetNames(pyblish.api.Action): on = "failed" def process(self, context, plugin): - context_asset_name = context.data["folderPath"] + context_folder_path = context.data["folderPath"] old_instance_items = list_instances() new_instance_items = [] for instance_item in old_instance_items: - instance_asset_name = instance_item.get("folderPath") + instance_folder_path = instance_item.get("folderPath") if ( - instance_asset_name - and instance_asset_name != context_asset_name + instance_folder_path + and instance_folder_path != context_folder_path ): - instance_item["folderPath"] = context_asset_name + instance_item["folderPath"] = context_folder_path new_instance_items.append(instance_item) write_instances(new_instance_items) @@ -38,23 +38,23 @@ class ValidateAssetName( OptionalPyblishPluginMixin, pyblish.api.ContextPlugin ): - """Validate asset name present on instance. + """Validate folder path present on instance. - Asset name on instance should be the same as context's. + Folder path on instance should be the same as context's. """ - label = "Validate Asset Names" + label = "Validate Folder Paths" order = pyblish.api.ValidatorOrder hosts = ["tvpaint"] - actions = [FixAssetNames] + actions = [FixFolderPaths] def process(self, context): if not self.is_active(context.data): return - context_asset_name = context.data["folderPath"] + context_folder_path = context.data["folderPath"] for instance in context: - asset_name = instance.data.get("folderPath") - if asset_name and asset_name == context_asset_name: + folder_path = instance.data.get("folderPath") + if folder_path and folder_path == context_folder_path: continue instance_label = ( @@ -64,14 +64,14 @@ class ValidateAssetName( raise PublishXmlValidationError( self, ( - "Different asset name on instance then context's." - " Instance \"{}\" has asset name: \"{}\"" - " Context asset name is: \"{}\"" + "Different folder path on instance then context's." + " Instance \"{}\" has folder path: \"{}\"" + " Context folder path is: \"{}\"" ).format( - instance_label, asset_name, context_asset_name + instance_label, folder_path, context_folder_path ), formatting_data={ - "expected_asset": context_asset_name, - "found_asset": asset_name + "expected_folder": context_folder_path, + "found_folder": folder_path } ) diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_scene_settings.py b/client/ayon_core/hosts/tvpaint/plugins/publish/validate_scene_settings.py index 2268e59d88..5e42b5ab2f 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_scene_settings.py +++ b/client/ayon_core/hosts/tvpaint/plugins/publish/validate_scene_settings.py @@ -22,7 +22,7 @@ class ValidateProjectSettings( if not self.is_active(context.data): return - expected_data = context.data["assetEntity"]["data"] + folder_attributes = context.data["folderEntity"]["attrib"] scene_data = { "fps": context.data.get("sceneFps"), "resolutionWidth": context.data.get("sceneWidth"), @@ -31,7 +31,7 @@ class ValidateProjectSettings( } invalid = {} for k in scene_data.keys(): - expected_value = expected_data[k] + expected_value = folder_attributes[k] if scene_data[k] != expected_value: invalid[k] = { "current": scene_data[k], "expected": expected_value @@ -46,13 +46,13 @@ class ValidateProjectSettings( json.dumps(invalid, sort_keys=True, indent=4) ), formatting_data={ - "expected_fps": expected_data["fps"], + "expected_fps": folder_attributes["fps"], "current_fps": scene_data["fps"], - "expected_width": expected_data["resolutionWidth"], - "expected_height": expected_data["resolutionHeight"], + "expected_width": folder_attributes["resolutionWidth"], + "expected_height": folder_attributes["resolutionHeight"], "current_width": scene_data["resolutionWidth"], "current_height": scene_data["resolutionHeight"], - "expected_pixel_ratio": expected_data["pixelAspect"], + "expected_pixel_ratio": folder_attributes["pixelAspect"], "current_pixel_ratio": scene_data["pixelAspect"] } ) From 92ce8956c4a8f490bbaeb3a85a1713a6cce38a2f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 16:41:29 +0100 Subject: [PATCH 418/573] traypublisher use folder and task entities --- .../hosts/traypublisher/api/editorial.py | 90 ++++++------ .../hosts/traypublisher/api/plugin.py | 68 +++++----- .../hosts/traypublisher/batch_parsing.py | 128 +++++++++++------- .../plugins/create/create_colorspace_look.py | 19 ++- .../plugins/create/create_editorial.py | 45 +++--- .../plugins/create/create_movie_batch.py | 83 +++++++----- .../plugins/create/create_online.py | 18 +-- .../collect_frame_data_from_asset_entity.py | 12 +- .../plugins/publish/collect_review_frames.py | 10 +- .../publish/collect_sequence_frame_data.py | 10 +- .../plugins/publish/collect_shot_instances.py | 5 +- .../help/validate_existing_version.xml | 2 +- .../publish/validate_existing_version.py | 2 +- .../plugins/publish/validate_frame_ranges.py | 19 ++- .../plugins/publish/validate_online_file.py | 4 +- 15 files changed, 294 insertions(+), 221 deletions(-) diff --git a/client/ayon_core/hosts/traypublisher/api/editorial.py b/client/ayon_core/hosts/traypublisher/api/editorial.py index 7cdfb0b699..8dedec7398 100644 --- a/client/ayon_core/hosts/traypublisher/api/editorial.py +++ b/client/ayon_core/hosts/traypublisher/api/editorial.py @@ -1,7 +1,8 @@ import re from copy import deepcopy -from ayon_core.client import get_asset_by_id +import ayon_api + from ayon_core.pipeline.create import CreatorError @@ -88,7 +89,7 @@ class ShotMetadataSolver: if not self.clip_name_tokenizer: return output_data - parent_name = source_data["selected_asset_doc"]["name"] + parent_name = source_data["selected_folder_entity"]["name"] search_text = parent_name + clip_name @@ -157,11 +158,11 @@ class ShotMetadataSolver: **_parent_tokens_formatting_data) except KeyError as _error: raise CreatorError(( - "Make sure all keys in settings are correct : \n\n" - f"`{_error}` from template string " - f"{shot_hierarchy['parents_path']}, " - f" has no equivalent in \n" - f"{list(_parent_tokens_formatting_data.keys())} parents" + "Make sure all keys in settings are correct:\n\n" + f"`{_error}` from template string" + f" {shot_hierarchy['parents_path']}," + f" has no equivalent in" + f"\n{list(_parent_tokens_formatting_data.keys())} parents" )) parent_token_name = ( @@ -174,7 +175,8 @@ class ShotMetadataSolver: # find parent type parent_token_type = _parent_tokens_type[parent_token_name] - # in case selected context is set to the same asset + # in case selected context is set to the same folder + # TODO keep index with 'parents' - name check is not enough if ( _index == 0 and parents[-1]["entity_name"] == parent_name @@ -213,50 +215,58 @@ class ShotMetadataSolver: ] ) if parents else "" - def _get_parents_from_selected_asset( + def _get_parents_from_selected_folder( self, - asset_doc, - project_entity + project_entity, + folder_entity, ): - """Returning parents from context on selected asset. + """Returning parents from context on selected folder. Context defined in Traypublisher project tree. Args: - asset_doc (db obj): selected asset doc project_entity (dict[str, Any]): Project entity. + folder_entity (dict[str, Any]): Selected folder entity. Returns: - list: list of dict parent components + list: list of dict parent components """ + project_name = project_entity["name"] - visual_hierarchy = [asset_doc] - current_doc = asset_doc + path_entries = folder_entity["path"].split("/") + subpaths = [] + subpath_items = [] + for name in path_entries: + subpath_items.append(name) + if name: + subpaths.append("/".join(subpath_items)) + # Remove last name because we already have folder entity + subpaths.pop(-1) - # looping through all available visual parents - # if they are not available anymore than it breaks - while True: - visual_parent_id = current_doc["data"]["visualParent"] - visual_parent = None - if visual_parent_id: - visual_parent = get_asset_by_id(project_name, visual_parent_id) - - if not visual_parent: - break - visual_hierarchy.append(visual_parent) - current_doc = visual_parent + folder_entity_by_path = {} + if subpaths: + folder_entity_by_path = { + parent_folder["path"]: parent_folder + for parent_folder in ayon_api.get_folders( + project_name, folder_paths=subpaths + ) + } + folders_hierarchy = [ + folder_entity_by_path[folder_path] + for folder_path in subpaths + ] + folders_hierarchy.append(folder_entity) # add current selection context hierarchy - output = [] - for entity in reversed(visual_hierarchy): - output.append({ - "entity_type": entity["data"]["entityType"], - "entity_name": entity["name"] - }) - output.append({ + output = [{ "entity_type": "project", "entity_name": project_name, - }) + }] + for entity in folders_hierarchy: + output.append({ + "entity_type": entity["folderType"], + "entity_name": entity["name"] + }) return output def _generate_tasks_from_settings(self, project_entity): @@ -310,7 +320,7 @@ class ShotMetadataSolver: """ tasks = {} - asset_doc = source_data["selected_asset_doc"] + folder_entity = source_data["selected_folder_entity"] project_entity = source_data["project_entity"] # match clip to shot name at start @@ -319,9 +329,9 @@ class ShotMetadataSolver: # parse all tokens and generate formatting data formatting_data = self._generate_tokens(shot_name, source_data) - # generate parents from selected asset - parents = self._get_parents_from_selected_asset( - asset_doc, project_entity + # generate parents from selected folder + parents = self._get_parents_from_selected_folder( + project_entity, folder_entity ) if self.shot_rename["enabled"]: diff --git a/client/ayon_core/hosts/traypublisher/api/plugin.py b/client/ayon_core/hosts/traypublisher/api/plugin.py index be50383510..bc7d352b10 100644 --- a/client/ayon_core/hosts/traypublisher/api/plugin.py +++ b/client/ayon_core/hosts/traypublisher/api/plugin.py @@ -1,8 +1,8 @@ +import ayon_api + from ayon_core.client import ( - get_assets, get_subsets, get_last_versions, - get_asset_name_identifier, ) from ayon_core.lib.attribute_definitions import ( FileDef, @@ -117,10 +117,12 @@ class SettingsCreator(TrayPublishCreator): # Fill 'version_to_use' if version control is enabled if self.allow_version_control: - asset_name = data["folderPath"] - subset_docs_by_asset_id = self._prepare_next_versions( - [asset_name], [product_name]) - version = subset_docs_by_asset_id[asset_name].get(product_name) + folder_path = data["folderPath"] + subset_docs_by_folder_path = self._prepare_next_versions( + [folder_path], [product_name]) + version = subset_docs_by_folder_path[folder_path].get( + product_name + ) pre_create_data["version_to_use"] = version data["_previous_last_version"] = version @@ -137,46 +139,46 @@ class SettingsCreator(TrayPublishCreator): if thumbnail_path: self.set_instance_thumbnail_path(new_instance.id, thumbnail_path) - def _prepare_next_versions(self, asset_names, product_names): - """Prepare next versions for given asset and product names. + def _prepare_next_versions(self, folder_paths, product_names): + """Prepare next versions for given folder and product names. Todos: - Expect combination of product names by asset name to avoid + Expect combination of product names by folder path to avoid unnecessary server calls for unused products. Args: - asset_names (Iterable[str]): Asset names. - product_names (Iterable[str]): Subset names. + folder_paths (Iterable[str]): Folder paths. + product_names (Iterable[str]): Product names. Returns: - dict[str, dict[str, int]]: Last versions by asset + dict[str, dict[str, int]]: Last versions by fodler path and product names. """ # Prepare all versions for all combinations to '1' # TODO use 'ayon_core.pipeline.version_start' logic - subset_docs_by_asset_id = { - asset_name: { + subset_docs_by_folder_path = { + folder_path: { product_name: 1 for product_name in product_names } - for asset_name in asset_names + for folder_path in folder_paths } - if not asset_names or not product_names: - return subset_docs_by_asset_id + if not folder_paths or not product_names: + return subset_docs_by_folder_path - asset_docs = get_assets( + folder_entities = ayon_api.get_folders( self.project_name, - asset_names=asset_names, - fields=["_id", "name", "data.parents"] + folder_paths=folder_paths, + fields={"id", "path"} ) - asset_names_by_id = { - asset_doc["_id"]: get_asset_name_identifier(asset_doc) - for asset_doc in asset_docs + folder_paths_by_id = { + folder_entity["id"]: folder_entity["path"] + for folder_entity in folder_entities } subset_docs = list(get_subsets( self.project_name, - asset_ids=asset_names_by_id.keys(), + asset_ids=folder_paths_by_id.keys(), subset_names=product_names, fields=["_id", "name", "parent"] )) @@ -188,16 +190,16 @@ class SettingsCreator(TrayPublishCreator): fields=["name", "parent"]) for subset_doc in subset_docs: - asset_id = subset_doc["parent"] - asset_name = asset_names_by_id[asset_id] + folder_id = subset_doc["parent"] + folder_path = folder_paths_by_id[folder_id] product_name = subset_doc["name"] product_id = subset_doc["_id"] last_version = last_versions.get(product_id) version = 0 if last_version is not None: version = last_version["name"] - subset_docs_by_asset_id[asset_name][product_name] += version - return subset_docs_by_asset_id + subset_docs_by_folder_path[folder_path][product_name] += version + return subset_docs_by_folder_path def _fill_next_versions(self, instances_data): """Fill next version for instances. @@ -222,20 +224,20 @@ class SettingsCreator(TrayPublishCreator): ): filtered_instance_data.append(instance) - asset_names = { + folder_paths = { instance["folderPath"] for instance in filtered_instance_data } product_names = { instance["productName"] for instance in filtered_instance_data} - subset_docs_by_asset_id = self._prepare_next_versions( - asset_names, product_names + subset_docs_by_folder_path = self._prepare_next_versions( + folder_paths, product_names ) for instance in filtered_instance_data: - asset_name = instance["folderPath"] + folder_path = instance["folderPath"] product_name = instance["productName"] - version = subset_docs_by_asset_id[asset_name][product_name] + version = subset_docs_by_folder_path[folder_path][product_name] instance["creator_attributes"]["version_to_use"] = version instance["_previous_last_version"] = version diff --git a/client/ayon_core/hosts/traypublisher/batch_parsing.py b/client/ayon_core/hosts/traypublisher/batch_parsing.py index fdb3021a20..fa3c8d5b9a 100644 --- a/client/ayon_core/hosts/traypublisher/batch_parsing.py +++ b/client/ayon_core/hosts/traypublisher/batch_parsing.py @@ -2,13 +2,18 @@ import os import re +import ayon_api + from ayon_core.lib import Logger -from ayon_core.client import get_assets, get_asset_by_name -def get_asset_doc_from_file_name(source_filename, project_name, - version_regex, all_selected_asset_ids=None): - """Try to parse out asset name from file name provided. +def get_folder_entity_from_filename( + project_name, + source_filename, + version_regex, + all_selected_folder_ids=None +): + """Try to parse out folder name from file name provided. Artists might provide various file name formats. Currently handled: @@ -17,72 +22,101 @@ def get_asset_doc_from_file_name(source_filename, project_name, - my_chair_to_upload.mov """ version = None - asset_name = os.path.splitext(source_filename)[0] - # Always first check if source filename is directly asset (eg. 'chair.mov') - matching_asset_doc = get_asset_by_name_case_not_sensitive( - project_name, asset_name, all_selected_asset_ids) + folder_name = os.path.splitext(source_filename)[0] + # Always first check if source filename is directly folder + # (eg. 'chair.mov') + matching_folder_entity = get_folder_by_name_case_not_sensitive( + project_name, folder_name, all_selected_folder_ids) - if matching_asset_doc is None: + if matching_folder_entity is None: # name contains also a version - matching_asset_doc, version = ( - parse_with_version(project_name, asset_name, version_regex, - all_selected_asset_ids)) + matching_folder_entity, version = ( + parse_with_version( + project_name, + folder_name, + version_regex, + all_selected_folder_ids + ) + ) - if matching_asset_doc is None: - matching_asset_doc = parse_containing(project_name, asset_name, - all_selected_asset_ids) + if matching_folder_entity is None: + matching_folder_entity = parse_containing( + project_name, + folder_name, + all_selected_folder_ids + ) - return matching_asset_doc, version + return matching_folder_entity, version -def parse_with_version(project_name, asset_name, version_regex, - all_selected_asset_ids=None, log=None): - """Try to parse asset name from a file name containing version too +def parse_with_version( + project_name, + folder_name, + version_regex, + all_selected_folder_ids=None, + log=None +): + """Try to parse folder name from a file name containing version too Eg. 'chair_v001.mov' >> 'chair', 1 """ if not log: log = Logger.get_logger(__name__) log.debug( - ("Asset doc by \"{}\" was not found, trying version regex.". - format(asset_name))) + ("Folder entity by \"{}\" was not found, trying version regex.". + format(folder_name))) - matching_asset_doc = version_number = None + matching_folder_entity = version_number = None - regex_result = version_regex.findall(asset_name) + regex_result = version_regex.findall(folder_name) if regex_result: - _asset_name, _version_number = regex_result[0] - matching_asset_doc = get_asset_by_name_case_not_sensitive( - project_name, _asset_name, - all_selected_asset_ids=all_selected_asset_ids) - if matching_asset_doc: + _folder_name, _version_number = regex_result[0] + matching_folder_entity = get_folder_by_name_case_not_sensitive( + project_name, + _folder_name, + all_selected_folder_ids=all_selected_folder_ids + ) + if matching_folder_entity: version_number = int(_version_number) - return matching_asset_doc, version_number + return matching_folder_entity, version_number -def parse_containing(project_name, asset_name, all_selected_asset_ids=None): - """Look if file name contains any existing asset name""" - for asset_doc in get_assets(project_name, asset_ids=all_selected_asset_ids, - fields=["name"]): - if asset_doc["name"].lower() in asset_name.lower(): - return get_asset_by_name(project_name, asset_doc["name"]) +def parse_containing(project_name, folder_name, all_selected_folder_ids=None): + """Look if file name contains any existing folder name""" + for folder_entity in ayon_api.get_folders( + project_name, + folder_ids=all_selected_folder_ids, + fields={"id", "name"} + ): + if folder_entity["name"].lower() in folder_name.lower(): + return ayon_api.get_folder_by_id( + project_name, + folder_entity["id"] + ) -def get_asset_by_name_case_not_sensitive(project_name, asset_name, - all_selected_asset_ids=None, - log=None): +def get_folder_by_name_case_not_sensitive( + project_name, + folder_name, + all_selected_folder_ids=None, + log=None +): """Handle more cases in file names""" if not log: log = Logger.get_logger(__name__) - asset_name = re.compile(asset_name, re.IGNORECASE) + folder_name = re.compile(folder_name, re.IGNORECASE) - assets = list(get_assets(project_name, asset_ids=all_selected_asset_ids, - asset_names=[asset_name])) - if assets: - if len(assets) > 1: - log.warning("Too many records found for {}".format( - asset_name)) - return + folder_entities = list(ayon_api.get_folders( + project_name, + folder_ids=all_selected_folder_ids, + folder_names=[folder_name] + )) - return assets.pop() + if len(folder_entities) > 1: + log.warning("Too many records found for {}".format( + folder_name)) + return None + + if folder_entities: + return folder_entities.pop() diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py index 5c913b3289..4d865c1c5c 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py @@ -5,8 +5,8 @@ This creator is used to publish colorspace look files thanks to production type `ociolook`. All files are published as representation. """ from pathlib import Path +import ayon_api -from ayon_core.client import get_asset_by_name from ayon_core.lib.attribute_definitions import ( FileDef, EnumDef, TextDef, UISeparatorDef ) @@ -54,14 +54,21 @@ This creator publishes color space look file (LUT). # this should never happen raise CreatorError("Missing files from representation") - asset_name = instance_data["folderPath"] - asset_doc = get_asset_by_name( - self.project_name, asset_name) + folder_path = instance_data["folderPath"] + task_name = instance_data["task"] + folder_entity = ayon_api.get_folder_by_path( + self.project_name, folder_path) + + task_entity = None + if task_name: + task_entity = ayon_api.get_task_by_name( + self.project_name, folder_entity["id"], task_name + ) product_name = self.get_product_name( project_name=self.project_name, - asset_doc=asset_doc, - task_name=instance_data["task"] or "Not set", + folder_entity=folder_entity, + task_entity=task_entity, variant=instance_data["variant"], ) diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py index 8ef96ed302..df35f54291 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py @@ -1,10 +1,9 @@ import os from copy import deepcopy -import opentimelineio as otio import ayon_api +import opentimelineio as otio -from ayon_core.client import get_asset_by_name from ayon_core.hosts.traypublisher.api.plugin import ( TrayPublishCreator, HiddenTrayPublishCreator @@ -195,12 +194,14 @@ or updating already created. Publishing will create OTIO file. if k not in product_types } - asset_name = instance_data["folderPath"] - asset_doc = get_asset_by_name(self.project_name, asset_name) + folder_path = instance_data["folderPath"] + folder_entity = ayon_api.get_folder_by_path( + self.project_name, folder_path + ) if pre_create_data["fps"] == "from_selection": - # get asset doc data attributes - fps = asset_doc["data"]["fps"] + # get 'fps' from folder attributes + fps = folder_entity["attrib"]["fps"] else: fps = float(pre_create_data["fps"]) @@ -225,18 +226,18 @@ or updating already created. Publishing will create OTIO file. # Create all clip instances clip_instance_properties.update({ "fps": fps, - "parent_asset_name": asset_name, "variant": instance_data["variant"] }) # create clip instances self._get_clip_instances( + folder_entity, otio_timeline, media_path, clip_instance_properties, allowed_product_type_presets, os.path.basename(seq_path), - first_otio_timeline + first_otio_timeline, ) if not first_otio_timeline: @@ -247,7 +248,8 @@ or updating already created. Publishing will create OTIO file. self._create_otio_instance( product_name, instance_data, - seq_path, media_path, + seq_path, + media_path, first_otio_timeline ) @@ -331,6 +333,7 @@ or updating already created. Publishing will create OTIO file. def _get_clip_instances( self, + folder_entity, otio_timeline, media_path, instance_data, @@ -341,6 +344,7 @@ or updating already created. Publishing will create OTIO file. """Helping function for creating clip instance Args: + folder_entity (dict[str, Any]): Folder entity. otio_timeline (otio.Timeline): otio timeline object media_path (str): media file path string instance_data (dict): clip instance data @@ -372,7 +376,6 @@ or updating already created. Publishing will create OTIO file. if not self._validate_clip_for_processing(otio_clip): continue - # get available frames info to clip data self._create_otio_reference(otio_clip, media_path, media_data) @@ -382,7 +385,8 @@ or updating already created. Publishing will create OTIO file. base_instance_data = self._get_base_instance_data( otio_clip, instance_data, - track_start_frame + track_start_frame, + folder_entity ) parenting_data = { @@ -574,7 +578,7 @@ or updating already created. Publishing will create OTIO file. Returns: str: label string """ - asset_name = instance_data["creator_attributes"]["folderPath"] + folder_path = instance_data["creator_attributes"]["folderPath"] variant_name = instance_data["variant"] product_type = product_type_preset["product_type"] @@ -587,7 +591,7 @@ or updating already created. Publishing will create OTIO file. product_type, _variant_name.capitalize() ) label = "{} {}".format( - asset_name, + folder_path, product_name ) @@ -605,6 +609,7 @@ or updating already created. Publishing will create OTIO file. otio_clip, instance_data, track_start_frame, + folder_entity, ): """Factoring basic set of instance data. @@ -615,9 +620,12 @@ or updating already created. Publishing will create OTIO file. Returns: dict: instance data + """ + parent_folder_path = folder_entity["path"] + parent_folder_name = parent_folder_path.rsplit("/", 1)[-1] + # get clip instance properties - parent_asset_name = instance_data["parent_asset_name"] handle_start = instance_data["handle_start"] handle_end = instance_data["handle_end"] timeline_offset = instance_data["timeline_offset"] @@ -625,7 +633,7 @@ or updating already created. Publishing will create OTIO file. fps = instance_data["fps"] variant_name = instance_data["variant"] - # basic unique asset name + # basic unique folder name clip_name = os.path.splitext(otio_clip.name)[0] project_entity = ayon_api.get_project(self.project_name) @@ -637,11 +645,10 @@ or updating already created. Publishing will create OTIO file. "name": self.project_name, "code": project_entity["code"] }, - "parent": parent_asset_name, + "parent": parent_folder_name, "app": self.host_name }, - "selected_asset_doc": get_asset_by_name( - self.project_name, parent_asset_name), + "selected_folder_entity": folder_entity, "project_entity": project_entity } ) @@ -679,7 +686,7 @@ or updating already created. Publishing will create OTIO file. # update base instance data with context data # and also update creator attributes with context data creator_attributes["folderPath"] = shot_metadata.pop("folderPath") - base_instance_data["folderPath"] = parent_asset_name + base_instance_data["folderPath"] = parent_folder_path # add creator attributes to shared instance data base_instance_data["creator_attributes"] = creator_attributes diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_movie_batch.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_movie_batch.py index 9b3dfdd334..1fa431fcd0 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_movie_batch.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_movie_batch.py @@ -1,8 +1,10 @@ import copy import os import re +import collections + +import ayon_api -from ayon_core.client import get_asset_name_identifier from ayon_core.lib import ( FileDef, BoolDef, @@ -17,7 +19,7 @@ from ayon_core.pipeline.create import ( from ayon_core.hosts.traypublisher.api.plugin import TrayPublishCreator from ayon_core.hosts.traypublisher.batch_parsing import ( - get_asset_doc_from_file_name + get_folder_entity_from_filename ) @@ -53,21 +55,46 @@ class BatchMovieCreator(TrayPublishCreator): if not file_paths: return + data_by_folder_id = collections.defaultdict(list) for file_info in file_paths: instance_data = copy.deepcopy(data) file_name = file_info["filenames"][0] filepath = os.path.join(file_info["directory"], file_name) instance_data["creator_attributes"] = {"filepath": filepath} - asset_doc, version = get_asset_doc_from_file_name( - file_name, self.project_name, self.version_regex) + folder_entity, version = get_folder_entity_from_filename( + self.project_name, file_name, self.version_regex) + data_by_folder_id[folder_entity["id"]].append( + (instance_data, folder_entity) + ) - product_name, task_name = self._get_product_and_task( - asset_doc, data["variant"], self.project_name) + all_task_entities = ayon_api.get_tasks( + self.project_name, task_ids=set(data_by_folder_id.keys()) + ) + task_entity_by_folder_id = collections.defaultdict(dict) + for task_entity in all_task_entities: + folder_id = task_entity["folderId"] + task_name = task_entity["name"].lower() + task_entity_by_folder_id[folder_id][task_name] = task_entity - asset_name = get_asset_name_identifier(asset_doc) + for ( + folder_id, (instance_data, folder_entity) + ) in data_by_folder_id.items(): + task_entities_by_name = task_entity_by_folder_id[folder_id] + task_name = None + task_entity = None + for default_task_name in self.default_tasks: + _name = default_task_name.lower() + if _name in task_entities_by_name: + task_name = task_entity["name"] + task_entity = task_entities_by_name[_name] + break - instance_data["folderPath"] = asset_name + product_name = self._get_product_name( + self.project_name, task_entity, data["variant"] + ) + + instance_data["folderPath"] = folder_entity["path"] instance_data["task"] = task_name # Create new instance @@ -75,15 +102,13 @@ class BatchMovieCreator(TrayPublishCreator): instance_data, self) self._store_new_instance(new_instance) - def _get_product_and_task(self, asset_doc, variant, project_name): + def _get_product_name(self, project_name, task_entity, variant): """Create product name according to standard template process""" - task_name = self._get_task_name(asset_doc) host_name = self.create_context.host_name try: product_name = get_product_name( project_name, - asset_doc, - task_name, + task_entity, host_name, self.product_type, variant, @@ -92,34 +117,22 @@ class BatchMovieCreator(TrayPublishCreator): # Create instance with fake task # - instance will be marked as invalid so it can't be published # but user have ability to change it - # NOTE: This expect that there is not task 'Undefined' on asset - task_name = "Undefined" + # NOTE: This expect that there is not task 'Undefined' on folder + dumb_value = "Undefined" + task_entity = { + "name": dumb_value, + "taskType": dumb_value, + "short": dumb_value, + } product_name = get_product_name( project_name, - asset_doc, - task_name, + task_entity, host_name, self.product_type, variant, ) - return product_name, task_name - - def _get_task_name(self, asset_doc): - """Get applicable task from 'asset_doc' """ - available_task_names = {} - asset_tasks = asset_doc.get("data", {}).get("tasks") or {} - for task_name in asset_tasks.keys(): - available_task_names[task_name.lower()] = task_name - - task_name = None - for _task_name in self.default_tasks: - _task_name_low = _task_name.lower() - if _task_name_low in available_task_names: - task_name = available_task_names[_task_name_low] - break - - return task_name + return product_name def get_instance_attr_defs(self): return [ @@ -149,8 +162,8 @@ class BatchMovieCreator(TrayPublishCreator): ] def get_detail_description(self): - return """# Publish batch of .mov to multiple assets. + return """# Publish batch of .mov to multiple folders. - File names must then contain only asset name, or asset name + version. + File names must then contain only folder name, or folder name + version. (eg. 'chair.mov', 'chair_v001.mov', not really safe `my_chair_v001.mov` """ diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_online.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_online.py index a25da0bf34..a832254ad9 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_online.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_online.py @@ -3,11 +3,13 @@ Online file retain their original name and use it as product name. To avoid conflicts, this creator checks if product with this name already -exists under selected asset. +exists under selected folder. """ from pathlib import Path -# from ayon_core.client import get_subset_by_name, get_asset_by_name +# import ayon_api + +# from ayon_core.client import get_subset_by_name from ayon_core.lib.attribute_definitions import FileDef, BoolDef from ayon_core.pipeline import ( CreatedInstance, @@ -52,14 +54,14 @@ class OnlineCreator(TrayPublishCreator): # disable check for existing product with the same name """ - asset = get_asset_by_name( - self.project_name, instance_data["folderPath"], fields=["_id"]) + folder_entity = ayon_api.get_folder_by_path( + self.project_name, instance_data["folderPath"], fields={"id"}) if get_subset_by_name( - self.project_name, origin_basename, asset["_id"], + self.project_name, origin_basename, folder_entity["id"], fields=["_id"]): raise CreatorError(f"product with {origin_basename} already " - "exists in selected asset") + "exists in selected folder") """ instance_data["originalBasename"] = origin_basename @@ -103,8 +105,8 @@ class OnlineCreator(TrayPublishCreator): def get_product_name( self, project_name, - asset_doc, - task_name, + folder_entity, + task_entity, variant, host_name=None, instance=None diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_frame_data_from_asset_entity.py b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_frame_data_from_asset_entity.py index e8a2cae16c..4d203649c7 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_frame_data_from_asset_entity.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_frame_data_from_asset_entity.py @@ -2,14 +2,14 @@ import pyblish.api class CollectFrameDataFromAssetEntity(pyblish.api.InstancePlugin): - """Collect Frame Data From AssetEntity found in context + """Collect Frame Data From 'folderEntity' found in context. Frame range data will only be collected if the keys are not yet collected for the instance. """ order = pyblish.api.CollectorOrder + 0.491 - label = "Collect Missing Frame Data From Asset" + label = "Collect Missing Frame Data From Folder" families = ["plate", "pointcache", "vdbcache", "online", "render"] @@ -27,11 +27,11 @@ class CollectFrameDataFromAssetEntity(pyblish.api.InstancePlugin): if key not in instance.data: missing_keys.append(key) keys_set = [] + folder_attributes = instance.data["folderEntity"]["attrib"] for key in missing_keys: - asset_data = instance.data["assetEntity"]["data"] - if key in asset_data: - instance.data[key] = asset_data[key] + if key in folder_attributes: + instance.data[key] = folder_attributes[key] keys_set.append(key) if keys_set: self.log.debug(f"Frame range data {keys_set} " - "has been collected from asset entity.") + "has been collected from folder entity.") diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_review_frames.py b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_review_frames.py index 6b41c0dd21..7eceda968a 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_review_frames.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_review_frames.py @@ -20,12 +20,12 @@ class CollectReviewInfo(pyblish.api.InstancePlugin): hosts = ["traypublisher"] def process(self, instance): - asset_entity = instance.data.get("assetEntity") - if instance.data.get("frameStart") is not None or not asset_entity: + folder_entity = instance.data.get("folderEntity") + if instance.data.get("frameStart") is not None or not folder_entity: self.log.debug("Missing required data on instance") return - asset_data = asset_entity["data"] + folder_attributes = folder_entity["attrib"] # Store collected data for logging collected_data = {} for key in ( @@ -35,9 +35,9 @@ class CollectReviewInfo(pyblish.api.InstancePlugin): "handleStart", "handleEnd", ): - if key in instance.data or key not in asset_data: + if key in instance.data or key not in folder_attributes: continue - value = asset_data[key] + value = folder_attributes[key] collected_data[key] = value instance.data[key] = value self.log.debug("Collected data: {}".format(str(collected_data))) diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_sequence_frame_data.py b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_sequence_frame_data.py index 7eded0f6f5..bd5bac114d 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_sequence_frame_data.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_sequence_frame_data.py @@ -28,9 +28,9 @@ class CollectSequenceFrameData( return # editorial would fail since they might not be in database yet - new_asset_publishing = instance.data.get("newAssetPublishing") - if new_asset_publishing: - self.log.debug("Instance is creating new asset. Skipping.") + new_folder_publishing = instance.data.get("newAssetPublishing") + if new_folder_publishing: + self.log.debug("Instance is creating new folders. Skipping.") return frame_data = self.get_frame_data_from_repre_sequence(instance) @@ -46,7 +46,7 @@ class CollectSequenceFrameData( def get_frame_data_from_repre_sequence(self, instance): repres = instance.data.get("representations") - asset_data = instance.data["assetEntity"]["data"] + folder_attributes = instance.data["folderEntity"]["attrib"] if repres: first_repre = repres[0] @@ -72,5 +72,5 @@ class CollectSequenceFrameData( "frameEnd": repres_frames[-1], "handleStart": 0, "handleEnd": 0, - "fps": asset_data["fps"] + "fps": folder_attributes["fps"] } diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py index d489528c57..edcbb27cb3 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py @@ -169,9 +169,8 @@ class CollectShotInstance(pyblish.api.InstancePlugin): parents = instance.data.get('parents', []) - # Split by '/' for AYON where asset is a path - asset_name = instance.data["folderPath"].split("/")[-1] - actual = {asset_name: in_info} + folder_name = instance.data["folderPath"].split("/")[-1] + actual = {folder_name: in_info} for parent in reversed(parents): parent_name = parent["entity_name"] diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/help/validate_existing_version.xml b/client/ayon_core/hosts/traypublisher/plugins/publish/help/validate_existing_version.xml index 726ccdffe3..89997b4c8c 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/help/validate_existing_version.xml +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/help/validate_existing_version.xml @@ -5,7 +5,7 @@ ## Version already exists -Version {version} you have set on instance '{product_name}' under '{asset_name}' already exists. This validation is enabled by default to prevent accidental override of existing versions. +Version {version} you have set on instance '{product_name}' under '{folder_path}' already exists. This validation is enabled by default to prevent accidental override of existing versions. ### How to repair? - Click on 'Repair' action -> this will change version to next available. diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_existing_version.py b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_existing_version.py index ddfe8904fa..3a62536507 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_existing_version.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_existing_version.py @@ -40,7 +40,7 @@ class ValidateExistingVersion( formatting_data = { "product_name": product_name, - "asset_name": instance.data["folderPath"], + "folder_path": instance.data["folderPath"], "version": version } raise PublishXmlValidationError( diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_frame_ranges.py b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_frame_ranges.py index cd4a98b84d..e5bf034d00 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_frame_ranges.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_frame_ranges.py @@ -31,9 +31,9 @@ class ValidateFrameRange(OptionalPyblishPluginMixin, return # editorial would fail since they might not be in database yet - new_asset_publishing = instance.data.get("newAssetPublishing") - if new_asset_publishing: - self.log.debug("Instance is creating new asset. Skipping.") + new_folder_publishing = instance.data.get("newAssetPublishing") + if new_folder_publishing: + self.log.debug("Instance is creating new folder. Skipping.") return if (self.skip_timelines_check and @@ -41,12 +41,11 @@ class ValidateFrameRange(OptionalPyblishPluginMixin, for pattern in self.skip_timelines_check)): self.log.info("Skipping for {} task".format(instance.data["task"])) - asset_doc = instance.data["assetEntity"] - asset_data = asset_doc["data"] - frame_start = asset_data["frameStart"] - frame_end = asset_data["frameEnd"] - handle_start = asset_data["handleStart"] - handle_end = asset_data["handleEnd"] + folder_attributes = instance.data["folderEntity"]["attrib"] + frame_start = folder_attributes["frameStart"] + frame_end = folder_attributes["frameEnd"] + handle_start = folder_attributes["handleStart"] + handle_end = folder_attributes["handleEnd"] duration = (frame_end - frame_start + 1) + handle_start + handle_end repres = instance.data.get("representations") @@ -68,7 +67,7 @@ class ValidateFrameRange(OptionalPyblishPluginMixin, msg = ( "Frame duration from DB:'{}' doesn't match number of files:'{}'" - " Please change frame range for Asset or limit no. of files" + " Please change frame range for Folder or limit no. of files" ). format(int(duration), frames) formatting_data = {"duration": duration, diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_online_file.py b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_online_file.py index 3bd55342af..4eb0503006 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_online_file.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_online_file.py @@ -23,9 +23,9 @@ class ValidateOnlineFile(OptionalPyblishPluginMixin, if not self.is_active(instance.data): return project_name = instance.context.data["projectName"] - asset_id = instance.data["assetEntity"]["_id"] + folder_id = instance.data["folderEntity"]["id"] subset_doc = get_subset_by_name( - project_name, instance.data["productName"], asset_id) + project_name, instance.data["productName"], folder_id) if subset_doc: raise PublishValidationError( From 69819879a5dd480d5d8eb2cc51cd1a17810d9965 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 16:41:46 +0100 Subject: [PATCH 419/573] unreal use folder and task entity --- client/ayon_core/hosts/unreal/api/pipeline.py | 30 ++++----- .../plugins/load/load_alembic_animation.py | 16 +++-- .../unreal/plugins/load/load_animation.py | 10 +-- .../hosts/unreal/plugins/load/load_camera.py | 62 ++++++++++++------- .../plugins/load/load_geometrycache_abc.py | 3 +- .../hosts/unreal/plugins/load/load_layout.py | 60 +++++++++++------- .../plugins/load/load_layout_existing.py | 59 ++++++++++++------ .../plugins/load/load_skeletalmesh_abc.py | 3 +- .../plugins/load/load_skeletalmesh_fbx.py | 3 +- .../plugins/load/load_staticmesh_abc.py | 3 +- .../plugins/load/load_staticmesh_fbx.py | 3 +- .../publish/validate_sequence_frames.py | 10 ++- 12 files changed, 159 insertions(+), 103 deletions(-) diff --git a/client/ayon_core/hosts/unreal/api/pipeline.py b/client/ayon_core/hosts/unreal/api/pipeline.py index 922fc8abd8..eb093a2e6f 100644 --- a/client/ayon_core/hosts/unreal/api/pipeline.py +++ b/client/ayon_core/hosts/unreal/api/pipeline.py @@ -4,12 +4,12 @@ import json import logging from typing import List from contextlib import contextmanager -import semver import time +import semver import pyblish.api +import ayon_api -from ayon_core.client import get_asset_by_name, get_assets from ayon_core.pipeline import ( register_loader_plugin_path, register_creator_plugin_path, @@ -601,34 +601,36 @@ def generate_sequence(h, h_dir): ) project_name = get_current_project_name() - asset_data = get_asset_by_name( + # TODO Fix this does not return folder path + folder_path = h_dir.split('/')[-1], + folder_entity = ayon_api.get_folder_by_path( project_name, - h_dir.split('/')[-1], - fields=["_id", "data.fps"] + folder_path, + fields={"id", "attrib.fps"} ) start_frames = [] end_frames = [] - elements = list(get_assets( + elements = list(ayon_api.get_folders( project_name, - parent_ids=[asset_data["_id"]], - fields=["_id", "data.clipIn", "data.clipOut"] + parent_ids=[folder_entity["id"]], + fields={"id", "attrib.clipIn", "attrib.clipOut"} )) for e in elements: - start_frames.append(e.get('data').get('clipIn')) - end_frames.append(e.get('data').get('clipOut')) + start_frames.append(e["attrib"].get("clipIn")) + end_frames.append(e["attrib"].get("clipOut")) - elements.extend(get_assets( + elements.extend(ayon_api.get_folders( project_name, - parent_ids=[e["_id"]], - fields=["_id", "data.clipIn", "data.clipOut"] + parent_ids=[e["id"]], + fields={"id", "attrib.clipIn", "attrib.clipOut"} )) min_frame = min(start_frames) max_frame = max(end_frames) - fps = asset_data.get('data').get("fps") + fps = folder_entity["attrib"].get("fps") sequence.set_display_rate( unreal.FrameRate(fps, 1.0)) diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_alembic_animation.py b/client/ayon_core/hosts/unreal/plugins/load/load_alembic_animation.py index a4fcda6ade..d75cf207b5 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_alembic_animation.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_alembic_animation.py @@ -70,10 +70,12 @@ class AnimationAlembicLoader(plugin.Loader): # Create directory for asset and ayon container root = unreal_pipeline.AYON_ASSET_DIR - asset = context.get('asset').get('name') + folder_name = context["folder"]["name"] + folder_path = context["folder"]["path"] + product_type = context["representation"]["context"]["family"] suffix = "_CON" - if asset: - asset_name = "{}_{}".format(asset, name) + if folder_name: + asset_name = "{}_{}".format(folder_name, name) else: asset_name = "{}".format(name) version = context.get('version') @@ -85,7 +87,7 @@ class AnimationAlembicLoader(plugin.Loader): tools = unreal.AssetToolsHelpers().get_asset_tools() asset_dir, container_name = tools.create_unique_asset_name( - f"{root}/{asset}/{name_version}", suffix="") + f"{root}/{folder_name}/{name_version}", suffix="") container_name += suffix @@ -105,14 +107,16 @@ class AnimationAlembicLoader(plugin.Loader): data = { "schema": "ayon:container-2.0", "id": AYON_CONTAINER_ID, - "asset": asset, + "asset": folder_path, + "folder_path": folder_path, "namespace": asset_dir, "container_name": container_name, "asset_name": asset_name, "loader": str(self.__class__.__name__), "representation": context["representation"]["_id"], "parent": context["representation"]["parent"], - "family": context["representation"]["context"]["family"] + "family": product_type, + "product_type": product_type, } unreal_pipeline.imprint( f"{asset_dir}/{container_name}", data) diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_animation.py b/client/ayon_core/hosts/unreal/plugins/load/load_animation.py index 3aad6886be..1832841ef0 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_animation.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_animation.py @@ -8,7 +8,7 @@ from unreal import EditorAssetLibrary from unreal import MovieSceneSkeletalAnimationTrack from unreal import MovieSceneSkeletalAnimationSection -from ayon_core.pipeline.context_tools import get_current_project_asset +from ayon_core.pipeline.context_tools import get_current_project_folder from ayon_core.pipeline import ( get_representation_path, AYON_CONTAINER_ID @@ -53,7 +53,7 @@ class AnimationFBXLoader(plugin.Loader): if not actor: return None - asset_doc = get_current_project_asset(fields=["data.fps"]) + folder_entity = get_current_project_folder(fields=["attrib.fps"]) task.set_editor_property('filename', path) task.set_editor_property('destination_path', asset_dir) @@ -82,7 +82,7 @@ class AnimationFBXLoader(plugin.Loader): task.options.anim_sequence_import_data.set_editor_property( 'use_default_sample_rate', False) task.options.anim_sequence_import_data.set_editor_property( - 'custom_sample_rate', asset_doc.get("data", {}).get("fps")) + 'custom_sample_rate', folder_entity.get("attrib", {}).get("fps")) task.options.anim_sequence_import_data.set_editor_property( 'import_custom_attribute', True) task.options.anim_sequence_import_data.set_editor_property( @@ -250,7 +250,7 @@ class AnimationFBXLoader(plugin.Loader): repre_doc = context["representation"] folder_name = container["asset_name"] source_path = get_representation_path(repre_doc) - asset_doc = get_current_project_asset(fields=["data.fps"]) + folder_entity = get_current_project_folder(fields=["attrib.fps"]) destination_path = container["namespace"] task = unreal.AssetImportTask() @@ -284,7 +284,7 @@ class AnimationFBXLoader(plugin.Loader): task.options.anim_sequence_import_data.set_editor_property( 'use_default_sample_rate', False) task.options.anim_sequence_import_data.set_editor_property( - 'custom_sample_rate', asset_doc.get("data", {}).get("fps")) + 'custom_sample_rate', folder_entity.get("attrib", {}).get("fps")) task.options.anim_sequence_import_data.set_editor_property( 'import_custom_attribute', True) task.options.anim_sequence_import_data.set_editor_property( diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_camera.py b/client/ayon_core/hosts/unreal/plugins/load/load_camera.py index 34c1e3e023..26ec80c804 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_camera.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_camera.py @@ -2,6 +2,8 @@ """Load camera from FBX.""" from pathlib import Path +import ayon_api + import unreal from unreal import ( EditorAssetLibrary, @@ -9,7 +11,6 @@ from unreal import ( EditorLevelUtils, LevelSequenceEditorBlueprintLibrary as LevelSequenceLib, ) -from ayon_core.client import get_asset_by_name from ayon_core.pipeline import ( AYON_CONTAINER_ID, get_current_project_name, @@ -83,24 +84,33 @@ class CameraLoader(plugin.Loader): """ # Create directory for asset and Ayon container - hierarchy = context.get('asset').get('data').get('parents') + folder_entity = context["folder"] + folder_attributes = folder_entity["attrib"] + folder_path = folder_entity["path"] + hierarchy_parts = folder_path.split("/") + # Remove empty string + hierarchy_parts.pop(0) + # Pop folder name + folder_name = hierarchy_parts.pop(-1) + root = "/Game/Ayon" hierarchy_dir = root hierarchy_dir_list = [] - for h in hierarchy: + for h in hierarchy_parts: hierarchy_dir = f"{hierarchy_dir}/{h}" hierarchy_dir_list.append(hierarchy_dir) - asset = context.get('asset').get('name') suffix = "_CON" - asset_name = f"{asset}_{name}" if asset else f"{name}" + asset_name = f"{folder_name}_{name}" if folder_name else name tools = unreal.AssetToolsHelpers().get_asset_tools() # Create a unique name for the camera directory unique_number = 1 - if EditorAssetLibrary.does_directory_exist(f"{hierarchy_dir}/{asset}"): + if EditorAssetLibrary.does_directory_exist( + f"{hierarchy_dir}/{folder_name}" + ): asset_content = EditorAssetLibrary.list_assets( - f"{root}/{asset}", recursive=False, include_folder=True + f"{root}/{folder_name}", recursive=False, include_folder=True ) # Get highest number to make a unique name @@ -113,7 +123,7 @@ class CameraLoader(plugin.Loader): unique_number = f_numbers[-1] + 1 if f_numbers else 1 asset_dir, container_name = tools.create_unique_asset_name( - f"{hierarchy_dir}/{asset}/{name}_{unique_number:02d}", suffix="") + f"{hierarchy_dir}/{folder_name}/{name}_{unique_number:02d}", suffix="") container_name += suffix @@ -122,14 +132,18 @@ class CameraLoader(plugin.Loader): # Create map for the shot, and create hierarchy of map. If the maps # already exist, we will use them. h_dir = hierarchy_dir_list[0] - h_asset = hierarchy[0] + h_asset = hierarchy_dir[0] master_level = f"{h_dir}/{h_asset}_map.{h_asset}_map" if not EditorAssetLibrary.does_asset_exist(master_level): EditorLevelLibrary.new_level(f"{h_dir}/{h_asset}_map") - level = f"{asset_dir}/{asset}_map_camera.{asset}_map_camera" + level = ( + f"{asset_dir}/{folder_name}_map_camera.{folder_name}_map_camera" + ) if not EditorAssetLibrary.does_asset_exist(level): - EditorLevelLibrary.new_level(f"{asset_dir}/{asset}_map_camera") + EditorLevelLibrary.new_level( + f"{asset_dir}/{folder_name}_map_camera" + ) EditorLevelLibrary.load_level(master_level) EditorLevelUtils.add_level_to_world( @@ -144,7 +158,7 @@ class CameraLoader(plugin.Loader): # they don't exist. frame_ranges = [] sequences = [] - for (h_dir, h) in zip(hierarchy_dir_list, hierarchy): + for (h_dir, h) in zip(hierarchy_dir_list, hierarchy_parts): root_content = EditorAssetLibrary.list_assets( h_dir, recursive=False, include_folder=False) @@ -170,7 +184,7 @@ class CameraLoader(plugin.Loader): EditorAssetLibrary.make_directory(asset_dir) cam_seq = tools.create_asset( - asset_name=f"{asset}_camera", + asset_name=f"{folder_name}_camera", package_path=asset_dir, asset_class=unreal.LevelSequence, factory=unreal.LevelSequenceFactoryNew() @@ -184,16 +198,17 @@ class CameraLoader(plugin.Loader): frame_ranges[i + 1][0], frame_ranges[i + 1][1], [level]) - project_name = get_current_project_name() - data = get_asset_by_name(project_name, asset)["data"] + clip_in = folder_attributes.get("clipIn") + clip_out = folder_attributes.get("clipOut") + cam_seq.set_display_rate( - unreal.FrameRate(data.get("fps"), 1.0)) - cam_seq.set_playback_start(data.get('clipIn')) - cam_seq.set_playback_end(data.get('clipOut') + 1) + unreal.FrameRate(folder_attributes.get("fps"), 1.0)) + cam_seq.set_playback_start(clip_in) + cam_seq.set_playback_end(clip_out + 1) set_sequence_hierarchy( sequences[-1], cam_seq, frame_ranges[-1][1], - data.get('clipIn'), data.get('clipOut'), + clip_in, clip_out, [level]) settings = unreal.MovieSceneUserImportFBXSettings() @@ -215,9 +230,7 @@ class CameraLoader(plugin.Loader): for possessable in cam_seq.get_possessables(): for tracks in possessable.get_tracks(): for section in tracks.get_sections(): - section.set_range( - data.get('clipIn'), - data.get('clipOut') + 1) + section.set_range(clip_in, clip_out + 1) for channel in section.get_all_channels(): for key in channel.get_keys(): old_time = key.get_time().get_editor_property( @@ -225,7 +238,7 @@ class CameraLoader(plugin.Loader): old_time_value = old_time.get_editor_property( 'value') new_time = old_time_value + ( - data.get('clipIn') - data.get('frameStart') + clip_in - folder_attributes.get('frameStart') ) key.set_time(unreal.FrameNumber(value=new_time)) @@ -236,7 +249,8 @@ class CameraLoader(plugin.Loader): data = { "schema": "ayon:container-2.0", "id": AYON_CONTAINER_ID, - "asset": asset, + "asset": folder_name, + "folder_path": folder_path, "namespace": asset_dir, "container_name": container_name, "asset_name": asset_name, diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py b/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py index bca99f202f..21ec5df1ae 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py @@ -164,13 +164,12 @@ class PointCacheAlembicLoader(plugin.Loader): return asset_content def update(self, container, context): - asset_doc = context["asset"] + folder_name = context["asset"]["name"] subset_doc = context["subset"] version_doc = context["version"] repre_doc = context["representation"] # Create directory for asset and Ayon container - folder_name = asset_doc["name"] product_name = subset_doc["name"] suffix = "_CON" diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_layout.py b/client/ayon_core/hosts/unreal/plugins/load/load_layout.py index f78dba9e57..47e99b9534 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_layout.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_layout.py @@ -15,8 +15,9 @@ from unreal import ( MovieSceneSubTrack, LevelSequenceEditorBlueprintLibrary as LevelSequenceLib, ) +import ayon_api -from ayon_core.client import get_asset_by_name, get_representations +from ayon_core.client import get_representations from ayon_core.pipeline import ( discover_loader_plugins, loaders_from_representation, @@ -25,7 +26,7 @@ from ayon_core.pipeline import ( AYON_CONTAINER_ID, get_current_project_name, ) -from ayon_core.pipeline.context_tools import get_current_project_asset +from ayon_core.pipeline.context_tools import get_current_project_folder from ayon_core.settings import get_current_project_settings from ayon_core.hosts.unreal.api import plugin from ayon_core.hosts.unreal.api.pipeline import ( @@ -169,7 +170,7 @@ class LayoutLoader(plugin.Loader): anim_path = f"{asset_dir}/animations/{anim_file_name}" - asset_doc = get_current_project_asset() + folder_entity = get_current_project_folder() # Import animation task = unreal.AssetImportTask() task.options = unreal.FbxImportUI() @@ -204,7 +205,7 @@ class LayoutLoader(plugin.Loader): task.options.anim_sequence_import_data.set_editor_property( 'use_default_sample_rate', False) task.options.anim_sequence_import_data.set_editor_property( - 'custom_sample_rate', asset_doc.get("data", {}).get("fps")) + 'custom_sample_rate', folder_entity.get("attrib", {}).get("fps")) task.options.anim_sequence_import_data.set_editor_property( 'import_custom_attribute', True) task.options.anim_sequence_import_data.set_editor_property( @@ -518,20 +519,25 @@ class LayoutLoader(plugin.Loader): create_sequences = data["unreal"]["level_sequences_for_layouts"] # Create directory for asset and Ayon container - hierarchy = context.get('asset').get('data').get('parents') + folder_entity = context["folder"] + folder_path = folder_entity["path"] + hierarchy = folder_path.lstrip("/").split("/") + # Remove folder name + folder_name = hierarchy.pop(-1) root = self.ASSET_ROOT hierarchy_dir = root hierarchy_dir_list = [] for h in hierarchy: hierarchy_dir = f"{hierarchy_dir}/{h}" hierarchy_dir_list.append(hierarchy_dir) - asset = context.get('asset').get('name') suffix = "_CON" - asset_name = f"{asset}_{name}" if asset else name + asset_name = f"{folder_name}_{name}" if folder_name else name tools = unreal.AssetToolsHelpers().get_asset_tools() asset_dir, container_name = tools.create_unique_asset_name( - "{}/{}/{}".format(hierarchy_dir, asset, name), suffix="") + "{}/{}/{}".format(hierarchy_dir, folder_name, name), + suffix="" + ) container_name += suffix @@ -541,8 +547,8 @@ class LayoutLoader(plugin.Loader): shot = None sequences = [] - level = f"{asset_dir}/{asset}_map.{asset}_map" - EditorLevelLibrary.new_level(f"{asset_dir}/{asset}_map") + level = f"{asset_dir}/{folder_name}_map.{folder_name}_map" + EditorLevelLibrary.new_level(f"{asset_dir}/{folder_name}_map") if create_sequences: # Create map for the shot, and create hierarchy of map. If the @@ -591,7 +597,7 @@ class LayoutLoader(plugin.Loader): e.get_asset().get_playback_end())) shot = tools.create_asset( - asset_name=asset, + asset_name=folder_name, package_path=asset_dir, asset_class=unreal.LevelSequence, factory=unreal.LevelSequenceFactoryNew() @@ -606,16 +612,24 @@ class LayoutLoader(plugin.Loader): [level]) project_name = get_current_project_name() - data = get_asset_by_name(project_name, asset)["data"] + folder_attributes = ( + ayon_api.get_folder_by_path(project_name, folder_path)["attrib"] + ) shot.set_display_rate( - unreal.FrameRate(data.get("fps"), 1.0)) + unreal.FrameRate(folder_attributes.get("fps"), 1.0)) shot.set_playback_start(0) - shot.set_playback_end(data.get('clipOut') - data.get('clipIn') + 1) + shot.set_playback_end( + folder_attributes.get('clipOut') + - folder_attributes.get('clipIn') + + 1 + ) if sequences: set_sequence_hierarchy( - sequences[-1], shot, + sequences[-1], + shot, frame_ranges[-1][1], - data.get('clipIn'), data.get('clipOut'), + folder_attributes.get('clipIn'), + folder_attributes.get('clipOut'), [level]) EditorLevelLibrary.load_level(level) @@ -635,7 +649,8 @@ class LayoutLoader(plugin.Loader): data = { "schema": "ayon:container-2.0", "id": AYON_CONTAINER_ID, - "asset": asset, + "asset": folder_name, + "folder_path": folder_path, "namespace": asset_dir, "container_name": container_name, "asset_name": asset_name, @@ -678,17 +693,18 @@ class LayoutLoader(plugin.Loader): asset_dir = container.get('namespace') - asset_doc = context["asset"] + folder_entity = context["folder"] repre_doc = context["representation"] - hierarchy = list(asset_doc["data"]["parents"]) + hierarchy = folder_entity["path"].lstrip("/").split("/") + first_parent_name = hierarchy[0] sequence = None master_level = None if create_sequences: - h_dir = f"{root}/{hierarchy[0]}" - h_asset = hierarchy[0] + h_dir = f"{root}/{first_parent_name}" + h_asset = first_parent_name master_level = f"{h_dir}/{h_asset}_map.{h_asset}_map" filter = unreal.ARFilter( @@ -744,7 +760,7 @@ class LayoutLoader(plugin.Loader): EditorLevelLibrary.save_current_level() - save_dir = f"{root}/{hierarchy[0]}" if create_sequences else asset_dir + save_dir = f"{root}/{first_parent_name}" if create_sequences else asset_dir asset_content = EditorAssetLibrary.list_assets( save_dir, recursive=True, include_folder=False) diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py b/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py index cf987765f4..c896e7688b 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py @@ -3,6 +3,7 @@ from pathlib import Path import unreal from unreal import EditorLevelLibrary +import ayon_api from ayon_core.client import get_representations from ayon_core.pipeline import ( @@ -11,7 +12,6 @@ from ayon_core.pipeline import ( load_container, get_representation_path, AYON_CONTAINER_ID, - get_current_project_name, ) from ayon_core.hosts.unreal.api import plugin from ayon_core.hosts.unreal.api import pipeline as upipeline @@ -43,11 +43,15 @@ class ExistingLayoutLoader(plugin.Loader): @staticmethod def _create_container( - asset_name, asset_dir, asset, representation, parent, family + asset_name, + asset_dir, + folder_path, + representation, + version_id, + product_type ): container_name = f"{asset_name}_CON" - container = None if not unreal.EditorAssetLibrary.does_asset_exist( f"{asset_dir}/{container_name}" ): @@ -61,14 +65,16 @@ class ExistingLayoutLoader(plugin.Loader): data = { "schema": "ayon:container-2.0", "id": AYON_CONTAINER_ID, - "asset": asset, + "asset": folder_path, + "folder_path": folder_path, "namespace": asset_dir, "container_name": container_name, "asset_name": asset_name, # "loader": str(self.__class__.__name__), "representation": representation, - "parent": parent, - "family": family + "parent": version_id, + "family": product_type, + "product_type": product_type, } upipeline.imprint( @@ -248,17 +254,27 @@ class ExistingLayoutLoader(plugin.Loader): layout_data.append((repre_doc, element)) version_ids.add(repre_doc["parent"]) + repre_parents_by_id = ayon_api.get_representation_parents( + project_name, repre_docs_by_id.keys() + ) + # Prequery valid repre documents for all elements at once valid_repre_doc_by_version_id = self._get_valid_repre_docs( project_name, version_ids) containers = [] actors_matched = [] - for (repr_data, lasset) in layout_data: + for (repre_doc, lasset) in layout_data: # For every actor in the scene, check if it has a representation in # those we got from the JSON. If so, create a container for it. # Otherwise, remove it from the scene. found = False + repre_id = repre_doc["_id"] + repre_parents = repre_parents_by_id[repre_id] + folder_path = repre_parents.folder["path"] + folder_name = repre_parents.folder["name"] + product_name = repre_parents.product["name"] + product_type = repre_parents.product["productType"] for actor in actors: if not actor.get_class().get_name() == 'StaticMeshActor': @@ -275,7 +291,7 @@ class ExistingLayoutLoader(plugin.Loader): path = Path(filename) if (not path.name or - path.name not in repr_data.get('data').get('path')): + path.name not in repre_doc["data"]["path"]): continue actor.set_actor_label(lasset.get('instance_name')) @@ -283,12 +299,13 @@ class ExistingLayoutLoader(plugin.Loader): mesh_path = Path(mesh.get_path_name()).parent.as_posix() # Create the container for the asset. - asset = repr_data.get('context').get('asset') - product_name = repr_data.get('context').get('subset') container = self._create_container( - f"{asset}_{product_name}", mesh_path, asset, - repr_data.get('_id'), repr_data.get('parent'), - repr_data.get('context').get('family') + f"{folder_name}_{product_name}", + mesh_path, + folder_path, + repre_doc["_id"], + repre_doc["parent"], + product_type ) containers.append(container) @@ -318,7 +335,7 @@ class ExistingLayoutLoader(plugin.Loader): for container in all_containers: repr = container.get('representation') - if not repr == str(repr_data.get('_id')): + if not repr == repre_doc["_id"]: continue asset_dir = container.get('namespace') @@ -370,9 +387,11 @@ class ExistingLayoutLoader(plugin.Loader): def load(self, context, name, namespace, options): print("Loading Layout and Match Assets") - asset = context.get('asset').get('name') - asset_name = f"{asset}_{name}" if asset else name - container_name = f"{asset}_{name}_CON" + folder_name = context["folder"]["name"] + folder_path = context["folder"]["path"] + product_type = context["representation"]["context"]["family"] + asset_name = f"{folder_name}_{name}" if folder_name else name + container_name = f"{folder_name}_{name}_CON" curr_level = self._get_current_level() @@ -395,14 +414,16 @@ class ExistingLayoutLoader(plugin.Loader): data = { "schema": "ayon:container-2.0", "id": AYON_CONTAINER_ID, - "asset": asset, + "asset": folder_path, + "folder_path": folder_path, "namespace": curr_level_path, "container_name": container_name, "asset_name": asset_name, "loader": str(self.__class__.__name__), "representation": context["representation"]["_id"], "parent": context["representation"]["parent"], - "family": context["representation"]["context"]["family"], + "family": product_type, + "product_type": product_type, "loaded_assets": containers } upipeline.imprint(f"{curr_level_path}/{container_name}", data) diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py index 58fbda491c..d7060a4c1c 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py @@ -145,12 +145,11 @@ class SkeletalMeshAlembicLoader(plugin.Loader): return asset_content def update(self, container, context): - asset_doc = context["asset"] + folder_name = context["folder"]["name"] subset_doc = context["subset"] version_doc = context["version"] repre_doc = context["representation"] - folder_name = asset_doc["name"] product_name = subset_doc["name"] # Create directory for folder and Ayon container diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py index 436d4c8a58..282e9a7f17 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py @@ -147,12 +147,11 @@ class SkeletalMeshFBXLoader(plugin.Loader): return asset_content def update(self, container, context): - asset_doc = context["asse"] + folder_name = context["folder"]["name"] subset_doc = context["subset"] version_doc = context["version"] repre_doc = context["representation"] - folder_name = asset_doc["name"] product_name = subset_doc["name"] # Create directory for asset and Ayon container diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_abc.py b/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_abc.py index 6ff4bcd4f2..5bff74c1e9 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_abc.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_abc.py @@ -146,11 +146,10 @@ class StaticMeshAlembicLoader(plugin.Loader): return asset_content def update(self, container, context): - asset_doc = context["asset"] + folder_name = context["folder"]["name"] subset_doc = context["subset"] repre_doc = context["representation"] - folder_name = asset_doc["name"] product_name = subset_doc["name"] # Create directory for asset and Ayon container diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py b/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py index d2c6fc5566..4ad255a346 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py @@ -135,12 +135,11 @@ class StaticMeshFBXLoader(plugin.Loader): return asset_content def update(self, container, context): - asset_doc = context["asset"] + folder_name = context["folder"]["name"] subset_doc = context["subset"] version_doc = context["version"] repre_doc = context["representation"] - folder_name = asset_doc["name"] product_name = subset_doc["name"] # Create directory for asset and Ayon container diff --git a/client/ayon_core/hosts/unreal/plugins/publish/validate_sequence_frames.py b/client/ayon_core/hosts/unreal/plugins/publish/validate_sequence_frames.py index 205436ad37..85214a2b0d 100644 --- a/client/ayon_core/hosts/unreal/plugins/publish/validate_sequence_frames.py +++ b/client/ayon_core/hosts/unreal/plugins/publish/validate_sequence_frames.py @@ -22,8 +22,12 @@ class ValidateSequenceFrames(pyblish.api.InstancePlugin): def process(self, instance): representations = instance.data.get("representations") + folder_attributes = ( + instance.data + .get("folderEntity", {}) + .get("attrib", {}) + ) for repr in representations: - data = instance.data.get("assetEntity", {}).get("data", {}) repr_files = repr["files"] if isinstance(repr_files, str): continue @@ -64,8 +68,8 @@ class ValidateSequenceFrames(pyblish.api.InstancePlugin): frames = frames[1:] current_range = (frames[0], frames[-1]) - required_range = (data["clipIn"], - data["clipOut"]) + required_range = (folder_attributes["clipIn"], + folder_attributes["clipOut"]) if current_range != required_range: raise PublishValidationError( From 2072f2fbe2bdaa09c96dd3c849bff65785c3b3bb Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 16:44:44 +0100 Subject: [PATCH 420/573] global plugins are using folder entity and task --- .../plugins/actions/open_file_explorer.py | 14 +- .../plugins/load/delete_old_versions.py | 36 +-- .../publish/collect_anatomy_context_data.py | 15 +- .../publish/collect_anatomy_instance_data.py | 280 ++++++++++++------ .../plugins/publish/collect_audio.py | 51 ++-- .../publish/collect_context_entities.py | 76 +++-- .../publish/collect_current_context.py | 8 +- .../plugins/publish/collect_frames_fix.py | 4 +- .../publish/collect_from_create_context.py | 4 +- .../publish/extract_hierarchy_to_ayon.py | 75 +++-- client/ayon_core/plugins/publish/integrate.py | 6 +- .../plugins/publish/integrate_thumbnail.py | 4 +- .../plugins/publish/validate_asset_docs.py | 24 +- .../publish/validate_editorial_asset_name.py | 108 ++++--- .../publish/validate_unique_subsets.py | 33 ++- 15 files changed, 457 insertions(+), 281 deletions(-) diff --git a/client/ayon_core/plugins/actions/open_file_explorer.py b/client/ayon_core/plugins/actions/open_file_explorer.py index c5c34cc4f0..c221752f11 100644 --- a/client/ayon_core/plugins/actions/open_file_explorer.py +++ b/client/ayon_core/plugins/actions/open_file_explorer.py @@ -5,7 +5,6 @@ from string import Formatter import ayon_api -from ayon_core.client import get_asset_by_name from ayon_core.pipeline import ( Anatomy, LauncherAction, @@ -27,10 +26,10 @@ class OpenTaskPath(LauncherAction): from qtpy import QtCore, QtWidgets project_name = session["AYON_PROJECT_NAME"] - asset_name = session["AYON_FOLDER_PATH"] + folder_path = session["AYON_FOLDER_PATH"] task_name = session.get("AYON_TASK_NAME", None) - path = self._get_workdir(project_name, asset_name, task_name) + path = self._get_workdir(project_name, folder_path, task_name) if not path: return @@ -61,11 +60,14 @@ class OpenTaskPath(LauncherAction): path = path.split(field, 1)[0] return path - def _get_workdir(self, project_name, asset_name, task_name): + def _get_workdir(self, project_name, folder_path, task_name): project_entity = ayon_api.get_project(project_name) - asset_doc = get_asset_by_name(project_name, asset_name) + folder_entity = ayon_api.get_folder_by_path(project_name, folder_path) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) - data = get_template_data(project_entity, asset_doc, task_name) + data = get_template_data(project_entity, folder_entity, task_entity) anatomy = Anatomy(project_name) workdir = anatomy.templates_obj["work"]["folder"].format(data) diff --git a/client/ayon_core/plugins/load/delete_old_versions.py b/client/ayon_core/plugins/load/delete_old_versions.py index 4fc61ebb8b..e0670caee5 100644 --- a/client/ayon_core/plugins/load/delete_old_versions.py +++ b/client/ayon_core/plugins/load/delete_old_versions.py @@ -1,5 +1,5 @@ # TODO This plugin is not converted for AYON - +# # import collections # import os # import uuid @@ -196,12 +196,14 @@ # msgBox.exec_() # # def get_data(self, context, versions_count): -# subset = context["subset"] -# asset = context["asset"] +# subset_doc = context["subset"] +# folder_entity = context["folder"] # project_name = context["project"]["name"] # anatomy = Anatomy(project_name) # -# versions = list(get_versions(project_name, subset_ids=[subset["_id"]])) +# versions = list(get_versions( +# project_name, subset_ids=[subset_doc["_id"]] +# )) # # versions_by_parent = collections.defaultdict(list) # for ent in versions: @@ -238,8 +240,10 @@ # versions_to_pop.append(version) # # for version in versions_to_pop: -# msg = "Asset: \"{}\" | Subset: \"{}\" | Version: \"{}\"".format( -# asset["name"], subset["name"], version["name"] +# msg = "Folder: \"{}\" | Subset: \"{}\" | Version: \"{}\"".format( +# folder_entity["path"], +# subset_doc["name"], +# version["name"] # ) # self.log.debug(( # "Skipping version. Already tagged as `deleted`. < {} >" @@ -254,7 +258,7 @@ # # if not version_ids: # msg = "Skipping processing. Nothing to delete on {}/{}".format( -# asset["name"], subset["name"] +# folder_entity["path"], subset_doc["name"] # ) # self.log.info(msg) # print(msg) @@ -310,17 +314,15 @@ # "Folder does not exist. Deleting it's files skipped: {}" # ).format(paths_msg)) # -# data = { +# return { # "dir_paths": dir_paths, # "file_paths_by_dir": file_paths_by_dir, # "versions": versions, -# "asset": asset, -# "subset": subset, +# "folder": folder_entity, +# "subset": subset_doc, # "archive_subset": versions_count == 0 # } # -# return data -# # def main(self, project_name, data, remove_publish_folder): # # Size of files. # size = 0 @@ -382,12 +384,12 @@ # data (dict): Data sent to subset loader with full context. # """ # -# # First check for ftrack id on asset document +# # First check for ftrack id on folder entity # # - skip if ther is none -# asset_ftrack_id = data["asset"]["data"].get("ftrackId") -# if not asset_ftrack_id: +# ftrack_id = data["folder"]["attrib"].get("ftrackId") +# if not ftrack_id: # self.log.info(( -# "Asset does not have filled ftrack id. Skipped delete" +# "Folder does not have filled ftrack id. Skipped delete" # " of ftrack version." # )) # return @@ -413,7 +415,7 @@ # " and asset.name is \"{}\"" # " and version in ({})" # ).format( -# asset_ftrack_id, +# ftrack_id, # product_name, # ",".join(versions) # ) diff --git a/client/ayon_core/plugins/publish/collect_anatomy_context_data.py b/client/ayon_core/plugins/publish/collect_anatomy_context_data.py index b5bb579498..cccf392e40 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_context_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_context_data.py @@ -3,7 +3,8 @@ Requires: context -> anatomy context -> projectEntity - context -> assetEntity + context -> folderEntity + context -> taskEntity context -> task context -> username context -> datetimeData @@ -49,15 +50,15 @@ class CollectAnatomyContextData(pyblish.api.ContextPlugin): host_name = context.data["hostName"] project_settings = context.data["project_settings"] project_entity = context.data["projectEntity"] - asset_entity = context.data.get("assetEntity") - task_name = None - if asset_entity: - task_name = context.data["task"] + folder_entity = context.data.get("folderEntity") + task_entity = None + if folder_entity: + task_entity = context.data["taskEntity"] anatomy_data = get_template_data( project_entity, - asset_entity, - task_name, + folder_entity, + task_entity, host_name, project_settings ) diff --git a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py index 8ad8a013c2..1b49d8288d 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py @@ -1,13 +1,17 @@ """ Requires: - context -> anatomyData + context -> projectName context -> projectEntity - context -> assetEntity + context -> anatomyData instance -> folderPath instance -> productName instance -> productType Optional: + context -> folderEntity + context -> taskEntity + instance -> task + instance -> taskEntity instance -> version instance -> resolutionWidth instance -> resolutionHeight @@ -15,7 +19,8 @@ Optional: Provides: instance -> projectEntity - instance -> assetEntity + instance -> folderEntity + instance -> taskEntity instance -> anatomyData instance -> version instance -> latestVersion @@ -26,12 +31,11 @@ import json import collections import pyblish.api +import ayon_api from ayon_core.client import ( - get_assets, get_subsets, get_last_versions, - get_asset_name_identifier, ) from ayon_core.pipeline.version_start import get_versioning_start @@ -51,73 +55,173 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): self.log.debug("Collecting anatomy data for all instances.") project_name = context.data["projectName"] - self.fill_missing_asset_docs(context, project_name) + self.fill_missing_folder_entities(context, project_name) + self.fill_missing_task_entities(context, project_name) self.fill_latest_versions(context, project_name) self.fill_anatomy_data(context) self.log.debug("Anatomy Data collection finished.") - def fill_missing_asset_docs(self, context, project_name): - self.log.debug("Querying asset documents for instances.") + def fill_missing_folder_entities(self, context, project_name): + self.log.debug("Querying folder entities for instances.") - context_asset_doc = context.data.get("assetEntity") - context_asset_name = None - if context_asset_doc: - context_asset_name = get_asset_name_identifier(context_asset_doc) + context_folder_entity = context.data.get("folderEntity") + context_folder_path = None + if context_folder_entity: + context_folder_path = context_folder_entity["path"] - instances_with_missing_asset_doc = collections.defaultdict(list) + instances_missing_folder = collections.defaultdict(list) for instance in context: - instance_asset_doc = instance.data.get("assetEntity") - _asset_name = instance.data["folderPath"] + instance_folder_entity = instance.data.get("folderEntity") + _folder_path = instance.data["folderPath"] + _task_name = instance.data["task"] - # There is possibility that assetEntity on instance is already set - # which can happen in standalone publisher - if instance_asset_doc: - instance_asset_name = get_asset_name_identifier( - instance_asset_doc) - if instance_asset_name == _asset_name: + # There is possibility that folderEntity on instance is set + if instance_folder_entity: + instance_folder_path = instance_folder_entity["path"] + if instance_folder_path == _folder_path: continue - # Check if asset name is the same as what is in context - # - they may be different, e.g. in NukeStudio - if context_asset_name and context_asset_name == _asset_name: - instance.data["assetEntity"] = context_asset_doc + # Check if folder path is the same as what is in context + # - they may be different, e.g. during editorial publishing + if context_folder_path and context_folder_path == _folder_path: + instance.data["folderEntity"] = context_folder_entity else: - instances_with_missing_asset_doc[_asset_name].append(instance) + instances_missing_folder[_folder_path].append( + instance + ) - if not instances_with_missing_asset_doc: - self.log.debug("All instances already had right asset document.") + if not instances_missing_folder: + self.log.debug("All instances already had right folder entity.") return - asset_names = list(instances_with_missing_asset_doc.keys()) - self.log.debug("Querying asset documents with names: {}".format( - ", ".join(["\"{}\"".format(name) for name in asset_names]) + folder_paths = list(instances_missing_folder.keys()) + self.log.debug("Querying folder entities with paths: {}".format( + ", ".join(["\"{}\"".format(path) for path in folder_paths]) )) - asset_docs = get_assets(project_name, asset_names=asset_names) - asset_docs_by_name = { - get_asset_name_identifier(asset_doc): asset_doc - for asset_doc in asset_docs + folder_entities_by_path = { + folder_entity["path"]: folder_entity + for folder_entity in ayon_api.get_folders( + project_name, folder_paths=folder_paths + ) } - not_found_asset_names = [] - for asset_name, instances in instances_with_missing_asset_doc.items(): - asset_doc = asset_docs_by_name.get(asset_name) - if not asset_doc: - not_found_asset_names.append(asset_name) + not_found_folder_paths = [] + for folder_path, instances in instances_missing_folder.items(): + folder_entity = folder_entities_by_path.get(folder_path) + if not folder_entity: + not_found_folder_paths.append(folder_path) continue for _instance in instances: - _instance.data["assetEntity"] = asset_doc + _instance.data["folderEntity"] = folder_entity - if not_found_asset_names: - joined_asset_names = ", ".join( - ["\"{}\"".format(name) for name in not_found_asset_names] + if not_found_folder_paths: + joined_folder_paths = ", ".join( + ["\"{}\"".format(path) for path in not_found_folder_paths] ) self.log.warning(( - "Not found asset documents with names \"{}\"." - ).format(joined_asset_names)) + "Not found folder entities with paths \"{}\"." + ).format(joined_folder_paths)) + + def fill_missing_task_entities(self, context, project_name): + self.log.debug("Querying task entities for instances.") + + context_folder_entity = context.data.get("folderEntity") + context_folder_id = None + if context_folder_entity: + context_folder_id = context_folder_entity["id"] + context_task_entity = context.data.get("taskEntity") + context_task_name = None + if context_task_entity: + context_task_name = context_task_entity["path"] + + instances_missing_task = {} + folder_path_by_id = {} + for instance in context: + folder_entity = instance.data.get("folderEntity") + # Skip if instnace does not have filled folder entity + if not folder_entity: + continue + folder_id = folder_entity["id"] + folder_path_by_id[folder_id] = folder_entity["path"] + + task_entity = instance.data.get("taskEntity") + _task_name = instance.data["task"] + + # There is possibility that taskEntity on instance is set + if task_entity: + task_parent_id = task_entity["folderId"] + instance_task_name = task_entity["name"] + if ( + folder_id == task_parent_id + and instance_task_name == _task_name + ): + continue + + # Check if folder path is the same as what is in context + # - they may be different, e.g. in NukeStudio + if ( + context_folder_id == folder_id + and context_task_name == _task_name + ): + instance.data["taskEntity"] = context_task_entity + continue + + _by_folder_id = instances_missing_task.setdefault(folder_id, {}) + _by_task_name = _by_folder_id.setdefault(_task_name, []) + _by_task_name.append(instance) + + if not instances_missing_task: + self.log.debug("All instances already had right task entity.") + return + + self.log.debug("Querying task entities") + + all_folder_ids = set(instances_missing_task.keys()) + all_task_names = set() + for per_task in instances_missing_task.values(): + all_task_names |= set(per_task.keys()) + + task_entities = ayon_api.get_tasks( + project_name, + folder_ids=all_folder_ids, + task_names=all_task_names + ) + task_entity_by_ids = {} + for task_entity in task_entities: + folder_id = task_entity["folderId"] + task_name = task_entity["name"] + _by_folder_id = task_entity_by_ids.setdefault(folder_id, {}) + _by_folder_id[task_name] = task_entity + + not_found_task_paths = [] + for folder_id, by_task in instances_missing_task.items(): + for task_name, instances in by_task.items(): + task_entity = ( + task_entity_by_ids + .get(folder_id, {}) + .get(task_name) + ) + if not task_entity: + folder_path = folder_path_by_id[folder_id] + not_found_task_paths.append( + "/".join([folder_path, task_name]) + ) + continue + + for instance in instances: + instance.data["taskEntity"] = task_entity + + if not_found_task_paths: + joined_paths = ", ".join( + ["\"{}\"".format(path) for path in not_found_task_paths] + ) + self.log.warning(( + "Not found task entities with paths \"{}\"." + ).format(joined_paths)) def fill_latest_versions(self, context, project_name): """Try to find latest version for each instance's product name. @@ -140,13 +244,13 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): latest_version = instance.data.get("latestVersion") instance.data["latestVersion"] = latest_version - # Skip instances without "assetEntity" - asset_doc = instance.data.get("assetEntity") - if not asset_doc: + # Skip instances without "folderEntity" + folder_entity = instance.data.get("folderEntity") + if not folder_entity: continue # Store folder ids and product names for queries - folder_id = asset_doc["_id"] + folder_id = folder_entity["id"] product_name = instance.data["productName"] # Prepare instance hierarchy for faster filling latest versions @@ -205,7 +309,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): } }) - self._fill_asset_data(instance, project_entity, anatomy_data) + self._fill_folder_data(instance, project_entity, anatomy_data) self._fill_task_data(instance, task_types_by_name, anatomy_data) # Define version @@ -275,24 +379,28 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): json.dumps(anatomy_data, indent=4) )) - def _fill_asset_data(self, instance, project_entity, anatomy_data): - # QUESTION should we make sure that all asset data are poped if asset - # data cannot be found? + def _fill_folder_data(self, instance, project_entity, anatomy_data): + # QUESTION should we make sure that all folder data are poped if + # folder data cannot be found? # - 'asset', 'hierarchy', 'parent', 'folder' - asset_doc = instance.data.get("assetEntity") - if asset_doc: - parents = asset_doc["data"].get("parents") or list() + folder_entity = instance.data.get("folderEntity") + if folder_entity: + folder_name = folder_entity["name"] + folder_path = folder_entity["path"] + hierarchy_parts = folder_path.split("/") + hierarchy_parts.pop(0) + hierarchy_parts.pop(-1) parent_name = project_entity["name"] - if parents: - parent_name = parents[-1] + if hierarchy_parts: + parent_name = hierarchy_parts[-1] - hierarchy = "/".join(parents) + hierarchy = "/".join(hierarchy_parts) anatomy_data.update({ - "asset": asset_doc["name"], + "asset": folder_name, "hierarchy": hierarchy, "parent": parent_name, "folder": { - "name": asset_doc["name"], + "name": folder_name, }, }) return @@ -305,13 +413,13 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): if hierarchy: parent_name = hierarchy.split("/")[-1] - asset_name = instance.data["folderPath"].split("/")[-1] + folder_name = instance.data["folderPath"].split("/")[-1] anatomy_data.update({ - "asset": asset_name, + "asset": folder_name, "hierarchy": hierarchy, "parent": parent_name, "folder": { - "name": asset_name, + "name": folder_name, }, }) @@ -325,10 +433,10 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): if not task_name: return - # Find task data based on asset entity - asset_doc = instance.data.get("assetEntity") - task_data = self._get_task_data_from_asset( - asset_doc, task_name, task_types_by_name + # Find task data based on folder entity + task_entity = instance.data.get("taskEntity") + task_data = self._get_task_data_from_entity( + task_entity, task_types_by_name ) if task_data: # Fill task data @@ -344,20 +452,20 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): if not instance.data.get("newAssetPublishing"): return - # Try to find task data based on hierarchy context and asset name + # Try to find task data based on hierarchy context and folder path hierarchy_context = instance.context.data.get("hierarchyContext") - asset_name = instance.data.get("folderPath") - if not hierarchy_context or not asset_name: + folder_path = instance.data.get("folderPath") + if not hierarchy_context or not folder_path: return project_name = instance.context.data["projectName"] - if "/" not in asset_name: + if "/" not in folder_path: tasks_info = self._find_tasks_info_in_hierarchy( - hierarchy_context, asset_name + hierarchy_context, folder_path ) else: current_data = hierarchy_context.get(project_name, {}) - for key in asset_name.split("/"): + for key in folder_path.split("/"): if key: current_data = current_data.get("childs", {}).get(key, {}) tasks_info = current_data.get("tasks", {}) @@ -375,14 +483,13 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): "short": task_code } - def _get_task_data_from_asset( - self, asset_doc, task_name, task_types_by_name + def _get_task_data_from_entity( + self, task_entity, task_types_by_name ): """ Args: - asset_doc (Union[dict[str, Any], None]): Asset document. - task_name (Union[str, None]): Task name. + task_entity (Union[dict[str, Any], None]): Task entity. task_types_by_name (dict[str, dict[str, Any]]): Project task types. @@ -390,28 +497,27 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): Union[dict[str, str], None]: Task data or None if not found. """ - if not asset_doc or not task_name: + if not task_entity: return None - asset_tasks = asset_doc["data"]["tasks"] - task_type = asset_tasks.get(task_name, {}).get("type") + task_type = task_entity["taskType"] task_code = ( task_types_by_name .get(task_type, {}) .get("shortName") ) return { - "name": task_name, + "name": task_entity["name"], "type": task_type, "short": task_code } - def _find_tasks_info_in_hierarchy(self, hierarchy_context, asset_name): + def _find_tasks_info_in_hierarchy(self, hierarchy_context, folder_name): """Find tasks info for an asset in editorial hierarchy. Args: hierarchy_context (dict[str, Any]): Editorial hierarchy context. - asset_name (str): Asset name. + folder_name (str): Folder name. Returns: dict[str, dict[str, Any]]: Tasks info by name. @@ -421,8 +527,8 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): hierarchy_queue.append(copy.deepcopy(hierarchy_context)) while hierarchy_queue: item = hierarchy_queue.popleft() - if asset_name in item: - return item[asset_name].get("tasks") or {} + if folder_name in item: + return item[folder_name].get("tasks") or {} for subitem in item.values(): hierarchy_queue.extend(subitem.get("childs") or []) diff --git a/client/ayon_core/plugins/publish/collect_audio.py b/client/ayon_core/plugins/publish/collect_audio.py index 357dad76d4..c818ebd147 100644 --- a/client/ayon_core/plugins/publish/collect_audio.py +++ b/client/ayon_core/plugins/publish/collect_audio.py @@ -1,18 +1,18 @@ import collections + +import ayon_api import pyblish.api from ayon_core.client import ( - get_assets, get_subsets, get_last_versions, get_representations, - get_asset_name_identifier, ) from ayon_core.pipeline.load import get_representation_path_with_anatomy class CollectAudio(pyblish.api.ContextPlugin): - """Collect asset's last published audio. + """Collect folders's last published audio. The audio product name searched for is defined in: project settings > Collect Audio @@ -23,7 +23,7 @@ class CollectAudio(pyblish.api.ContextPlugin): converted to context plugin which requires only 4 queries top. """ - label = "Collect Asset Audio" + label = "Collect Folder Audio" order = pyblish.api.CollectorOrder + 0.1 families = ["review"] hosts = [ @@ -64,30 +64,30 @@ class CollectAudio(pyblish.api.ContextPlugin): return # Add audio to instance if exists. - instances_by_asset_name = collections.defaultdict(list) + instances_by_folder_path = collections.defaultdict(list) for instance in filtered_instances: - asset_name = instance.data["folderPath"] - instances_by_asset_name[asset_name].append(instance) + folder_path = instance.data["folderPath"] + instances_by_folder_path[folder_path].append(instance) - asset_names = set(instances_by_asset_name.keys()) + folder_paths = set(instances_by_folder_path.keys()) self.log.debug(( - "Searching for audio product '{product}' in assets {assets}" + "Searching for audio product '{product}' in folders {folders}" ).format( product=self.audio_product_name, - assets=", ".join([ - '"{}"'.format(asset_name) - for asset_name in asset_names + folders=", ".join([ + '"{}"'.format(folder_path) + for folder_path in folder_paths ]) )) # Query all required documents project_name = context.data["projectName"] anatomy = context.data["anatomy"] - repre_docs_by_asset_names = self.query_representations( - project_name, asset_names) + repre_docs_by_folder_paths = self.query_representations( + project_name, folder_paths) - for asset_name, instances in instances_by_asset_name.items(): - repre_docs = repre_docs_by_asset_names[asset_name] + for folder_path, instances in instances_by_folder_path.items(): + repre_docs = repre_docs_by_folder_paths[folder_path] if not repre_docs: continue @@ -103,7 +103,7 @@ class CollectAudio(pyblish.api.ContextPlugin): self.log.debug("Audio Data added to instance ...") def query_representations(self, project_name, folder_paths): - """Query representations related to audio products for passed assets. + """Query representations related to audio products for passed folders. Args: project_name (str): Project in which we're looking for all @@ -113,25 +113,26 @@ class CollectAudio(pyblish.api.ContextPlugin): Returns: collections.defaultdict[str, List[Dict[Str, Any]]]: Representations - related to audio products by asset name. + related to audio products by folder path. """ output = collections.defaultdict(list) - # Query asset documents - asset_docs = get_assets( + # Query folder entities + folder_entities = ayon_api.get_folders( project_name, - asset_names=folder_paths, - fields=["_id", "name", "data.parents"] + folder_paths=folder_paths, + fields={"id", "path"} ) folder_id_by_path = { - get_asset_name_identifier(asset_doc): asset_doc["_id"] - for asset_doc in asset_docs + folder_entity["path"]: folder_entity["id"] + for folder_entity in folder_entities } folder_ids = set(folder_id_by_path.values()) # Query products with name define by 'audio_product_name' attr - # - one or none products with the name should be available on an asset + # - one or none products with the name should be available on + # an folder subset_docs = get_subsets( project_name, subset_names=[self.audio_product_name], diff --git a/client/ayon_core/plugins/publish/collect_context_entities.py b/client/ayon_core/plugins/publish/collect_context_entities.py index b5646576d6..f340178e4f 100644 --- a/client/ayon_core/plugins/publish/collect_context_entities.py +++ b/client/ayon_core/plugins/publish/collect_context_entities.py @@ -2,19 +2,20 @@ Requires: context -> projectName - context -> asset + context -> folderPath context -> task Provides: - context -> projectEntity - Project document from database. - context -> assetEntity - Asset document from database only if 'asset' is - set in context. + context -> projectEntity - Project entity from AYON server. + context -> folderEntity - Folder entity from AYON server only if + 'folderPath' is set in context data. + context -> taskEntity - Task entity from AYON server only if 'folderPath' + and 'task' are set in context data. """ import pyblish.api import ayon_api -from ayon_core.client import get_asset_by_name from ayon_core.pipeline import KnownPublishError @@ -26,45 +27,48 @@ class CollectContextEntities(pyblish.api.ContextPlugin): def process(self, context): project_name = context.data["projectName"] - asset_name = context.data["folderPath"] + folder_path = context.data["folderPath"] task_name = context.data["task"] project_entity = ayon_api.get_project(project_name) if not project_entity: raise KnownPublishError( - "Project '{0}' was not found.".format(project_name) + "Project '{}' was not found.".format(project_name) ) self.log.debug("Collected Project \"{}\"".format(project_entity)) context.data["projectEntity"] = project_entity - if not asset_name: + if not folder_path: self.log.info("Context is not set. Can't collect global data.") return - asset_entity = get_asset_by_name(project_name, asset_name) - assert asset_entity, ( - "No asset found by the name '{0}' in project '{1}'" - ).format(asset_name, project_name) + folder_entity = self._get_folder_entity(project_name, folder_path) + self.log.debug("Collected Folder \"{}\"".format(folder_entity)) - self.log.debug("Collected Asset \"{}\"".format(asset_entity)) + task_entity = self._get_task_entity( + project_name, folder_entity, task_name + ) + self.log.debug("Collected Task \"{}\"".format(task_entity)) - context.data["assetEntity"] = asset_entity + context.data["folderEntity"] = folder_entity + context.data["taskEntity"] = task_entity - data = asset_entity['data'] + folder_attributes = folder_entity["attrib"] # Task type - asset_tasks = data.get("tasks") or {} - task_info = asset_tasks.get(task_name) or {} - task_type = task_info.get("type") + task_type = None + if task_entity: + task_type = task_entity["taskType"] + context.data["taskType"] = task_type - frame_start = data.get("frameStart") + frame_start = folder_attributes.get("frameStart") if frame_start is None: frame_start = 1 self.log.warning("Missing frame start. Defaulting to 1.") - frame_end = data.get("frameEnd") + frame_end = folder_attributes.get("frameEnd") if frame_end is None: frame_end = 2 self.log.warning("Missing frame end. Defaulting to 2.") @@ -72,8 +76,8 @@ class CollectContextEntities(pyblish.api.ContextPlugin): context.data["frameStart"] = frame_start context.data["frameEnd"] = frame_end - handle_start = data.get("handleStart") or 0 - handle_end = data.get("handleEnd") or 0 + handle_start = folder_attributes.get("handleStart") or 0 + handle_end = folder_attributes.get("handleEnd") or 0 context.data["handleStart"] = int(handle_start) context.data["handleEnd"] = int(handle_end) @@ -83,4 +87,30 @@ class CollectContextEntities(pyblish.api.ContextPlugin): context.data["frameStartHandle"] = frame_start_h context.data["frameEndHandle"] = frame_end_h - context.data["fps"] = data["fps"] + context.data["fps"] = folder_attributes["fps"] + + def _get_folder_entity(self, project_name, folder_path): + if not folder_path: + return None + folder_entity = ayon_api.get_folder_by_path(project_name, folder_path) + if not folder_entity: + raise KnownPublishError( + "Folder '{}' was not found in project '{}'.".format( + folder_path, project_name + ) + ) + return folder_entity + + def _get_task_entity(self, project_name, folder_entity, task_name): + if not folder_entity or not task_name: + return None + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) + if not task_entity: + task_path = "/".join([folder_entity["path"], task_name]) + raise KnownPublishError( + "Task '{}' was not found in project '{}'.".format( + task_path, project_name) + ) + return task_entity \ No newline at end of file diff --git a/client/ayon_core/plugins/publish/collect_current_context.py b/client/ayon_core/plugins/publish/collect_current_context.py index 76d30a913e..76181ffc39 100644 --- a/client/ayon_core/plugins/publish/collect_current_context.py +++ b/client/ayon_core/plugins/publish/collect_current_context.py @@ -21,14 +21,14 @@ class CollectCurrentContext(pyblish.api.ContextPlugin): def process(self, context): # Check if values are already set project_name = context.data.get("projectName") - asset_name = context.data.get("folderPath") + folder_path = context.data.get("folderPath") task_name = context.data.get("task") current_context = get_current_context() if not project_name: context.data["projectName"] = current_context["project_name"] - if not asset_name: + if not folder_path: context.data["folderPath"] = current_context["folder_path"] if not task_name: @@ -40,10 +40,10 @@ class CollectCurrentContext(pyblish.api.ContextPlugin): self.log.info(( "Collected project context\n" "Project: {project_name}\n" - "Asset: {asset_name}\n" + "Folder: {folder_path}\n" "Task: {task_name}" ).format( project_name=context.data["projectName"], - asset_name=context.data["folderPath"], + folder_path=context.data["folderPath"], task_name=context.data["task"] )) diff --git a/client/ayon_core/plugins/publish/collect_frames_fix.py b/client/ayon_core/plugins/publish/collect_frames_fix.py index 0fe86b8d70..bd2379984c 100644 --- a/client/ayon_core/plugins/publish/collect_frames_fix.py +++ b/client/ayon_core/plugins/publish/collect_frames_fix.py @@ -41,7 +41,7 @@ class CollectFramesFixDef( instance.data["frames_to_fix"] = frames_to_fix product_name = instance.data["productName"] - asset_name = instance.data["folderPath"] + folder_path = instance.data["folderPath"] project_entity = instance.data["projectEntity"] project_name = project_entity["name"] @@ -49,7 +49,7 @@ class CollectFramesFixDef( version = get_last_version_by_subset_name( project_name, product_name, - asset_name=asset_name + asset_name=folder_path ) if not version: self.log.warning( diff --git a/client/ayon_core/plugins/publish/collect_from_create_context.py b/client/ayon_core/plugins/publish/collect_from_create_context.py index 36d44def41..b99866fed9 100644 --- a/client/ayon_core/plugins/publish/collect_from_create_context.py +++ b/client/ayon_core/plugins/publish/collect_from_create_context.py @@ -53,11 +53,11 @@ class CollectFromCreateContext(pyblish.api.ContextPlugin): context.data.update(create_context.context_data_to_store()) context.data["newPublishing"] = True # Update context data - asset_name = create_context.get_current_folder_path() + folder_path = create_context.get_current_folder_path() task_name = create_context.get_current_task_name() for key, value in ( ("AYON_PROJECT_NAME", project_name), - ("AYON_FOLDER_PATH", asset_name), + ("AYON_FOLDER_PATH", folder_path), ("AYON_TASK_NAME", task_name) ): if value is None: diff --git a/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py b/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py index ef8a8b8dfc..59a15af299 100644 --- a/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py +++ b/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py @@ -4,12 +4,11 @@ import json import uuid import pyblish.api -from ayon_api import slugify_string +from ayon_api import slugify_string, get_folders, get_tasks from ayon_api.entity_hub import EntityHub -from ayon_core.client import get_assets, get_asset_name_identifier from ayon_core.pipeline.template_data import ( - get_asset_template_data, + get_folder_template_data, get_task_template_data, ) @@ -35,38 +34,74 @@ class ExtractHierarchyToAYON(pyblish.api.ContextPlugin): self._fill_instance_entities(context, project_name) def _fill_instance_entities(self, context, project_name): - instances_by_asset_name = collections.defaultdict(list) + instances_by_folder_path = collections.defaultdict(list) for instance in context: if instance.data.get("publish") is False: continue - instance_entity = instance.data.get("assetEntity") + instance_entity = instance.data.get("folderEntity") if instance_entity: continue - # Skip if instance asset does not match - instance_asset_name = instance.data.get("folderPath") - instances_by_asset_name[instance_asset_name].append(instance) + folder_path = instance.data.get("folderPath") + instances_by_folder_path[folder_path].append(instance) project_entity = context.data["projectEntity"] - asset_docs = get_assets( - project_name, asset_names=instances_by_asset_name.keys() + folder_entities = get_folders( + project_name, folder_paths=instances_by_folder_path.keys() ) - asset_docs_by_name = { - get_asset_name_identifier(asset_doc): asset_doc - for asset_doc in asset_docs + folder_entities_by_path = { + folder_entity["path"]: folder_entity + for folder_entity in folder_entities } - for asset_name, instances in instances_by_asset_name.items(): - asset_doc = asset_docs_by_name[asset_name] - asset_data = get_asset_template_data(asset_doc, project_name) + all_task_names = set() + folder_ids = set() + # Fill folderEntity and prepare data for task entities + for folder_path, instances in instances_by_folder_path.items(): + folder_entity = folder_entities_by_path[folder_path] + folder_ids.add(folder_entity["id"]) for instance in instances: task_name = instance.data.get("task") - template_data = get_task_template_data( - project_entity, asset_doc, task_name) - template_data.update(copy.deepcopy(asset_data)) + all_task_names.add(task_name) + + # Query task entities + # Discard 'None' task names + all_task_names.discard(None) + tasks_by_name_by_folder_id = { + folder_id: {} for folder_id in folder_ids + } + task_entities = [] + if all_task_names: + task_entities = get_tasks( + project_name, + task_names=all_task_names, + folder_ids=folder_ids, + ) + for task_entity in task_entities: + task_name = task_entity["name"] + folder_id = task_entity["folderId"] + tasks_by_name_by_folder_id[folder_id][task_name] = task_entity + + for folder_path, instances in instances_by_folder_path.items(): + folder_entity = folder_entities_by_path[folder_path] + folder_id = folder_entity["id"] + folder_data = get_folder_template_data( + folder_entity, project_name + ) + task_entities_by_name = tasks_by_name_by_folder_id[folder_id] + for instance in instances: + task_name = instance.data.get("task") + task_entity = task_entities_by_name.get(task_name) + template_data = {} + if task_entity: + template_data = get_task_template_data( + project_entity, task_entity + ) + template_data.update(copy.deepcopy(folder_data)) instance.data["anatomyData"].update(template_data) - instance.data["assetEntity"] = asset_doc + instance.data["folderEntity"] = folder_entity + instance.data["taskEntity"] = task_entity def _create_hierarchy(self, context, project_name): hierarchy_context = self._filter_hierarchy(context) diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index 12c702c93b..1e295d2763 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -413,14 +413,14 @@ class IntegrateAsset(pyblish.api.InstancePlugin): ) def prepare_subset(self, instance, op_session, project_name): - asset_doc = instance.data["assetEntity"] + folder_entity = instance.data["folderEntity"] product_name = instance.data["productName"] product_type = instance.data["productType"] self.log.debug("Product: {}".format(product_name)) # Get existing subset if it exists existing_subset_doc = get_subset_by_name( - project_name, product_name, asset_doc["_id"] + project_name, product_name, folder_entity["id"] ) # Define subset data @@ -441,7 +441,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): if existing_subset_doc: subset_id = existing_subset_doc["_id"] subset_doc = new_subset_document( - product_name, product_type, asset_doc["_id"], data, subset_id + product_name, product_type, folder_entity["id"], data, subset_id ) if existing_subset_doc is None: diff --git a/client/ayon_core/plugins/publish/integrate_thumbnail.py b/client/ayon_core/plugins/publish/integrate_thumbnail.py index 9eb649d5a0..362c5686ab 100644 --- a/client/ayon_core/plugins/publish/integrate_thumbnail.py +++ b/client/ayon_core/plugins/publish/integrate_thumbnail.py @@ -191,9 +191,9 @@ class IntegrateThumbnailsAYON(pyblish.api.ContextPlugin): version_name, version_id )) - asset_entity = instance.data["assetEntity"] + folder_id = instance.data["folderEntity"]["id"] folder_path = instance.data["folderPath"] - thumbnail_info_by_entity_id[asset_entity["_id"]] = { + thumbnail_info_by_entity_id[folder_id] = { "thumbnail_id": thumbnail_id, "entity_type": "asset", } diff --git a/client/ayon_core/plugins/publish/validate_asset_docs.py b/client/ayon_core/plugins/publish/validate_asset_docs.py index 22d957f6e2..95fe4252be 100644 --- a/client/ayon_core/plugins/publish/validate_asset_docs.py +++ b/client/ayon_core/plugins/publish/validate_asset_docs.py @@ -2,27 +2,27 @@ import pyblish.api from ayon_core.pipeline import PublishValidationError -class ValidateAssetDocs(pyblish.api.InstancePlugin): - """Validate existence of asset documents on instances. +class ValidateFolderEntities(pyblish.api.InstancePlugin): + """Validate existence of folder entity on instances. - Without asset document it is not possible to publish the instance. + Without folder entity it is not possible to publish the instance. - If context has set asset document the validation is skipped. + If context has set folder entity the validation is skipped. - Plugin was added because there are cases when context asset is not defined - e.g. in tray publisher. + Plugin was added because there are cases when context folder is not + defined e.g. in tray publisher. """ - label = "Validate Asset docs" + label = "Validate Folder entities" order = pyblish.api.ValidatorOrder def process(self, instance): - context_asset_doc = instance.context.data.get("assetEntity") - if context_asset_doc: + context_folder_entity = instance.context.data.get("folderEntity") + if context_folder_entity: return - if instance.data.get("assetEntity"): - self.log.debug("Instance has set asset document in its data.") + if instance.data.get("folderEntity"): + self.log.debug("Instance has set fodler entity in its data.") elif instance.data.get("newAssetPublishing"): # skip if it is editorial @@ -30,6 +30,6 @@ class ValidateAssetDocs(pyblish.api.InstancePlugin): else: raise PublishValidationError(( - "Instance \"{}\" doesn't have asset document " + "Instance \"{}\" doesn't have folder entity " "set which is needed for publishing." ).format(instance.data["name"])) diff --git a/client/ayon_core/plugins/publish/validate_editorial_asset_name.py b/client/ayon_core/plugins/publish/validate_editorial_asset_name.py index dd1a19f602..9b6794a0c4 100644 --- a/client/ayon_core/plugins/publish/validate_editorial_asset_name.py +++ b/client/ayon_core/plugins/publish/validate_editorial_asset_name.py @@ -1,19 +1,20 @@ from pprint import pformat +import ayon_api import pyblish.api -from ayon_core.client import get_assets, get_asset_name_identifier +from ayon_core.pipeline import KnownPublishError class ValidateEditorialAssetName(pyblish.api.ContextPlugin): - """ Validating if editorial's asset names are not already created in db. + """ Validating if editorial's folder names are not already created in db. Checking variations of names with different size of caps or with or without underscores. """ order = pyblish.api.ValidatorOrder - label = "Validate Editorial Asset Name" + label = "Validate Editorial Folder Name" hosts = [ "hiero", "resolve", @@ -23,62 +24,67 @@ class ValidateEditorialAssetName(pyblish.api.ContextPlugin): def process(self, context): - asset_and_parents = self.get_parents(context) - self.log.debug("__ asset_and_parents: {}".format(asset_and_parents)) + folder_and_parents = self.get_parents(context) + self.log.debug("__ folder_and_parents: {}".format(folder_and_parents)) project_name = context.data["projectName"] - db_assets = list(get_assets( - project_name, fields=["name", "data.parents"] + folder_entities = list(ayon_api.get_folders( + project_name, fields={"path"} )) - self.log.debug("__ db_assets: {}".format(db_assets)) + self.log.debug("__ folder_entities: {}".format(folder_entities)) - asset_db_docs = { - get_asset_name_identifier(asset_doc): list( - asset_doc["data"]["parents"] + existing_folder_paths = { + folder_entity["path"]: ( + folder_entity["path"].lstrip("/").rsplit("/")[0] ) - for asset_doc in db_assets + for folder_entity in folder_entities } self.log.debug("__ project_entities: {}".format( - pformat(asset_db_docs))) + pformat(existing_folder_paths))) - assets_missing_name = {} - assets_wrong_parent = {} - for asset in asset_and_parents.keys(): - if asset not in asset_db_docs.keys(): + folders_missing_name = {} + folders_wrong_parent = {} + for folder_path in folder_and_parents.keys(): + if folder_path not in existing_folder_paths.keys(): # add to some nonexistent list for next layer of check - assets_missing_name[asset] = asset_and_parents[asset] + folders_missing_name[folder_path] = ( + folder_and_parents[folder_path] + ) continue - if asset_and_parents[asset] != asset_db_docs[asset]: + existing_parents = existing_folder_paths[folder_path] + if folder_and_parents[folder_path] != existing_parents: # add to some nonexistent list for next layer of check - assets_wrong_parent[asset] = { - "required": asset_and_parents[asset], - "already_in_db": asset_db_docs[asset] + folders_wrong_parent[folder_path] = { + "required": folder_and_parents[folder_path], + "already_in_db": existing_folder_paths[folder_path] } continue - self.log.debug("correct asset: {}".format(asset)) + self.log.debug("correct folder: {}".format(folder_path)) - if assets_missing_name: + if folders_missing_name: wrong_names = {} self.log.debug( - ">> assets_missing_name: {}".format(assets_missing_name)) + ">> folders_missing_name: {}".format(folders_missing_name)) - # This will create set asset names - asset_names = { - a.lower().replace("_", "") for a in asset_db_docs + # This will create set of folder paths + folder_paths = { + folder_path.lower().replace("_", "") + for folder_path in existing_folder_paths } - for asset in assets_missing_name: - _asset = asset.lower().replace("_", "") - if _asset in asset_names: - wrong_names[asset].update( + for folder_path in folders_missing_name: + _folder_path = folder_path.lower().replace("_", "") + if _folder_path in folder_paths: + wrong_names[folder_path].update( { - "required_name": asset, + "required_name": folder_path, "used_variants_in_db": [ - a for a in asset_db_docs - if a.lower().replace("_", "") == _asset + p + for p in existing_folder_paths + if p.lower().replace("_", "") == _folder_path ] } ) @@ -87,33 +93,19 @@ class ValidateEditorialAssetName(pyblish.api.ContextPlugin): self.log.debug( ">> wrong_names: {}".format(wrong_names)) raise Exception( - "Some already existing asset name variants `{}`".format( + "Some already existing folder name variants `{}`".format( wrong_names)) - if assets_wrong_parent: + if folders_wrong_parent: self.log.debug( - ">> assets_wrong_parent: {}".format(assets_wrong_parent)) - raise Exception( - "Wrong parents on assets `{}`".format(assets_wrong_parent)) - - def _get_all_assets(self, input_dict): - """ Returns asset names in list. - - List contains all asset names including parents - """ - for key in input_dict.keys(): - # check if child key is available - if input_dict[key].get("childs"): - # loop deeper - self._get_all_assets( - input_dict[key]["childs"]) - else: - self.all_testing_assets.append(key) + ">> folders_wrong_parent: {}".format(folders_wrong_parent)) + raise KnownPublishError( + "Wrong parents on folders `{}`".format(folders_wrong_parent)) def get_parents(self, context): - return_dict = {} + output = {} for instance in context: - asset = instance.data["folderPath"] + folder_path = instance.data["folderPath"] families = instance.data.get("families", []) + [ instance.data["family"] ] @@ -123,8 +115,8 @@ class ValidateEditorialAssetName(pyblish.api.ContextPlugin): parents = instance.data["parents"] - return_dict[asset] = [ + output[folder_path] = [ str(p["entity_name"]) for p in parents if p["entity_type"].lower() != "project" ] - return return_dict + return output diff --git a/client/ayon_core/plugins/publish/validate_unique_subsets.py b/client/ayon_core/plugins/publish/validate_unique_subsets.py index 3144675c50..bda40b25b5 100644 --- a/client/ayon_core/plugins/publish/validate_unique_subsets.py +++ b/client/ayon_core/plugins/publish/validate_unique_subsets.py @@ -1,5 +1,7 @@ from collections import defaultdict + import pyblish.api + from ayon_core.pipeline.publish import ( PublishXmlValidationError, ) @@ -9,7 +11,7 @@ class ValidateSubsetUniqueness(pyblish.api.ContextPlugin): """Validate all product names are unique. This only validates whether the instances currently set to publish from - the workfile overlap one another for the asset + product they are publishing + the workfile overlap one another for the folder + product they are publishing to. This does not perform any check against existing publishes in the database @@ -17,7 +19,7 @@ class ValidateSubsetUniqueness(pyblish.api.ContextPlugin): versioning. A product may appear twice to publish from the workfile if one - of them is set to publish to another asset than the other. + of them is set to publish to another folder than the other. """ @@ -27,18 +29,18 @@ class ValidateSubsetUniqueness(pyblish.api.ContextPlugin): def process(self, context): - # Find instance per (asset,product) - instance_per_asset_product = defaultdict(list) + # Find instance per (folder,product) + instance_per_folder_product = defaultdict(list) for instance in context: # Ignore disabled instances if not instance.data.get('publish', True): continue - # Ignore instance without asset data - asset = instance.data.get("folderPath") - if asset is None: - self.log.warning("Instance found without `asset` data: " + # Ignore instance without folder data + folder_path = instance.data.get("folderPath") + if folder_path is None: + self.log.warning("Instance found without `folderPath` data: " "{}".format(instance.name)) continue @@ -50,16 +52,21 @@ class ValidateSubsetUniqueness(pyblish.api.ContextPlugin): ).format(instance.name)) continue - instance_per_asset_product[(asset, product_name)].append(instance) + instance_per_folder_product[(folder_path, product_name)].append( + instance + ) non_unique = [] - for (asset, product_name), instances in instance_per_asset_product.items(): - - # A single instance per asset, product is fine + for (folder_path, product_name), instances in ( + instance_per_folder_product.items() + ): + # A single instance per folder, product is fine if len(instances) < 2: continue - non_unique.append("{} > {}".format(asset, product_name)) + non_unique.append( + "{} > {}".format(folder_path, product_name) + ) if not non_unique: # All is ok From 9400cabe2bf79e82b4a1ff88a738afce016a8e91 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 17:41:30 +0100 Subject: [PATCH 421/573] fix get product name in publisher --- client/ayon_core/tools/publisher/control.py | 55 ++++++++++----------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/client/ayon_core/tools/publisher/control.py b/client/ayon_core/tools/publisher/control.py index f6153da47f..32f3a5f9dc 100644 --- a/client/ayon_core/tools/publisher/control.py +++ b/client/ayon_core/tools/publisher/control.py @@ -12,10 +12,8 @@ from abc import ABCMeta, abstractmethod import six import arrow import pyblish.api -import ayon_api from ayon_core.client import ( - get_asset_by_name, get_subsets, ) from ayon_core.lib.events import QueuedEventSystem @@ -66,24 +64,6 @@ class MainThreadItem: self.callback(*self.args, **self.kwargs) -class AssetDocsCache: - """Cache asset documents for creation part.""" - - def __init__(self, controller): - self._controller = controller - self._asset_docs_by_path = {} - - def reset(self): - self._asset_docs_by_path = {} - - def get_asset_doc_by_folder_path(self, folder_path): - if folder_path not in self._asset_docs_by_path: - project_name = self._controller.project_name - asset_doc = get_asset_by_name(project_name, folder_path) - self._asset_docs_by_path[folder_path] = asset_doc - return copy.deepcopy(self._asset_docs_by_path[folder_path]) - - class PublishReportMaker: """Report for single publishing process. @@ -1653,7 +1633,6 @@ class PublisherController(BasePublisherController): # Cacher of avalon documents self._hierarchy_model = HierarchyModel(self) - self._asset_docs_cache = AssetDocsCache(self) @property def project_name(self): @@ -1816,11 +1795,10 @@ class PublisherController(BasePublisherController): self._create_context.reset_preparation() - # Reset avalon context + # Reset current context self._create_context.reset_current_context() self._hierarchy_model.reset() - self._asset_docs_cache.reset() self._reset_plugins() # Publish part must be reset after plugins @@ -2052,16 +2030,37 @@ class PublisherController(BasePublisherController): """ creator = self._creators[creator_identifier] - project_name = self.project_name - asset_doc = self._asset_docs_cache.get_asset_doc_by_folder_path( - folder_path - ) + instance = None if instance_id: instance = self.instances[instance_id] + project_name = self.project_name + folder_item = self._hierarchy_model.get_folder_item_by_path( + project_name, folder_path + ) + folder_entity = None + task_item = None + task_entity = None + if folder_item is not None: + folder_entity = self._hierarchy_model.get_folder_entity( + project_name, folder_item.entity_id + ) + task_item = self._hierarchy_model.get_task_item_by_name( + project_name, folder_item.entity_id, task_name, "controller" + ) + + if task_item is not None: + task_entity = self._hierarchy_model.get_task_entity( + project_name, task_item.task_id + ) + return creator.get_product_name( - project_name, asset_doc, task_name, variant, instance=instance + project_name, + folder_entity, + task_entity, + variant, + instance=instance ) def trigger_convertor_items(self, convertor_identifiers): From a2def7be0eb779470fc9453eb9bf9983f7e3e6ba Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 17:41:43 +0100 Subject: [PATCH 422/573] modified push to project --- .../tools/push_to_project/control.py | 60 +++--- .../tools/push_to_project/models/integrate.py | 187 +++++++++--------- .../tools/push_to_project/ui/window.py | 8 +- 3 files changed, 134 insertions(+), 121 deletions(-) diff --git a/client/ayon_core/tools/push_to_project/control.py b/client/ayon_core/tools/push_to_project/control.py index 3b6bd85028..1e1434c27e 100644 --- a/client/ayon_core/tools/push_to_project/control.py +++ b/client/ayon_core/tools/push_to_project/control.py @@ -1,7 +1,8 @@ import threading +import ayon_api + from ayon_core.client import ( - get_asset_by_id, get_subset_by_id, get_version_by_id, get_representations, @@ -32,7 +33,8 @@ class PushToContextController: self._src_project_name = None self._src_version_id = None - self._src_asset_doc = None + self._src_folder_entity = None + self._src_folder_task_entities = {} self._src_subset_doc = None self._src_version_doc = None self._src_label = None @@ -71,7 +73,8 @@ class PushToContextController: self._src_project_name = project_name self._src_version_id = version_id self._src_label = None - asset_doc = None + folder_entity = None + task_entities = {} subset_doc = None version_doc = None if project_name and version_id: @@ -81,13 +84,24 @@ class PushToContextController: subset_doc = get_subset_by_id(project_name, version_doc["parent"]) if subset_doc: - asset_doc = get_asset_by_id(project_name, subset_doc["parent"]) + folder_entity = ayon_api.get_folder_by_id( + project_name, subset_doc["parent"] + ) - self._src_asset_doc = asset_doc + if folder_entity: + task_entities = { + task_entity["name"]: task_entity + for task_entity in ayon_api.get_tasks( + project_name, folder_ids=[folder_entity["id"]] + ) + } + + self._src_folder_entity = folder_entity + self._src_folder_task_entities = task_entities self._src_subset_doc = subset_doc self._src_version_doc = version_doc - if asset_doc: - self._user_values.set_new_folder_name(asset_doc["name"]) + if folder_entity: + self._user_values.set_new_folder_name(folder_entity["name"]) variant = self._get_src_variant() if variant: self._user_values.set_variant(variant) @@ -197,39 +211,35 @@ class PushToContextController: if not self._src_project_name or not self._src_version_id: return "Source is not defined" - asset_doc = self._src_asset_doc - if not asset_doc: + folder_entity = self._src_folder_entity + if not folder_entity: return "Source is invalid" - folder_path_parts = list(asset_doc["data"]["parents"]) - folder_path_parts.append(asset_doc["name"]) - folder_path = "/".join(folder_path_parts) + folder_path = folder_entity["path"] subset_doc = self._src_subset_doc version_doc = self._src_version_doc - return "Source: {}/{}/{}/v{:0>3}".format( + return "Source: {}{}/{}/v{:0>3}".format( self._src_project_name, folder_path, subset_doc["name"], version_doc["name"] ) - def _get_task_info_from_repre_docs(self, asset_doc, repre_docs): - asset_tasks = asset_doc["data"].get("tasks") or {} + def _get_task_info_from_repre_docs(self, task_entities, repre_docs): found_comb = [] for repre_doc in repre_docs: context = repre_doc["context"] - task_info = context.get("task") - if task_info is None: + repre_task_name = context.get("task") + if repre_task_name is None: continue + if isinstance(repre_task_name, dict): + repre_task_name = repre_task_name.get("name") + task_name = None task_type = None - if isinstance(task_info, str): - task_name = task_info - asset_task_info = asset_tasks.get(task_info) or {} - task_type = asset_task_info.get("type") - - elif isinstance(task_info, dict): + if repre_task_name: + task_info = task_entities.get(repre_task_name) or {} task_name = task_info.get("name") task_type = task_info.get("type") @@ -246,12 +256,12 @@ class PushToContextController: def _get_src_variant(self): project_name = self._src_project_name version_doc = self._src_version_doc - asset_doc = self._src_asset_doc + task_entities = self._src_folder_task_entities repre_docs = get_representations( project_name, version_ids=[version_doc["_id"]] ) task_name, task_type = self._get_task_info_from_repre_docs( - asset_doc, repre_docs + task_entities, repre_docs ) project_settings = get_project_settings(project_name) diff --git a/client/ayon_core/tools/push_to_project/models/integrate.py b/client/ayon_core/tools/push_to_project/models/integrate.py index d0b26d1c03..0c37055a2b 100644 --- a/client/ayon_core/tools/push_to_project/models/integrate.py +++ b/client/ayon_core/tools/push_to_project/models/integrate.py @@ -11,8 +11,6 @@ import uuid import ayon_api from ayon_core.client import ( - get_assets, - get_asset_by_id, get_subset_by_id, get_subset_by_name, get_version_by_id, @@ -22,7 +20,6 @@ from ayon_core.client import ( ) from ayon_core.client.operations import ( OperationsSession, - new_asset_document, new_subset_document, new_version_doc, new_representation_doc, @@ -449,15 +446,14 @@ class ProjectPushItemProcess: self._model = model self._item = item - self._src_asset_doc = None + self._src_folder_entity = None self._src_subset_doc = None self._src_version_doc = None self._src_repre_items = None self._project_entity = None self._anatomy = None - self._asset_doc = None - self._created_asset_doc = None + self._folder_entity = None self._task_info = None self._subset_doc = None self._version_doc = None @@ -493,8 +489,8 @@ class ProjectPushItemProcess: self._log_info("Source entities were found") self._fill_destination_project() self._log_info("Destination project was found") - self._fill_or_create_destination_asset() - self._log_info("Destination asset was determined") + self._fill_or_create_destination_folder() + self._log_info("Destination folder was determined") self._determine_product_type() self._determine_publish_template_name() self._determine_product_name() @@ -594,11 +590,13 @@ class ProjectPushItemProcess: )) raise PushToProjectError(self._status.fail_reason) - asset_id = subset_doc["parent"] - asset_doc = get_asset_by_id(src_project_name, asset_id) - if not asset_doc: + folder_id = subset_doc["parent"] + folder_entity = ayon_api.get_folder_by_id( + src_project_name, folder_id, own_attributes=True + ) + if not folder_entity: self._status.set_failed(( - f"Could find asset with id \"{asset_id}\"" + f"Could find folder with id \"{folder_id}\"" f" in project \"{src_project_name}\"" )) raise PushToProjectError(self._status.fail_reason) @@ -624,7 +622,7 @@ class ProjectPushItemProcess: ) raise PushToProjectError(self._status.fail_reason) - self._src_asset_doc = asset_doc + self._src_folder_entity = folder_entity self._src_subset_doc = subset_doc self._src_version_doc = version_doc self._src_repre_items = repre_items @@ -652,50 +650,37 @@ class ProjectPushItemProcess: self._item.dst_project_name ) - def _create_asset( + def _create_folder( self, - src_asset_doc, + src_folder_entity, project_entity, - parent_asset_doc, - asset_name + parent_folder_entity, + folder_name ): parent_id = None - parents = [] - tools = [] - if parent_asset_doc: - parent_id = parent_asset_doc["_id"] - parents = list(parent_asset_doc["data"]["parents"]) - parents.append(parent_asset_doc["name"]) - _tools = parent_asset_doc["data"].get("tools_env") - if _tools: - tools = list(_tools) + if parent_folder_entity: + parent_id = parent_folder_entity["id"] - asset_name_low = asset_name.lower() - other_asset_docs = get_assets( + folder_name_low = folder_name.lower() + other_folder_entities = ayon_api.get_folders( project_entity["name"], - fields=["_id", "name", "data.visualParent"] + parent_ids=[parent_id], + fields={"id", "name"} ) - for other_asset_doc in other_asset_docs: - other_name = other_asset_doc["name"] - other_parent_id = other_asset_doc["data"].get("visualParent") - if other_name.lower() != asset_name_low: + for other_folder_entity in other_folder_entities: + other_name = other_folder_entity["name"] + if other_name.lower() != folder_name_low: continue - if other_parent_id != parent_id: - self._status.set_failed(( - f"Asset with name \"{other_name}\" already" - " exists in different hierarchy." - )) - raise PushToProjectError(self._status.fail_reason) - self._log_debug(( - f"Found already existing asset with name \"{other_name}\"" - f" which match requested name \"{asset_name}\"" + f"Found already existing folder with name \"{other_name}\"" + f" which match requested name \"{folder_name}\"" )) - return get_asset_by_id( - project_entity["name"], other_asset_doc["_id"] + return ayon_api.get_folder_by_id( + project_entity["name"], other_folder_entity["id"] ) + # TODO should we hard pass attribute values? data_keys = ( "clipIn", "clipOut", @@ -708,87 +693,105 @@ class ProjectPushItemProcess: "fps", "pixelAspect", ) - asset_data = { - "visualParent": parent_id, - "parents": parents, - "tasks": {}, - "tools_env": tools + new_folder_attrib = {} + src_attrib = src_folder_entity["attrib"] + for attr_name, attr_value in src_attrib.items(): + if attr_name in data_keys: + new_folder_attrib[attr_name] = attr_value + + new_folder_name = ayon_api.slugify_string(folder_name) + folder_label = None + if new_folder_name != folder_name: + folder_label = folder_name + fodler_create_data = { + "name": folder_name, + # TODO use different folder type? + "folderType": "Folder", } - src_asset_data = src_asset_doc["data"] - for key in data_keys: - if key in src_asset_data: - asset_data[key] = src_asset_data[key] + if parent_id: + fodler_create_data["parentId"] = parent_id - asset_doc = new_asset_document( - asset_name, - project_entity["name"], - parent_id, - parents, - data=asset_data + if folder_label: + fodler_create_data["label"] = folder_label + + if new_folder_attrib: + fodler_create_data["attrib"] = new_folder_attrib + + project_name = project_entity["name"] + response = ayon_api.post( + f"projects/{project_name}/folders", + **fodler_create_data ) - self._operations.create_entity( - project_entity["name"], - asset_doc["type"], - asset_doc + new_folder_entity = ayon_api.get_folder_by_id( + project_name, response.data["id"] ) + # TODO use operations + # self._operations.create_entity( + # project_entity["name"], + # "folder", + # fodler_create_data + # ) self._log_info( - f"Creating new asset with name \"{asset_name}\"" + f"Creating new folder with name \"{folder_name}\"" ) - self._created_asset_doc = asset_doc - return asset_doc + return new_folder_entity - def _fill_or_create_destination_asset(self): + def _fill_or_create_destination_folder(self): dst_project_name = self._item.dst_project_name dst_folder_id = self._item.dst_folder_id dst_task_name = self._item.dst_task_name + dst_task_name_low = dst_task_name.lower() new_folder_name = self._item.new_folder_name if not dst_folder_id and not new_folder_name: self._status.set_failed( - "Push item does not have defined destination asset" + "Push item does not have defined destination folder" ) raise PushToProjectError(self._status.fail_reason) - # Get asset document - parent_asset_doc = None + # Get folder entity + parent_folder_entity = None if dst_folder_id: - parent_asset_doc = get_asset_by_id( + parent_folder_entity = ayon_api.get_folder_by_id( self._item.dst_project_name, self._item.dst_folder_id ) - if not parent_asset_doc: + if not parent_folder_entity: self._status.set_failed( - f"Could find asset with id \"{dst_folder_id}\"" + f"Could find folder with id \"{dst_folder_id}\"" f" in project \"{dst_project_name}\"" ) raise PushToProjectError(self._status.fail_reason) if not new_folder_name: - asset_doc = parent_asset_doc + folder_entity = parent_folder_entity else: - asset_doc = self._create_asset( - self._src_asset_doc, + folder_entity = self._create_folder( + self._src_folder_entity, self._project_entity, - parent_asset_doc, + parent_folder_entity, new_folder_name ) - self._asset_doc = asset_doc + self._folder_entity = folder_entity if not dst_task_name: self._task_info = {} return - asset_path_parts = list(asset_doc["data"]["parents"]) - asset_path_parts.append(asset_doc["name"]) - asset_path = "/".join(asset_path_parts) - asset_tasks = asset_doc.get("data", {}).get("tasks") or {} - task_info = asset_tasks.get(dst_task_name) + folder_path = folder_entity["path"] + folder_tasks = { + task_entity["name"].lower(): task_entity + for task_entity in ayon_api.get_tasks( + dst_project_name, folder_ids=[folder_entity["id"]] + ) + } + task_info = folder_tasks.get(dst_task_name_low) if not task_info: self._status.set_failed( f"Could find task with name \"{dst_task_name}\"" - f" on asset \"{asset_path}\"" + f" on folder \"{folder_path}\"" f" in project \"{dst_project_name}\"" ) raise PushToProjectError(self._status.fail_reason) - # Create copy of task info to avoid changing data in asset document + # Create copy of task info to avoid changing data in task entity task_info = copy.deepcopy(task_info) task_info["name"] = dst_task_name # Fill rest of task information based on task type @@ -836,12 +839,12 @@ class ProjectPushItemProcess: def _determine_product_name(self): product_type = self._product_type - asset_doc = self._asset_doc + folder_entity = self._folder_entity task_info = self._task_info product_name = get_product_name( self._item.dst_project_name, - asset_doc, - task_info.get("name"), + folder_entity, + task_info, self.host_name, product_type, self._item.variant, @@ -854,10 +857,10 @@ class ProjectPushItemProcess: def _make_sure_subset_exists(self): project_name = self._item.dst_project_name - asset_id = self._asset_doc["_id"] + folder_id = self._folder_entity["id"] product_name = self._product_name product_type = self._product_type - subset_doc = get_subset_by_name(project_name, product_name, asset_id) + subset_doc = get_subset_by_name(project_name, product_name, folder_id) if subset_doc: self._subset_doc = subset_doc return subset_doc @@ -866,7 +869,7 @@ class ProjectPushItemProcess: "families": [product_type] } subset_doc = new_subset_document( - product_name, product_type, asset_id, data + product_name, product_type, folder_id, data ) self._operations.create_entity(project_name, "subset", subset_doc) self._subset_doc = subset_doc @@ -961,7 +964,7 @@ class ProjectPushItemProcess: anatomy = self._anatomy formatting_data = get_template_data( self._project_entity, - self._asset_doc, + self._folder_entity, self._task_info.get("name"), self.host_name ) diff --git a/client/ayon_core/tools/push_to_project/ui/window.py b/client/ayon_core/tools/push_to_project/ui/window.py index 4d39075dc3..bc2fc6bf96 100644 --- a/client/ayon_core/tools/push_to_project/ui/window.py +++ b/client/ayon_core/tools/push_to_project/ui/window.py @@ -158,7 +158,7 @@ class PushToContextSelectWindow(QtWidgets.QWidget): main_thread_timer.timeout.connect(self._on_main_thread_timer) show_timer.timeout.connect(self._on_show_timer) user_input_changed_timer.timeout.connect(self._on_user_input_timer) - folder_name_input.textChanged.connect(self._on_new_asset_change) + folder_name_input.textChanged.connect(self._on_new_folder_change) variant_input.textChanged.connect(self._on_variant_change) comment_input.textChanged.connect(self._on_comment_change) @@ -169,7 +169,7 @@ class PushToContextSelectWindow(QtWidgets.QWidget): controller.register_event_callback( "new_folder_name.changed", - self._on_controller_new_asset_change + self._on_controller_new_folder_change ) controller.register_event_callback( "variant.changed", self._on_controller_variant_change @@ -291,7 +291,7 @@ class PushToContextSelectWindow(QtWidgets.QWidget): self.refresh() - def _on_new_asset_change(self, text): + def _on_new_folder_change(self, text): self._new_folder_name_input_text = text self._user_input_changed_timer.start() @@ -319,7 +319,7 @@ class PushToContextSelectWindow(QtWidgets.QWidget): self._comment_input_text = None self._controller.set_user_value_comment(comment) - def _on_controller_new_asset_change(self, event): + def _on_controller_new_folder_change(self, event): folder_name = event["new_folder_name"] if ( self._new_folder_name_input_text is None From d4f9de2cee8936f265a602c5043ad98a0667bac8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 17:41:57 +0100 Subject: [PATCH 423/573] texture copy tool use folder entity --- client/ayon_core/tools/texture_copy/app.py | 29 +++++++++++----------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/client/ayon_core/tools/texture_copy/app.py b/client/ayon_core/tools/texture_copy/app.py index 2736354cf2..b3484fbcd0 100644 --- a/client/ayon_core/tools/texture_copy/app.py +++ b/client/ayon_core/tools/texture_copy/app.py @@ -5,7 +5,6 @@ import click import speedcopy import ayon_api -from ayon_core.client import get_asset_by_name from ayon_core.lib import Terminal from ayon_core.pipeline import Anatomy from ayon_core.pipeline.template_data import get_template_data @@ -26,12 +25,12 @@ class TextureCopy: if os.path.splitext(x)[1].lower() in texture_extensions) return textures - def _get_destination_path(self, asset_doc, project_entity): + def _get_destination_path(self, folder_entity, project_entity): project_name = project_entity["name"] product_name = "Main" product_type = "texture" - template_data = get_template_data(project_entity, asset_doc) + template_data = get_template_data(project_entity, folder_entity) template_data.update({ "family": product_type, "subset": product_name, @@ -68,9 +67,9 @@ class TextureCopy: t.echo("!!! {}".format(e)) exit(1) - def process(self, asset_name, project_name, path): + def process(self, project_name, folder_path, path): """ - Process all textures found in path and copy them to asset under + Process all textures found in path and copy them to folder under project. """ @@ -87,17 +86,19 @@ class TextureCopy: t.echo("!!! Project name [ {} ] not found.".format(project_name)) exit(1) - asset_doc = get_asset_by_name(project_name, asset_name) - if not asset_doc: - t.echo("!!! Asset [ {} ] not found in project".format(asset_name)) + folder_entity = ayon_api.get_folder_by_path(project_name, folder_path) + if not folder_entity: + t.echo( + "!!! Folder [ {} ] not found in project".format(folder_path) + ) exit(1) t.echo( ( ">>> Project [ {} ] and folder [ {} ] seems to be OK ..." - ).format(project_entity['name'], asset_doc['name']) + ).format(project_entity['name'], folder_entity['path']) ) - dst_path = self._get_destination_path(asset_doc, project_entity) + dst_path = self._get_destination_path(folder_entity, project_entity) t.echo("--- Using [ {} ] as destination path".format(dst_path)) if not os.path.exists(dst_path): try: @@ -127,15 +128,15 @@ class TextureCopy: @click.command() -@click.option('--asset', required=True) @click.option('--project', required=True) +@click.option('--folder', required=True) @click.option('--path', required=True) -def texture_copy(asset, project, path): +def texture_copy(project, folder, path): t.echo("*** Running Texture tool ***") t.echo(">>> Initializing avalon session ...") os.environ["AYON_PROJECT_NAME"] = project - os.environ["AYON_FOLDER_PATH"] = asset - TextureCopy().process(asset, project, path) + os.environ["AYON_FOLDER_PATH"] = folder + TextureCopy().process(project, folder, path) if __name__ == '__main__': From 4cdb64621f07e342e8fdb616aeadfd8320690ebf Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 17:42:04 +0100 Subject: [PATCH 424/573] fix traypublisher window --- client/ayon_core/tools/traypublisher/window.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/tools/traypublisher/window.py b/client/ayon_core/tools/traypublisher/window.py index 210e77f0fa..988c22819a 100644 --- a/client/ayon_core/tools/traypublisher/window.py +++ b/client/ayon_core/tools/traypublisher/window.py @@ -41,7 +41,6 @@ class TrayPublisherController(QtPublisherController): def reset_hierarchy_cache(self): self._hierarchy_model.reset() - self._asset_docs_cache.reset() def get_project_items(self, sender=None): return self._projects_model.get_project_items(sender) From fafe213afefa0c5f8b1d8ae5a3d1098c334c9f8b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 17:42:30 +0100 Subject: [PATCH 425/573] fix loader tool --- client/ayon_core/tools/loader/control.py | 6 +- .../ayon_core/tools/loader/models/actions.py | 57 +++++++++++-------- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/client/ayon_core/tools/loader/control.py b/client/ayon_core/tools/loader/control.py index d2ee1d890c..5995bd2cae 100644 --- a/client/ayon_core/tools/loader/control.py +++ b/client/ayon_core/tools/loader/control.py @@ -320,10 +320,10 @@ class LoaderController(BackendLoaderController, FrontendLoaderController): context = get_current_context() folder_id = None project_name = context.get("project_name") - asset_name = context.get("folder_path") - if project_name and asset_name: + folder_path = context.get("folder_path") + if project_name and folder_path: folder = ayon_api.get_folder_by_path( - project_name, asset_name, fields=["id"] + project_name, folder_path, fields=["id"] ) if folder: folder_id = folder["id"] diff --git a/client/ayon_core/tools/loader/models/actions.py b/client/ayon_core/tools/loader/models/actions.py index 70577f6e6c..51a21155c9 100644 --- a/client/ayon_core/tools/loader/models/actions.py +++ b/client/ayon_core/tools/loader/models/actions.py @@ -8,7 +8,6 @@ import uuid import ayon_api from ayon_core.client import ( - get_assets, get_subsets, get_versions, get_representations, @@ -441,8 +440,10 @@ class LoaderActionsModel: product_docs_by_id = {p["_id"]: p for p in _product_docs} _folder_ids = {p["parent"] for p in product_docs_by_id.values()} - _folder_docs = get_assets(project_name, asset_ids=_folder_ids) - folder_docs_by_id = {f["_id"]: f for f in _folder_docs} + _folder_entities = ayon_api.get_folders( + project_name, folder_ids=_folder_ids + ) + folder_entities_by_id = {f["id"]: f for f in _folder_entities} project_entity = ayon_api.get_project(project_name) @@ -451,10 +452,10 @@ class LoaderActionsModel: product_id = version_doc["parent"] product_doc = product_docs_by_id[product_id] folder_id = product_doc["parent"] - folder_doc = folder_docs_by_id[folder_id] + folder_entity = folder_entities_by_id[folder_id] version_context_by_id[version_id] = { "project": project_entity, - "asset": folder_doc, + "folder": folder_entity, "subset": product_doc, "version": version_doc, } @@ -467,11 +468,11 @@ class LoaderActionsModel: product_id = version_doc["parent"] product_doc = product_docs_by_id[product_id] folder_id = product_doc["parent"] - folder_doc = folder_docs_by_id[folder_id] + folder_entity = folder_entities_by_id[folder_id] repre_context_by_id[repre_doc["_id"]] = { "project": project_entity, - "asset": folder_doc, + "folder": folder_entity, "subset": product_doc, "version": version_doc, "representation": repre_doc, @@ -519,19 +520,21 @@ class LoaderActionsModel: } folder_ids = {p["parent"] for p in product_docs_by_id.values()} - folder_docs = get_assets(project_name, asset_ids=folder_ids) - folder_docs_by_id = { - f["_id"]: f for f in folder_docs + folder_entities = ayon_api.get_folders( + project_name, folder_ids=folder_ids + ) + folder_entities_by_id = { + f["id"]: f for f in folder_entities } project_entity = ayon_api.get_project(project_name) for product_id, product_doc in product_docs_by_id.items(): folder_id = product_doc["parent"] - folder_doc = folder_docs_by_id[folder_id] + folder_entity = folder_entities_by_id[folder_id] product_context_by_id[product_id] = { "project": project_entity, - "asset": folder_doc, + "folder": folder_entity, "subset": product_doc, } @@ -541,11 +544,11 @@ class LoaderActionsModel: product_id = version_doc["parent"] product_doc = product_docs_by_id[product_id] folder_id = product_doc["parent"] - folder_doc = folder_docs_by_id[folder_id] + folder_entity = folder_entities_by_id[folder_id] repre_context_by_id[repre_doc["_id"]] = { "project": project_entity, - "asset": folder_doc, + "folder": folder_entity, "subset": product_doc, "version": version_doc, "representation": repre_doc, @@ -596,7 +599,7 @@ class LoaderActionsModel: repre_ids.add(repre_context["representation"]["_id"]) repre_product_ids.add(repre_context["subset"]["_id"]) repre_version_ids.add(repre_context["version"]["_id"]) - repre_folder_ids.add(repre_context["asset"]["_id"]) + repre_folder_ids.add(repre_context["folder"]["id"]) item = self._create_loader_action_item( loader, @@ -616,7 +619,7 @@ class LoaderActionsModel: product_ids = set() for product_context in version_context_by_id.values(): product_ids.add(product_context["subset"]["_id"]) - product_folder_ids.add(product_context["asset"]["_id"]) + product_folder_ids.add(product_context["folder"]["id"]) version_contexts = list(version_context_by_id.values()) for loader in product_loaders: @@ -666,17 +669,19 @@ class LoaderActionsModel: product_docs = get_subsets(project_name, subset_ids=product_ids) product_docs_by_id = {f["_id"]: f for f in product_docs} folder_ids = {p["parent"] for p in product_docs_by_id.values()} - folder_docs = get_assets(project_name, asset_ids=folder_ids) - folder_docs_by_id = {f["_id"]: f for f in folder_docs} + folder_entities = ayon_api.get_folders( + project_name, folder_ids=folder_ids + ) + folder_entities_by_id = {f["id"]: f for f in folder_entities} product_contexts = [] for version_doc in version_docs: product_id = version_doc["parent"] product_doc = product_docs_by_id[product_id] folder_id = product_doc["parent"] - folder_doc = folder_docs_by_id[folder_id] + folder_entity = folder_entities_by_id[folder_id] product_contexts.append({ "project": project_entity, - "asset": folder_doc, + "folder": folder_entity, "subset": product_doc, "version": version_doc, }) @@ -716,8 +721,10 @@ class LoaderActionsModel: product_docs = get_subsets(project_name, subset_ids=product_ids) product_docs_by_id = {p["_id"]: p for p in product_docs} folder_ids = {p["parent"] for p in product_docs_by_id.values()} - folder_docs = get_assets(project_name, asset_ids=folder_ids) - folder_docs_by_id = {f["_id"]: f for f in folder_docs} + folder_entities = ayon_api.get_folders( + project_name, folder_ids=folder_ids + ) + folder_entities_by_id = {f["_id"]: f for f in folder_entities} repre_contexts = [] for repre_doc in repre_docs: version_id = repre_doc["parent"] @@ -725,10 +732,10 @@ class LoaderActionsModel: product_id = version_doc["parent"] product_doc = product_docs_by_id[product_id] folder_id = product_doc["parent"] - folder_doc = folder_docs_by_id[folder_id] + folder_entity = folder_entities_by_id[folder_id] repre_contexts.append({ "project": project_entity, - "asset": folder_doc, + "folder": folder_entity, "subset": product_doc, "version": version_doc, "representation": repre_doc, @@ -744,7 +751,7 @@ class LoaderActionsModel: Args: loader (LoaderPlugin): Loader plugin to use. repre_contexts (list[dict]): Full info about selected - representations, containing repre, version, subset, asset and + representations, containing repre, version, subset, folder and project documents. options (dict): Data from options. """ From 6df9386234aba7f8a8addb19577726b5880fceb5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 17:42:49 +0100 Subject: [PATCH 426/573] workfiles tool use folder and task entity --- client/ayon_core/tools/workfiles/control.py | 25 ++++++----- .../tools/workfiles/models/workfiles.py | 44 +++---------------- 2 files changed, 18 insertions(+), 51 deletions(-) diff --git a/client/ayon_core/tools/workfiles/control.py b/client/ayon_core/tools/workfiles/control.py index 86c6a62a11..e09f9beea9 100644 --- a/client/ayon_core/tools/workfiles/control.py +++ b/client/ayon_core/tools/workfiles/control.py @@ -3,7 +3,6 @@ import shutil import ayon_api -from ayon_core.client import get_asset_by_id from ayon_core.host import IWorkfileHost from ayon_core.lib import Logger, emit_event from ayon_core.lib.events import QueuedEventSystem @@ -635,13 +634,10 @@ class BaseWorkfileController( folder = self.get_folder_entity(project_name, folder_id) if task is None: task = self.get_task_entity(project_name, task_id) - # NOTE keys should are OpenPype compatible return { "project_name": project_name, "folder_id": folder_id, "folder_path": folder["path"], - "asset_id": folder_id, - "asset_name": folder["name"], "task_id": task_id, "task_name": task["name"], "host_name": self.get_host_name(), @@ -662,15 +658,16 @@ class BaseWorkfileController( folder_id != self.get_current_folder_id() or task_name != self.get_current_task_name() ): - # Use OpenPype asset-like object - asset_doc = get_asset_by_id( + folder_entity = ayon_api.get_folder_by_id( event_data["project_name"], event_data["folder_id"], ) - change_current_context( - asset_doc, + task_entity = ayon_api.get_task_by_name( + event_data["project_name"], + event_data["folder_id"], event_data["task_name"] ) + change_current_context(folder_entity, task_entity) self._host_open_workfile(filepath) @@ -712,11 +709,15 @@ class BaseWorkfileController( folder_id != self.get_current_folder_id() or task_name != self.get_current_task_name() ): - # Use OpenPype asset-like object - asset_doc = get_asset_by_id(project_name, folder["id"]) + folder_entity = ayon_api.get_folder_by_id( + project_name, folder["id"] + ) + task_entity = ayon_api.get_task_by_name( + project_name, folder["id"], task_name + ) change_current_context( - asset_doc, - task["name"], + folder_entity, + task_entity, template_key=template_key ) diff --git a/client/ayon_core/tools/workfiles/models/workfiles.py b/client/ayon_core/tools/workfiles/models/workfiles.py index a0d618c1ae..ae8efaf23a 100644 --- a/client/ayon_core/tools/workfiles/models/workfiles.py +++ b/client/ayon_core/tools/workfiles/models/workfiles.py @@ -11,6 +11,8 @@ from ayon_core.client.operations import ( ) from ayon_core.pipeline.template_data import ( get_template_data, + get_task_template_data, + get_folder_template_data, ) from ayon_core.pipeline.workfile import ( get_workdir_with_workdir_data, @@ -25,42 +27,6 @@ from ayon_core.tools.workfiles.abstract import ( ) -def get_folder_template_data(folder): - if not folder: - return {} - parts = folder["path"].split("/") - parts.pop(-1) - hierarchy = "/".join(parts) - return { - "asset": folder["name"], - "folder": { - "name": folder["name"], - "type": folder["folderType"], - "path": folder["path"], - }, - "hierarchy": hierarchy, - } - - -def get_task_template_data(project_entity, task): - if not task: - return {} - short_name = None - task_type_name = task["taskType"] - for task_type_info in project_entity["taskTypes"]: - if task_type_info["name"] == task_type_name: - short_name = task_type_info["shortName"] - break - - return { - "task": { - "name": task["name"], - "type": task_type_name, - "short": short_name, - } - } - - class CommentMatcher(object): """Use anatomy and work file data to parse comments from filenames""" def __init__(self, extensions, file_template, data): @@ -152,7 +118,7 @@ class WorkareaModel: folder = self._controller.get_folder_entity( self.project_name, folder_id ) - fill_data = get_folder_template_data(folder) + fill_data = get_folder_template_data(folder, self.project_name) self._fill_data_by_folder_id[folder_id] = fill_data return copy.deepcopy(fill_data) @@ -228,9 +194,9 @@ class WorkareaModel: task_type = fill_data.get("task", {}).get("type") # TODO cache return get_workfile_template_key( + self.project_name, task_type, self._controller.get_host_name(), - project_name=self.project_name ) def _get_last_workfile_version( @@ -618,7 +584,7 @@ class PublishWorkfilesModel: def get_file_items(self, folder_id, task_name): # TODO refactor to use less server API calls project_name = self._controller.get_current_project_name() - # Get subset docs of asset + # Get subset docs of folder product_entities = ayon_api.get_products( project_name, folder_ids=[folder_id], From 24de1734312e71f6f3c278d5fb2f9c7e84efb5fe Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 17:43:21 +0100 Subject: [PATCH 427/573] removed unused function 'get_task_icon' --- client/ayon_core/tools/utils/lib.py | 31 ----------------------------- 1 file changed, 31 deletions(-) diff --git a/client/ayon_core/tools/utils/lib.py b/client/ayon_core/tools/utils/lib.py index 8c2eb517df..2e70f27da5 100644 --- a/client/ayon_core/tools/utils/lib.py +++ b/client/ayon_core/tools/utils/lib.py @@ -240,37 +240,6 @@ def get_default_task_icon(color=None): return get_qta_icon_by_name_and_color("fa.male", color) -def get_task_icon(project_entity, asset_doc, task_name): - """Get icon for a task. - - Icon should be defined by task type which is stored on project. - """ - - 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") - task_types_by_name = { - task_type["name"]: task_type - for task_type in project_entity["taskTypes"] - } - - task_type_info = task_types_by_name.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 iter_model_rows(model, column, include_root=False): """Iterate over all row indices in a model""" indices = [QtCore.QModelIndex()] # start iteration at root From 0ebc2bca71c7c4cb038346dfe469d1e5fe3d8cec Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 17:43:34 +0100 Subject: [PATCH 428/573] creator use folder and task entity --- client/ayon_core/tools/creator/window.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/tools/creator/window.py b/client/ayon_core/tools/creator/window.py index bda7ebb11e..a537fe551e 100644 --- a/client/ayon_core/tools/creator/window.py +++ b/client/ayon_core/tools/creator/window.py @@ -2,9 +2,10 @@ import sys import traceback import re +import ayon_api from qtpy import QtWidgets, QtCore -from ayon_core.client import get_asset_by_name, get_subsets +from ayon_core.client import get_subsets from ayon_core import style from ayon_core.settings import get_current_project_settings from ayon_core.tools.utils.lib import qt_app_context @@ -216,20 +217,20 @@ class CreatorWindow(QtWidgets.QDialog): # Early exit if no folder path if not folder_path: self._build_menu() - self.echo("Asset name is required ..") + self.echo("Folder is required ..") self._set_valid_state(False) return project_name = get_current_project_name() - asset_doc = None + folder_entity = None if creator_plugin: - # Get the asset from the database which match with the name - asset_doc = get_asset_by_name( - project_name, folder_path, fields=["_id"] + # Get the folder from the database which match with the name + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path, fields={"id"} ) # Get plugin - if not asset_doc or not creator_plugin: + if not folder_entity or not creator_plugin: self._build_menu() if not creator_plugin: @@ -239,12 +240,16 @@ class CreatorWindow(QtWidgets.QDialog): self._set_valid_state(False) return - folder_id = asset_doc["_id"] + folder_id = folder_entity["id"] + task_name = get_current_task_name() + task_entity = ayon_api.get_task_by_name( + project_name, folder_id, task_name + ) # Calculate product name with Creator plugin product_name = creator_plugin.get_product_name( - project_name, folder_id, task_name, user_input_text + project_name, folder_entity, task_entity, user_input_text ) # Force replacement of prohibited symbols # QUESTION should Creator care about this and here should be only From 70ed24c0cefc05f1c0c0f0efac715dfd82a76e1c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 17:44:19 +0100 Subject: [PATCH 429/573] added 'get_selected_task_id' to tasks widget --- .../tools/ayon_utils/widgets/tasks_widget.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/tools/ayon_utils/widgets/tasks_widget.py b/client/ayon_core/tools/ayon_utils/widgets/tasks_widget.py index b273d83fa6..cfe901c492 100644 --- a/client/ayon_core/tools/ayon_utils/widgets/tasks_widget.py +++ b/client/ayon_core/tools/ayon_utils/widgets/tasks_widget.py @@ -381,6 +381,15 @@ class TasksWidget(QtWidgets.QWidget): "task_type": task_type, } + def get_selected_task_id(self): + """Get selected task id. + + Returns: + Union[str, None]: Task id. + + """ + return self.get_selected_task_info()["task_id"] + def get_selected_task_name(self): """Get selected task name. @@ -388,8 +397,7 @@ class TasksWidget(QtWidgets.QWidget): Union[str, None]: Task name. """ - _, _, task_name, _ = self._get_selected_item_ids() - return task_name + return self.get_selected_task_info()["task_name"] def get_selected_task_type(self): """Get selected task type. @@ -398,8 +406,7 @@ class TasksWidget(QtWidgets.QWidget): Union[str, None]: Task type. """ - _, _, _, task_type = self._get_selected_item_ids() - return task_type + return self.get_selected_task_info()["task_type"] def set_selected_task(self, task_name): """Set selected task by name. From 43bf1726e7cac848cdd8b13cdb5e8858e1eea313 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 17:44:51 +0100 Subject: [PATCH 430/573] implemented 'get_task_item_by_name' in hierarchy model --- .../tools/ayon_utils/models/hierarchy.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/client/ayon_core/tools/ayon_utils/models/hierarchy.py b/client/ayon_core/tools/ayon_utils/models/hierarchy.py index 10495cf10b..d8b28f020d 100644 --- a/client/ayon_core/tools/ayon_utils/models/hierarchy.py +++ b/client/ayon_core/tools/ayon_utils/models/hierarchy.py @@ -380,6 +380,26 @@ class HierarchyModel(object): ) return items.get(folder_path) + def get_task_item_by_name( + self, project_name, folder_id, task_name, sender + ): + """Get task item by name and folder id. + + Args: + project_name (str): Project name. + folder_id (str): Folder id. + task_name (str): Task name. + sender (Union[str, None]): Who requested the task item. + + Returns: + Union[TaskItem, None]: Task item found by name and folder id. + + """ + for task_item in self.get_task_items(project_name, folder_id, sender): + if task_item.name == task_name: + return task_item + return None + def get_task_items(self, project_name, folder_id, sender): if not project_name or not folder_id: return [] From 9b86afb4facf7b12ba8794fb4bbdcd38e64260e5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 17:45:26 +0100 Subject: [PATCH 431/573] use folder entity in deadline --- .../plugins/publish/submit_publish_cache_job.py | 15 +++++++++------ .../plugins/publish/submit_publish_job.py | 11 +++++++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py index ef146b5d79..1bf23f84a5 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py @@ -110,7 +110,7 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin, output_dir = self._get_publish_folder( anatomy, deepcopy(instance.data["anatomyData"]), - instance.data.get("folderPath"), + instance.data.get("folderEntity"), instance.data["productName"], instance.context, instance.data["productType"], @@ -380,7 +380,7 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin, json.dump(publish_job, f, indent=4, sort_keys=True) def _get_publish_folder(self, anatomy, template_data, - asset, product_name, context, + folder_entity, product_name, context, product_type, version=None): """ Extracted logic to pre-calculate real publish folder, which is @@ -394,7 +394,7 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin, Args: anatomy (ayon_core.pipeline.anatomy.Anatomy): template_data (dict): pre-calculated collected data for process - asset (str): asset name + folder_entity (dict[str, Any]): Folder entity. product_name (str): Product name (actually group name of product). product_type (str): for current deadline process it's always 'render' @@ -409,18 +409,22 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin, """ project_name = context.data["projectName"] + host_name = context.data["hostName"] if not version: + folder_id = None + if folder_entity: + folder_id = folder_entity["id"] version = get_last_version_by_subset_name( project_name, product_name, - asset_name=asset + asset_id=folder_id ) if version: version = int(version["name"]) + 1 else: version = get_versioning_start( project_name, - template_data["app"], + host_name, task_name=template_data["task"]["name"], task_type=template_data["task"]["type"], product_type="render", @@ -428,7 +432,6 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin, project_settings=context.data["project_settings"] ) - host_name = context.data["hostName"] task_info = template_data.get("task") or {} template_name = publish.get_publish_template_name( diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py index c512dc570c..3c78a95f1e 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py @@ -187,7 +187,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, output_dir = self._get_publish_folder( anatomy, deepcopy(instance.data["anatomyData"]), - instance.data.get("folderPath"), + instance.data.get("folderEntity"), instances[0]["productName"], instance.context, instances[0]["productType"], @@ -501,7 +501,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, json.dump(publish_job, f, indent=4, sort_keys=True) def _get_publish_folder(self, anatomy, template_data, - asset, product_name, context, + folder_entity, product_name, context, product_type, version=None): """ Extracted logic to pre-calculate real publish folder, which is @@ -515,7 +515,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, Args: anatomy (ayon_core.pipeline.anatomy.Anatomy): template_data (dict): pre-calculated collected data for process - asset (string): asset name + folder_entity (dict[str, Any]): Folder entity. product_name (string): Product name (actually group name of product) product_type (string): for current deadline process it's always @@ -533,10 +533,13 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, project_name = context.data["projectName"] host_name = context.data["hostName"] if not version: + folder_id = None + if folder_entity: + folder_id = folder_entity["id"] version = get_last_version_by_subset_name( project_name, product_name, - asset_name=asset + asset_id=folder_id ) if version: version = int(version["name"]) + 1 From 262cbda49306d2ed5b3bfa916c3bf197e3239fda Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 17:46:11 +0100 Subject: [PATCH 432/573] clockify use folder and task entity --- .../modules/clockify/clockify_module.py | 27 +++++++------------ .../launcher_actions/ClockifyStart.py | 25 +++++++---------- 2 files changed, 20 insertions(+), 32 deletions(-) diff --git a/client/ayon_core/modules/clockify/clockify_module.py b/client/ayon_core/modules/clockify/clockify_module.py index 58407bfe94..d2ee4f1e1e 100644 --- a/client/ayon_core/modules/clockify/clockify_module.py +++ b/client/ayon_core/modules/clockify/clockify_module.py @@ -3,7 +3,6 @@ import threading import time from ayon_core.modules import AYONAddon, ITrayModule, IPluginPaths -from ayon_core.client import get_asset_by_name from .constants import CLOCKIFY_FTRACK_USER_PATH, CLOCKIFY_FTRACK_SERVER_PATH @@ -255,33 +254,27 @@ class ClockifyModule(AYONAddon, ITrayModule, IPluginPaths): if not self.clockify_api.get_api_key(): return + project_name = input_data.get("project_name") + folder_path = input_data.get("folder_path") task_name = input_data.get("task_name") + task_type = input_data.get("task_type") + if not all((project_name, folder_path, task_name, task_type)): + return # Concatenate hierarchy and task to get description - description_items = list(input_data.get("hierarchy", [])) - description_items.append(task_name) - description = "/".join(description_items) + description = "/".join([folder_path.lstrip("/"), task_name]) # Check project existence - project_name = input_data.get("project_name") project_id = self._verify_project_exists(project_name) if not project_id: return # Setup timer tags - tag_ids = [] - tag_name = input_data.get("task_type") - if not tag_name: - # no task_type found in the input data - # if the timer is restarted by idle time (bug?) - asset_name = input_data["hierarchy"][-1] - asset_doc = get_asset_by_name(project_name, asset_name) - task_info = asset_doc["data"]["tasks"][task_name] - tag_name = task_info.get("type", "") - if not tag_name: - self.log.info("No tag information found for the timer") + if not task_type: + self.log.info("No tag information found for the timer") - task_tag_id = self.clockify_api.get_tag_id(tag_name) + tag_ids = [] + task_tag_id = self.clockify_api.get_tag_id(task_type) if task_tag_id is not None: tag_ids.append(task_tag_id) diff --git a/client/ayon_core/modules/clockify/launcher_actions/ClockifyStart.py b/client/ayon_core/modules/clockify/launcher_actions/ClockifyStart.py index f7dd1772b0..61c5eac2f5 100644 --- a/client/ayon_core/modules/clockify/launcher_actions/ClockifyStart.py +++ b/client/ayon_core/modules/clockify/launcher_actions/ClockifyStart.py @@ -1,4 +1,5 @@ -from ayon_core.client import get_asset_by_name +import ayon_api + from ayon_core.pipeline import LauncherAction from openpype_modules.clockify.clockify_api import ClockifyAPI @@ -21,24 +22,18 @@ class ClockifyStart(LauncherAction): user_id = self.clockify_api.user_id workspace_id = self.clockify_api.workspace_id project_name = session["AYON_PROJECT_NAME"] - asset_name = session["AYON_FOLDER_PATH"] + folder_path = session["AYON_FOLDER_PATH"] task_name = session["AYON_TASK_NAME"] - description = asset_name + description = "/".join([folder_path.lstrip("/"), task_name]) - # fetch asset docs - asset_doc = get_asset_by_name(project_name, asset_name) + # fetch folder entity + folder_entity = ayon_api.get_folder_by_path(project_name, folder_path) + task_entity = ayon_api.get_task_by_name( + project_name, folder_entity["id"], task_name + ) # get task type to fill the timer tag - task_info = asset_doc["data"]["tasks"][task_name] - task_type = task_info["type"] - - # check if the task has hierarchy and fill the - parents_data = asset_doc["data"] - if parents_data is not None: - description_items = parents_data.get("parents", []) - description_items.append(asset_name) - description_items.append(task_name) - description = "/".join(description_items) + task_type = task_entity["taskType"] project_id = self.clockify_api.get_project_id( project_name, workspace_id From c3296b13228637d7db236c95e00b7b909b9dfd32 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 17:48:24 +0100 Subject: [PATCH 433/573] use folder naming --- client/ayon_core/cli_commands.py | 4 +- client/ayon_core/host/host.py | 6 +- .../perjob/m50__openpype_publish_render.py | 6 +- .../plugins/publish/start_timer.py | 6 +- .../modules/timers_manager/rest_api.py | 12 +-- .../modules/timers_manager/timers_manager.py | 98 ++++++++++--------- client/ayon_core/pipeline/anatomy.py | 4 +- client/ayon_core/pipeline/create/context.py | 10 +- client/ayon_core/tools/adobe_webserver/app.py | 20 ++-- .../ayon_core/tools/context_dialog/window.py | 42 ++++---- .../tools/launcher/models/actions.py | 8 +- 11 files changed, 110 insertions(+), 106 deletions(-) diff --git a/client/ayon_core/cli_commands.py b/client/ayon_core/cli_commands.py index a24710aef2..156196c401 100644 --- a/client/ayon_core/cli_commands.py +++ b/client/ayon_core/cli_commands.py @@ -181,7 +181,7 @@ class Commands: json.dump(env, file_stream, indent=4) @staticmethod - def contextselection(output_path, project_name, asset_name, strict): + def contextselection(output_path, project_name, folder_path, strict): from ayon_core.tools.context_dialog import main - main(output_path, project_name, asset_name, strict) + main(output_path, project_name, folder_path, strict) diff --git a/client/ayon_core/host/host.py b/client/ayon_core/host/host.py index d9739603d7..b815dadfe1 100644 --- a/client/ayon_core/host/host.py +++ b/client/ayon_core/host/host.py @@ -161,13 +161,13 @@ class HostBase(object): # Use current context to fill the context title current_context = self.get_current_context() project_name = current_context["project_name"] - asset_name = current_context["folder_path"] + folder_path = current_context["folder_path"] task_name = current_context["task_name"] items = [] if project_name: items.append(project_name) - if asset_name: - items.append(asset_name.lstrip("/")) + if folder_path: + items.append(folder_path.lstrip("/")) if task_name: items.append(task_name) if items: diff --git a/client/ayon_core/modules/royalrender/rr_root/plugins/control_job/perjob/m50__openpype_publish_render.py b/client/ayon_core/modules/royalrender/rr_root/plugins/control_job/perjob/m50__openpype_publish_render.py index 778052778f..8405f69b3e 100644 --- a/client/ayon_core/modules/royalrender/rr_root/plugins/control_job/perjob/m50__openpype_publish_render.py +++ b/client/ayon_core/modules/royalrender/rr_root/plugins/control_job/perjob/m50__openpype_publish_render.py @@ -109,7 +109,7 @@ class OpenPypeContextSelector: if not self.context or \ not self.context.get("project") or \ - not self.context.get("asset") or \ + not self.context.get("folder") or \ not self.context.get("task"): self._show_rr_warning("Context selection failed.") return False @@ -137,7 +137,7 @@ class OpenPypeContextSelector: def run_publish(self): """Run publish process.""" env = {"AYON_PROJECT_NAME": str(self.context.get("project")), - "AYON_FOLDER_PATH": str(self.context.get("asset")), + "AYON_FOLDER_PATH": str(self.context.get("folder")), "AYON_TASK_NAME": str(self.context.get("task")), # "AYON_APP_NAME": str(self.context.get("app_name")) } @@ -184,7 +184,7 @@ selector = OpenPypeContextSelector() # try to set context from environment for key, env_keys in ( ("project", ["AYON_PROJECT_NAME", "AVALON_PROJECT"]), - ("asset", ["AYON_FOLDER_PATH", "AVALON_ASSET"]), + ("folder", ["AYON_FOLDER_PATH", "AVALON_ASSET"]), ("task", ["AYON_TASK_NAME", "AVALON_TASK"]), # ("app_name", ["AYON_APP_NAME", "AVALON_APP_NAME"]) ): diff --git a/client/ayon_core/modules/timers_manager/plugins/publish/start_timer.py b/client/ayon_core/modules/timers_manager/plugins/publish/start_timer.py index 182efbc4ae..620cdb6e65 100644 --- a/client/ayon_core/modules/timers_manager/plugins/publish/start_timer.py +++ b/client/ayon_core/modules/timers_manager/plugins/publish/start_timer.py @@ -24,14 +24,14 @@ class StartTimer(pyblish.api.ContextPlugin): return project_name = context.data["projectName"] - asset_name = context.data.get("folderPath") + folder_path = context.data.get("folderPath") task_name = context.data.get("task") - if not project_name or not asset_name or not task_name: + if not project_name or not folder_path or not task_name: self.log.info(( "Current context does not contain all" " required information to start a timer." )) return timers_manager.start_timer_with_webserver( - project_name, asset_name, task_name, self.log + project_name, folder_path, task_name, self.log ) diff --git a/client/ayon_core/modules/timers_manager/rest_api.py b/client/ayon_core/modules/timers_manager/rest_api.py index c890d587de..88a6539510 100644 --- a/client/ayon_core/modules/timers_manager/rest_api.py +++ b/client/ayon_core/modules/timers_manager/rest_api.py @@ -45,7 +45,7 @@ class TimersManagerModuleRestApi: data = await request.json() try: project_name = data["project_name"] - asset_name = data["folder_path"] + folder_path = data["folder_path"] task_name = data["task_name"] except KeyError: msg = ( @@ -57,7 +57,7 @@ class TimersManagerModuleRestApi: self.module.stop_timers() try: - self.module.start_timer(project_name, asset_name, task_name) + self.module.start_timer(project_name, folder_path, task_name) except Exception as exc: return Response(status=404, message=str(exc)) @@ -70,9 +70,9 @@ class TimersManagerModuleRestApi: async def get_task_time(self, request): data = await request.json() try: - project_name = data['project_name'] - asset_name = data['folder_path'] - task_name = data['task_name'] + project_name = data["project_name"] + folder_path = data["folder_path"] + task_name = data["task_name"] except KeyError: message = ( "Payload must contain fields 'project_name, 'folder_path'," @@ -81,5 +81,5 @@ class TimersManagerModuleRestApi: self.log.warning(message) return Response(text=message, status=404) - time = self.module.get_task_time(project_name, asset_name, task_name) + time = self.module.get_task_time(project_name, folder_path, task_name) return Response(text=json.dumps(time)) diff --git a/client/ayon_core/modules/timers_manager/timers_manager.py b/client/ayon_core/modules/timers_manager/timers_manager.py index e04200525a..4212ff6b25 100644 --- a/client/ayon_core/modules/timers_manager/timers_manager.py +++ b/client/ayon_core/modules/timers_manager/timers_manager.py @@ -1,8 +1,8 @@ import os import platform +import ayon_api -from ayon_core.client import get_asset_by_name from ayon_core.addon import ( AYONAddon, ITrayService, @@ -24,14 +24,18 @@ class ExampleTimersManagerConnector: Required methods are 'stop_timer' and 'start_timer'. - # TODO pass asset document instead of `hierarchy` Example of `data` that are passed during changing timer: ``` data = { "project_name": project_name, + "folder_id": folder_id, + "folder_path": folder_entity["path"], "task_name": task_name, "task_type": task_type, - "hierarchy": hierarchy + # Deprecated + "asset_id": folder_id, + "asset_name": folder_entity["name"], + "hierarchy": hierarchy_items, } ``` """ @@ -176,16 +180,14 @@ class TimersManager( """Convert string path to a timer data. It is expected that first item is project name, last item is task name - and parent asset name is before task name. + and folder path in the middle. """ path_items = task_path.split("/") - if len(path_items) < 3: - raise InvalidContextError("Invalid path \"{}\"".format(task_path)) task_name = path_items.pop(-1) - asset_name = path_items.pop(-1) project_name = path_items.pop(0) + folder_path = "/" + "/".join(path_items) return self.get_timer_data_for_context( - project_name, asset_name, task_name, self.log + project_name, folder_path, task_name, self.log ) def get_launch_hook_paths(self): @@ -204,40 +206,38 @@ class TimersManager( @staticmethod def get_timer_data_for_context( - project_name, asset_name, task_name, logger=None + project_name, folder_path, task_name, logger=None ): - """Prepare data for timer related callbacks. - - TODO: - - return predefined object that has access to asset document etc. - """ - if not project_name or not asset_name or not task_name: + """Prepare data for timer related callbacks.""" + if not project_name or not folder_path or not task_name: raise InvalidContextError(( "Missing context information got" - " Project: \"{}\" Asset: \"{}\" Task: \"{}\"" - ).format(str(project_name), str(asset_name), str(task_name))) + " Project: \"{}\" Folder: \"{}\" Task: \"{}\"" + ).format(str(project_name), str(folder_path), str(task_name))) - asset_doc = get_asset_by_name( + folder_entity = ayon_api.get_folder_by_path( project_name, - asset_name, - fields=["_id", "name", "data.tasks", "data.parents"] + folder_path, + fields={"id", "name", "path"} ) - if not asset_doc: + if not folder_entity: raise InvalidContextError(( - "Asset \"{}\" not found in project \"{}\"" - ).format(asset_name, project_name)) + "Folder \"{}\" not found in project \"{}\"" + ).format(folder_path, project_name)) - asset_data = asset_doc.get("data") or {} - asset_tasks = asset_data.get("tasks") or {} - if task_name not in asset_tasks: + folder_id = folder_entity["id"] + task_entity = ayon_api.get_task_by_name( + project_name, folder_id, task_name + ) + if not task_entity: raise InvalidContextError(( - "Task \"{}\" not found on asset \"{}\" in project \"{}\"" - ).format(task_name, asset_name, project_name)) + "Task \"{}\" not found on folder \"{}\" in project \"{}\"" + ).format(task_name, folder_path, project_name)) task_type = "" try: - task_type = asset_tasks[task_name]["type"] + task_type = task_entity["taskType"] except KeyError: msg = "Couldn't find task_type for {}".format(task_name) if logger is not None: @@ -245,32 +245,34 @@ class TimersManager( else: print(msg) - hierarchy_items = asset_data.get("parents") or [] - hierarchy_items.append(asset_name) + hierarchy_items = folder_entity["path"].split("/") + hierarchy_items.pop(0) return { "project_name": project_name, - "asset_id": str(asset_doc["_id"]), - "asset_name": asset_name, + "folder_id": folder_id, + "folder_path": folder_entity["path"], "task_name": task_name, "task_type": task_type, - "hierarchy": hierarchy_items + "asset_id": folder_id, + "asset_name": folder_entity["name"], + "hierarchy": hierarchy_items, } - def start_timer(self, project_name, asset_name, task_name): + def start_timer(self, project_name, folder_path, task_name): """Start timer for passed context. Args: - project_name (str): Project name - asset_name (str): Asset name - task_name (str): Task name + project_name (str): Project name. + folder_path (str): Folder path. + task_name (str): Task name. """ data = self.get_timer_data_for_context( - project_name, asset_name, task_name, self.log + project_name, folder_path, task_name, self.log ) self.timer_started(None, data) - def get_task_time(self, project_name, asset_name, task_name): + def get_task_time(self, project_name, folder_path, task_name): """Get total time for passed context. TODO: @@ -281,7 +283,7 @@ class TimersManager( if hasattr(connector, "get_task_time"): module = self._modules_by_id[module_id] times[module.name] = connector.get_task_time( - project_name, asset_name, task_name + project_name, folder_path, task_name ) return times @@ -394,7 +396,7 @@ class TimersManager( @staticmethod def start_timer_with_webserver( - project_name, asset_name, task_name, logger=None + project_name, folder_path, task_name, logger=None ): """Prepared method for calling change timers on REST api. @@ -403,7 +405,7 @@ class TimersManager( Args: project_name (str): Project name. - asset_name (str): Asset name. + folder_path (str): Folder path. task_name (str): Task name. logger (logging.Logger): Logger object. Using 'print' if not passed. @@ -430,7 +432,7 @@ class TimersManager( return data = { "project_name": project_name, - "folder_path": asset_name, + "folder_path": folder_path, "task_name": task_name } @@ -472,13 +474,13 @@ class TimersManager( def _on_host_task_change(self, event): project_name = event["project_name"] - asset_name = event["folder_path"] + folder_path = event["folder_path"] task_name = event["task_name"] self.log.debug(( "Sending message that timer should change to" - " Project: {} Asset: {} Task: {}" - ).format(project_name, asset_name, task_name)) + " Project: {} Folder: {} Task: {}" + ).format(project_name, folder_path, task_name)) self.start_timer_with_webserver( - project_name, asset_name, task_name, self.log + project_name, folder_path, task_name, self.log ) diff --git a/client/ayon_core/pipeline/anatomy.py b/client/ayon_core/pipeline/anatomy.py index 6ba5175a2a..d6e09bad39 100644 --- a/client/ayon_core/pipeline/anatomy.py +++ b/client/ayon_core/pipeline/anatomy.py @@ -280,13 +280,13 @@ class BaseAnatomy(object): ``` ## Entered filepath - "P:/projects/project/asset/task/animation_v001.ma" + "P:/projects/project/folder/task/animation_v001.ma" ## Entered template "<{}>" ## Output - "/project/asset/task/animation_v001.ma" + "/project/folder/task/animation_v001.ma" Args: filepath (str): Full file path where root should be replaced. diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index 46c4d2d1b4..4fdba96bb6 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -1603,12 +1603,12 @@ class CreateContext: bool: Context changed. """ - project_name, asset_name, task_name, workfile_path = ( + project_name, folder_path, task_name, workfile_path = ( self._get_current_host_context() ) return ( self._current_project_name != project_name - or self._current_folder_path != asset_name + or self._current_folder_path != folder_path or self._current_task_name != task_name or self._current_workfile_path != workfile_path ) @@ -1679,18 +1679,18 @@ class CreateContext: self.refresh_thumbnails() def _get_current_host_context(self): - project_name = asset_name = task_name = workfile_path = None + project_name = folder_path = task_name = workfile_path = None if hasattr(self.host, "get_current_context"): host_context = self.host.get_current_context() if host_context: project_name = host_context.get("project_name") - asset_name = host_context.get("folder_path") + folder_path = host_context.get("folder_path") task_name = host_context.get("task_name") if isinstance(self.host, IWorkfileHost): workfile_path = self.host.get_current_workfile() - return project_name, asset_name, task_name, workfile_path + return project_name, folder_path, task_name, workfile_path def reset_current_context(self): """Refresh current context. diff --git a/client/ayon_core/tools/adobe_webserver/app.py b/client/ayon_core/tools/adobe_webserver/app.py index b10509f484..7d97d7d66d 100644 --- a/client/ayon_core/tools/adobe_webserver/app.py +++ b/client/ayon_core/tools/adobe_webserver/app.py @@ -81,15 +81,19 @@ class WebServerTool: await client.connect() context = get_global_context() - project = context["project_name"] - asset = context["folder_path"] - task = context["task_name"] - log.info("Sending context change to {}-{}-{}".format(project, - asset, - task)) + project_name = context["project_name"] + folder_path = context["folder_path"] + task_name = context["task_name"] + log.info("Sending context change to {}{}/{}".format( + project_name, folder_path, task_name + )) - await client.call('{}.set_context'.format(host), - project=project, asset=asset, task=task) + await client.call( + '{}.set_context'.format(host), + project=project_name, + folder=folder_path, + task=task_name + ) await client.close() def port_occupied(self, host_name, port): diff --git a/client/ayon_core/tools/context_dialog/window.py b/client/ayon_core/tools/context_dialog/window.py index e2c9f71aaa..b145e77515 100644 --- a/client/ayon_core/tools/context_dialog/window.py +++ b/client/ayon_core/tools/context_dialog/window.py @@ -277,8 +277,8 @@ class ContextDialogController: def is_initial_context_valid(self): return self._initial_folder_found and self._initial_project_found - def set_initial_context(self, project_name=None, asset_name=None): - result = self._prepare_initial_context(project_name, asset_name) + def set_initial_context(self, project_name=None, folder_path=None): + result = self._prepare_initial_context(project_name, folder_path) self._initial_project_name = project_name self._initial_folder_id = result["folder_id"] @@ -352,7 +352,7 @@ class ContextDialogController: with open(self._output_path, "w") as stream: json.dump(self.get_selected_context(), stream, indent=4) - def _prepare_initial_context(self, project_name, asset_name): + def _prepare_initial_context(self, project_name, folder_path): project_found = True output = { "project_found": project_found, @@ -362,26 +362,26 @@ class ContextDialogController: "tasks_found": True, } if project_name is None: - asset_name = None + folder_path = None else: project = ayon_api.get_project(project_name) project_found = project is not None output["project_found"] = project_found - if not project_found or not asset_name: + if not project_found or not folder_path: return output - output["folder_label"] = asset_name + output["folder_label"] = folder_path folder_id = None folder_found = False # First try to find by path - folder = ayon_api.get_folder_by_path(project_name, asset_name) + folder = ayon_api.get_folder_by_path(project_name, folder_path) # Try to find by name if folder was not found by path - # - prevent to query by name if 'asset_name' contains '/' - if not folder and "/" not in asset_name: + # - prevent to query by name if 'folder_path' contains '/' + if not folder and "/" not in folder_path: folder = next( ayon_api.get_folders( - project_name, folder_names=[asset_name], fields=["id"]), + project_name, folder_names=[folder_path], fields=["id"]), None ) @@ -496,10 +496,10 @@ class ContextDialog(QtWidgets.QDialog): Context has 3 parts: - Project - - Asset + - Folder - Task - It is possible to predefine project and asset. In that case their widgets + It is possible to predefine project and folder. In that case their widgets will have passed preselected values and will be disabled. """ def __init__(self, controller=None, parent=None): @@ -521,7 +521,7 @@ class ContextDialog(QtWidgets.QDialog): # UI initialization main_splitter = QtWidgets.QSplitter(self) - # Left side widget contains project combobox and asset widget + # Left side widget contains project combobox and folders widget left_side_widget = QtWidgets.QWidget(main_splitter) project_combobox = ProjectsCombobox( @@ -531,7 +531,7 @@ class ContextDialog(QtWidgets.QDialog): ) project_combobox.set_select_item_visible(True) - # Assets widget + # Folders widget folders_widget = FoldersWidget( controller, parent=left_side_widget, @@ -661,11 +661,7 @@ class ContextDialog(QtWidgets.QDialog): self._controller.set_strict(enabled) def refresh(self): - """Refresh all widget one by one. - - When asset refresh is triggered we have to wait when is done so - this method continues with `_on_asset_widget_refresh_finished`. - """ + """Refresh all widget one by one.""" self._controller.reset() @@ -673,10 +669,10 @@ class ContextDialog(QtWidgets.QDialog): """Result of dialog.""" return self._controller.get_selected_context() - def set_context(self, project_name=None, asset_name=None): + def set_context(self, project_name=None, folder_path=None): """Set context which will be used and locked in dialog.""" - self._controller.set_initial_context(project_name, asset_name) + self._controller.set_initial_context(project_name, folder_path) def _on_projects_refresh(self): initial_context = self._controller.get_initial_context() @@ -784,14 +780,14 @@ class ContextDialog(QtWidgets.QDialog): def main( path_to_store, project_name=None, - asset_name=None, + folder_path=None, strict=True ): # Run Qt application app = get_ayon_qt_app() controller = ContextDialogController() controller.set_strict(strict) - controller.set_initial_context(project_name, asset_name) + controller.set_initial_context(project_name, folder_path) controller.set_output_json_path(path_to_store) window = ContextDialog(controller=controller) window.show() diff --git a/client/ayon_core/tools/launcher/models/actions.py b/client/ayon_core/tools/launcher/models/actions.py index 6b9a33e57a..50c3e85b0a 100644 --- a/client/ayon_core/tools/launcher/models/actions.py +++ b/client/ayon_core/tools/launcher/models/actions.py @@ -309,9 +309,11 @@ class ActionsModel: task_name = None task_type = None if task_id is not None: - task = self._controller.get_task_entity(project_name, task_id) - task_name = task["name"] - task_type = task["taskType"] + task_entity = self._controller.get_task_entity( + project_name, task_id + ) + task_name = task_entity["name"] + task_type = task_entity["taskType"] output = should_start_last_workfile( project_name, From 458bb80526dd42892044da471dc9a7253a0f486a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 17:48:46 +0100 Subject: [PATCH 434/573] modified usdlib modified as best as I could --- client/ayon_core/pipeline/usdlib.py | 47 ++++++++++++++--------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/client/ayon_core/pipeline/usdlib.py b/client/ayon_core/pipeline/usdlib.py index 971caa7b87..7d4105f06f 100644 --- a/client/ayon_core/pipeline/usdlib.py +++ b/client/ayon_core/pipeline/usdlib.py @@ -9,7 +9,6 @@ except ImportError: # Allow to fall back on Multiverse 6.3.0+ pxr usd library from mvpxr import Usd, UsdGeom, Sdf, Kind -from ayon_core.client import get_asset_by_name from ayon_core.pipeline import Anatomy, get_current_project_name from ayon_core.pipeline.template_data import get_template_data @@ -120,7 +119,7 @@ def create_shot(filepath, layers, create_layers=False): return filepath -def create_model(filename, asset, variant_subsets): +def create_model(filename, folder_path, variant_subsets): """Create a USD Model file. For each of the variation paths it will payload the path and set its @@ -129,8 +128,8 @@ def create_model(filename, asset, variant_subsets): """ project_name = get_current_project_name() - asset_doc = get_asset_by_name(project_name, asset) - assert asset_doc, "Asset not found: %s" % asset + folder_entity = ayon_api.get_folder_by_path(project_name, folder_path) + assert folder_entity, "Folder not found: %s" % folder_path variants = [] for subset in variant_subsets: @@ -144,7 +143,9 @@ def create_model(filename, asset, variant_subsets): ) path = get_usd_master_path( - asset=asset_doc, product_name=subset, representation="usd" + folder_entity=folder_entity, + product_name=subset, + representation="usd" ) variants.append((variant, path)) @@ -171,7 +172,7 @@ def create_model(filename, asset, variant_subsets): stage.GetRootLayer().Save() -def create_shade(filename, asset, variant_subsets): +def create_shade(filename, folder_path, variant_subsets): """Create a master USD shade file for an asset. For each available model variation this should generate a reference @@ -180,8 +181,8 @@ def create_shade(filename, asset, variant_subsets): """ project_name = get_current_project_name() - asset_doc = get_asset_by_name(project_name, asset) - assert asset_doc, "Asset not found: %s" % asset + folder_entity = ayon_api.get_folder_by_path(project_name, folder_path) + assert folder_entity, "Folder not found: %s" % folder_path variants = [] @@ -197,7 +198,9 @@ def create_shade(filename, asset, variant_subsets): shade_subset = re.sub("^usdModel", "usdShade", subset) path = get_usd_master_path( - asset=asset_doc, product_name=shade_subset, representation="usd" + folder_entity=folder_entity, + product_name=shade_subset, + representation="usd" ) variants.append((variant, path)) @@ -208,7 +211,7 @@ def create_shade(filename, asset, variant_subsets): stage.GetRootLayer().Save() -def create_shade_variation(filename, asset, model_variant, shade_variants): +def create_shade_variation(filename, folder_path, model_variant, shade_variants): """Create the master Shade file for a specific model variant. This should reference all shade variants for the specific model variant. @@ -216,8 +219,8 @@ def create_shade_variation(filename, asset, model_variant, shade_variants): """ project_name = get_current_project_name() - asset_doc = get_asset_by_name(project_name, asset) - assert asset_doc, "Asset not found: %s" % asset + folder_entity = ayon_api.get_folder_by_path(project_name, folder_path) + assert folder_entity, "Folder not found: %s" % folder_path variants = [] for variant in shade_variants: @@ -225,7 +228,9 @@ def create_shade_variation(filename, asset, model_variant, shade_variants): model=model_variant, shade=variant ) path = get_usd_master_path( - asset=asset_doc, product_name=subset, representation="usd" + folder_entity=folder_entity, + product_name=subset, + representation="usd" ) variants.append((variant, path)) @@ -308,14 +313,14 @@ def _create_variants_file( return stage -def get_usd_master_path(asset, product_name, representation): +def get_usd_master_path(folder_entity, product_name, representation): """Get the filepath for a .usd file of a subset. This will return the path to an unversioned master file generated by `usd_master_file.py`. Args: - asset (Union[str, dict]): Folder path or asset document. + folder (Union[str, dict]): Folder path or entity. product_name (str): Product name. representation (str): Representation name. """ @@ -324,13 +329,7 @@ def get_usd_master_path(asset, product_name, representation): project_entity = ayon_api.get_project(project_name) anatomy = Anatomy(project_name, project_entity=project_entity) - if isinstance(asset, dict) and "name" in asset: - # Allow explicitly passing asset document - asset_doc = asset - else: - asset_doc = get_asset_by_name(project_name, asset, fields=["name"]) - - template_data = get_template_data(project_entity, asset_doc) + template_data = get_template_data(project_entity, folder_entity) template_data.update({ "product": { "name": product_name @@ -352,8 +351,8 @@ def get_usd_master_path(asset, product_name, representation): def parse_avalon_uri(uri): - # URI Pattern: avalon://{asset}/{subset}.{ext} - pattern = r"avalon://(?P[^/.]*)/(?P[^/]*)\.(?P.*)" + # URI Pattern: avalon://{folder}/{subset}.{ext} + pattern = r"avalon://(?P[^/.]*)/(?P[^/]*)\.(?P.*)" if uri.startswith("avalon://"): match = re.match(pattern, uri) if match: From 016056e3df47a95f105e6f9b43a30bc284103672 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 17:49:28 +0100 Subject: [PATCH 435/573] usd select folder dialog in houdini is using 'SimpleFoldersWidget' --- client/ayon_core/hosts/houdini/api/usd.py | 96 +++++++++++++++-------- 1 file changed, 65 insertions(+), 31 deletions(-) diff --git a/client/ayon_core/hosts/houdini/api/usd.py b/client/ayon_core/hosts/houdini/api/usd.py index e9c02a0307..443a52bc37 100644 --- a/client/ayon_core/hosts/houdini/api/usd.py +++ b/client/ayon_core/hosts/houdini/api/usd.py @@ -3,12 +3,13 @@ import contextlib import logging +import ayon_api from qtpy import QtWidgets, QtCore, QtGui from ayon_core import style -from ayon_core.client import get_asset_by_name from ayon_core.pipeline import get_current_project_name -from ayon_core.tools.utils.assets_widget import SingleSelectAssetsWidget +from ayon_core.tools.utils import PlaceholderLineEdit, RefreshButton +from ayon_core.tools.ayon_utils.widgets import SimpleFoldersWidget from pxr import Sdf @@ -16,77 +17,110 @@ from pxr import Sdf log = logging.getLogger(__name__) -class SelectAssetDialog(QtWidgets.QWidget): - """Frameless assets dialog to select asset with double click. +class SelectFolderDialog(QtWidgets.QWidget): + """Frameless folders dialog to select folder with double click. Args: - parm: Parameter where selected asset name is set. + parm: Parameter where selected folder path is set. """ def __init__(self, parm): - self.setWindowTitle("Pick Asset") + self.setWindowTitle("Pick Folder") self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Popup) - assets_widget = SingleSelectAssetsWidget(self) - assets_widget.set_project_name(get_current_project_name(), False) + header_widget = QtWidgets.QWidget(self) + + filter_input = PlaceholderLineEdit(header_widget) + filter_input.setPlaceholderText("Filter folders..") + + refresh_btn = RefreshButton(self) + + header_layout = QtWidgets.QHBoxLayout(header_widget) + header_layout.setContentsMargins(0, 0, 0, 0) + header_layout.addWidget(filter_input) + header_layout.addWidget(refresh_btn) + + for widget in ( + refresh_btn, + filter_input, + ): + size_policy = widget.sizePolicy() + size_policy.setVerticalPolicy( + QtWidgets.QSizePolicy.MinimumExpanding) + widget.setSizePolicy(size_policy) + + folders_widget = SimpleFoldersWidget(self) + folders_widget.set_project_name(get_current_project_name()) layout = QtWidgets.QHBoxLayout(self) - layout.addWidget(assets_widget) + layout.addWidget(header_widget, 0) + layout.addWidget(folders_widget, 1) - assets_widget.double_clicked.connect(self._set_parameter) - self._assets_widget = assets_widget + folders_widget.double_clicked.connect(self._set_parameter) + filter_input.textChanged.connect(self._on_filter_change) + refresh_btn.clicked.connect(self._on_refresh_clicked) + + self._folders_widget = folders_widget self._parm = parm + def _on_refresh_clicked(self): + self._folders_widget.refresh() + + def _on_filter_change(self, text): + self._folders_widget.set_name_filter(text) + def _set_parameter(self): - name = self._assets_widget.get_selected_asset_name() - self._parm.set(name) + folder_path = self._folders_widget.get_selected_folder_path() + self._parm.set(folder_path) self.close() def _on_show(self): pos = QtGui.QCursor.pos() - # Select the current asset if there is any + # Select the current folder if there is any select_id = None - name = self._parm.eval() - if name: + folder_path = self._parm.eval() + if folder_path: project_name = get_current_project_name() - db_asset = get_asset_by_name(project_name, name, fields=["_id"]) - if db_asset: - select_id = db_asset["_id"] + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path, fields={"id"} + ) + if folder_entity: + select_id = folder_entity["id"] # Set stylesheet self.setStyleSheet(style.load_stylesheet()) - # Refresh assets (is threaded) - self._assets_widget.refresh() - # Select asset - must be done after refresh + # Refresh folders (is threaded) + self._folders_widget.refresh() + # Select folder - must be done after refresh if select_id is not None: - self._assets_widget.select_asset(select_id) + self._folders_widget.set_selected_folder(select_id) # Show cursor (top right of window) near cursor self.resize(250, 400) self.move(self.mapFromGlobal(pos) - QtCore.QPoint(self.width(), 0)) def showEvent(self, event): - super(SelectAssetDialog, self).showEvent(event) + super(SelectFolderDialog, self).showEvent(event) self._on_show() -def pick_asset(node): - """Show a user interface to select an Asset in the project +def pick_folder(node): + """Show a user interface to select an Folder in the project - When double clicking an asset it will set the Asset value in the - 'asset' parameter. + When double clicking an folder it will set the Folder value in the + 'folderPath' parameter. """ - parm = node.parm("asset_name") + parm = node.parm("folderPath") if not parm: - log.error("Node has no 'asset' parameter: %s", node) + log.error("Node has no 'folderPath' parameter: %s", node) return # Construct a frameless popup so it automatically # closes when clicked outside of it. global tool - tool = SelectAssetDialog(parm) + tool = SelectFolderDialog(parm) tool.show() From e8a5230fc71dc187197385b1fe5129eae969aff2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 17:49:52 +0100 Subject: [PATCH 436/573] removed unused assets_widget.py --- client/ayon_core/tools/utils/assets_widget.py | 694 ------------------ 1 file changed, 694 deletions(-) delete mode 100644 client/ayon_core/tools/utils/assets_widget.py diff --git a/client/ayon_core/tools/utils/assets_widget.py b/client/ayon_core/tools/utils/assets_widget.py deleted file mode 100644 index e7036ff8e8..0000000000 --- a/client/ayon_core/tools/utils/assets_widget.py +++ /dev/null @@ -1,694 +0,0 @@ -import time -import collections - -from qtpy import QtWidgets, QtCore, QtGui -import qtawesome -import ayon_api - -from ayon_core.client import get_assets -from ayon_core.style import ( - get_default_tools_icon_color, - get_default_entity_icon_color, -) -from ayon_core.tools.flickcharm import FlickCharm - -from .views import ( - TreeViewSpinner, - DeselectableTreeView -) -from .widgets import PlaceholderLineEdit -from .models import RecursiveSortFilterProxyModel -from .lib import ( - DynamicQThread, - get_qta_icon_by_name_and_color -) - -ASSET_ID_ROLE = QtCore.Qt.UserRole + 1 -ASSET_NAME_ROLE = QtCore.Qt.UserRole + 2 -ASSET_LABEL_ROLE = QtCore.Qt.UserRole + 3 -ASSET_UNDERLINE_COLORS_ROLE = QtCore.Qt.UserRole + 4 -ASSET_PATH_ROLE = QtCore.Qt.UserRole + 5 - - -def _get_default_asset_icon_name(has_children): - if has_children: - return "fa.folder" - return "fa.folder-o" - - -def _get_asset_icon_color_from_doc(asset_doc): - if asset_doc: - return asset_doc["data"].get("color") - return None - - -def _get_asset_icon_name_from_doc(asset_doc): - if asset_doc: - return asset_doc["data"].get("icon") - return None - - -def _get_asset_icon_color(asset_doc): - icon_color = _get_asset_icon_color_from_doc(asset_doc) - if icon_color: - return icon_color - return get_default_entity_icon_color() - - -def _get_asset_icon_name(asset_doc, has_children=True): - icon_name = _get_asset_icon_name_from_doc(asset_doc) - if icon_name: - return icon_name - return _get_default_asset_icon_name(has_children) - - -def get_asset_icon(asset_doc, has_children=False): - """Get asset icon. - - Deprecated: - This function will be removed in future releases. Use on your own - risk. - - Args: - asset_doc (dict): Asset document. - has_children (Optional[bool]): Asset has children assets. - - Returns: - QIcon: Asset icon. - - """ - 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) - - -class _AssetsView(TreeViewSpinner, DeselectableTreeView): - """Asset items view. - - Adds abilities to deselect, show loading spinner and add flick charm - (scroll by mouse/touchpad click and move). - """ - - def __init__(self, parent=None): - super(_AssetsView, self).__init__(parent) - self.setIndentation(15) - self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) - self.setHeaderHidden(True) - - self._flick_charm_activated = False - self._flick_charm = FlickCharm(parent=self) - self._before_flick_scroll_mode = None - - def activate_flick_charm(self): - if self._flick_charm_activated: - return - self._flick_charm_activated = True - self._before_flick_scroll_mode = self.verticalScrollMode() - self._flick_charm.activateOn(self) - self.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) - - def deactivate_flick_charm(self): - if not self._flick_charm_activated: - return - self._flick_charm_activated = False - self._flick_charm.deactivateFrom(self) - if self._before_flick_scroll_mode is not None: - self.setVerticalScrollMode(self._before_flick_scroll_mode) - - def mousePressEvent(self, event): - index = self.indexAt(event.pos()) - if not index.isValid(): - modifiers = QtWidgets.QApplication.keyboardModifiers() - if modifiers == QtCore.Qt.ShiftModifier: - return - elif modifiers == QtCore.Qt.ControlModifier: - return - - super(_AssetsView, self).mousePressEvent(event) - - def set_loading_state(self, loading, empty): - """Change loading state. - - TODO: Separate into 2 individual methods. - - Args: - loading(bool): Is loading. - empty(bool): Is model empty. - """ - if self.is_loading != loading: - if loading: - self.spinner.repaintNeeded.connect( - self.viewport().update - ) - else: - self.spinner.repaintNeeded.disconnect() - self.viewport().update() - - self.is_loading = loading - self.is_empty = empty - - -class _AssetModel(QtGui.QStandardItemModel): - """A model listing assets 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. - - Asset document may have defined label, icon or icon color. - - Loading of data for model happens in thread which means that refresh - is not sequential. When refresh is triggered it is required to listen for - 'refreshed' signal. - - Args: - parent (QObject): Parent Qt object. - """ - - _doc_fetched = QtCore.Signal() - refreshed = QtCore.Signal(bool) - - # Asset document projection - _asset_projection = { - "name": 1, - "parent": 1, - "data.visualParent": 1, - "data.label": 1, - "data.icon": 1, - "data.color": 1 - } - - def __init__(self, parent=None): - super(_AssetModel, self).__init__(parent=parent) - - self._refreshing = False - self._doc_fetching_thread = None - self._doc_fetching_stop = False - self._doc_payload = [] - - self._doc_fetched.connect(self._on_docs_fetched) - - self._item_ids_with_color = set() - self._items_by_asset_id = {} - - self._project_name = None - self._last_project_name = None - - @property - def refreshing(self): - return self._refreshing - - def get_index_by_asset_id(self, asset_id): - item = self._items_by_asset_id.get(asset_id) - if item is not None: - return item.index() - return QtCore.QModelIndex() - - def get_indexes_by_asset_ids(self, asset_ids): - return [ - self.get_index_by_asset_id(asset_id) - for asset_id in asset_ids - ] - - def get_index_by_asset_name(self, asset_name): - indexes = self.get_indexes_by_asset_names([asset_name]) - for index in indexes: - if index.isValid(): - return index - return indexes[0] - - def get_indexes_by_asset_names(self, asset_names): - asset_ids_by_name = { - asset_name: None - for asset_name in asset_names - } - - for asset_id, item in self._items_by_asset_id.items(): - asset_name = item.data(ASSET_NAME_ROLE) - if asset_name in asset_ids_by_name: - asset_ids_by_name[asset_name] = asset_id - - asset_ids = [ - asset_ids_by_name[asset_name] - for asset_name in asset_names - ] - - return self.get_indexes_by_asset_ids(asset_ids) - - def get_project_name(self): - return self._project_name - - def set_project_name(self, project_name, refresh): - if self._project_name == project_name: - return - self._project_name = project_name - if refresh: - self.refresh() - - def refresh(self, force=False): - """Refresh the data for the model. - - Args: - force (bool): Stop currently running refresh start new refresh. - """ - # Skip fetch if there is already other thread fetching documents - if self._refreshing: - if not force: - return - self.stop_refresh() - - project_name = self._project_name - clear_model = False - if project_name != self._last_project_name: - clear_model = True - self._last_project_name = project_name - - if clear_model: - self._clear_items() - - # Fetch documents from mongo - # Restart payload - self._refreshing = True - self._doc_payload = [] - self._doc_fetching_thread = DynamicQThread(self._threaded_fetch) - self._doc_fetching_thread.start() - - def stop_refresh(self): - self._stop_fetch_thread() - - def _clear_items(self): - root_item = self.invisibleRootItem() - root_item.removeRows(0, root_item.rowCount()) - self._items_by_asset_id = {} - self._item_ids_with_color = set() - - def _on_docs_fetched(self): - # Make sure refreshing did not change - # - since this line is refreshing sequential and - # triggering of new refresh will happen when this method is done - if not self._refreshing: - self._clear_items() - return - - self._fill_assets(self._doc_payload) - - self.refreshed.emit(bool(self._items_by_asset_id)) - - self._stop_fetch_thread() - - def _fill_assets(self, asset_docs): - # Collect asset documents as needed - asset_ids = set() - asset_docs_by_id = {} - asset_ids_by_parents = collections.defaultdict(set) - for asset_doc in asset_docs: - asset_id = asset_doc["_id"] - asset_data = asset_doc.get("data") or {} - parent_id = asset_data.get("visualParent") - asset_ids.add(asset_id) - asset_docs_by_id[asset_id] = asset_doc - asset_ids_by_parents[parent_id].add(asset_id) - - # Prepare removed asset ids - removed_asset_ids = ( - set(self._items_by_asset_id.keys()) - set(asset_docs_by_id.keys()) - ) - - # Prepare queue for adding new items - asset_items_queue = collections.deque() - - # Queue starts with root item and 'visualParent' None - root_item = self.invisibleRootItem() - asset_items_queue.append((None, root_item)) - - while asset_items_queue: - # Get item from queue - parent_id, parent_item = asset_items_queue.popleft() - # Skip if there are no children - children_ids = asset_ids_by_parents[parent_id] - - # Go through current children of parent item - # - find out items that were deleted and skip creation of already - # existing items - for row in reversed(range(parent_item.rowCount())): - child_item = parent_item.child(row, 0) - asset_id = child_item.data(ASSET_ID_ROLE) - # Remove item that is not available - if asset_id not in children_ids: - if asset_id in removed_asset_ids: - # Remove and destroy row - parent_item.removeRow(row) - else: - # Just take the row from parent without destroying - parent_item.takeRow(row) - continue - - # Remove asset id from `children_ids` set - # - is used as set for creation of "new items" - children_ids.remove(asset_id) - # Add existing children to queue - asset_items_queue.append((asset_id, child_item)) - - new_items = [] - for asset_id in children_ids: - # Look for item in cache (maybe parent changed) - item = self._items_by_asset_id.get(asset_id) - # Create new item if was not found - if item is None: - item = QtGui.QStandardItem() - item.setEditable(False) - item.setData(asset_id, ASSET_ID_ROLE) - self._items_by_asset_id[asset_id] = item - new_items.append(item) - # Add item to queue - asset_items_queue.append((asset_id, item)) - - if new_items: - parent_item.appendRows(new_items) - - # Remove cache of removed items - for asset_id in removed_asset_ids: - self._items_by_asset_id.pop(asset_id) - - # Refresh data - # - all items refresh all data except id - for asset_id, item in self._items_by_asset_id.items(): - asset_doc = asset_docs_by_id[asset_id] - - asset_name = asset_doc["name"] - if item.data(ASSET_NAME_ROLE) != asset_name: - item.setData(asset_name, ASSET_NAME_ROLE) - - asset_data = asset_doc.get("data") or {} - asset_label = asset_data.get("label") or asset_name - if item.data(ASSET_LABEL_ROLE) != asset_label: - item.setData(asset_label, QtCore.Qt.DisplayRole) - item.setData(asset_label, ASSET_LABEL_ROLE) - - has_children = item.rowCount() > 0 - icon = get_asset_icon(asset_doc, has_children) - item.setData(icon, QtCore.Qt.DecorationRole) - - def _threaded_fetch(self): - asset_docs = self._fetch_asset_docs() - if not self._refreshing: - return - - self._doc_payload = asset_docs - - # Emit doc fetched only if was not stopped - self._doc_fetched.emit() - - def _fetch_asset_docs(self): - project_name = self.get_project_name() - if not project_name: - return [] - - project_entity = ayon_api.get_project(project_name, fields=["name"]) - if not project_entity: - return [] - - # Get all assets sorted by name - return list( - get_assets(project_name, fields=self._asset_projection.keys()) - ) - - def _stop_fetch_thread(self): - self._refreshing = False - if self._doc_fetching_thread is not None: - while self._doc_fetching_thread.isRunning(): - time.sleep(0.01) - self._doc_fetching_thread = None - - -class _AssetsWidget(QtWidgets.QWidget): - """Base widget to display a tree of assets with filter. - - Assets have only one column and are sorted by name. - - Refreshing of assets happens in thread so calling 'refresh' method - is not sequential. To capture moment when refreshing is finished listen - to 'refreshed' signal. - - To capture selection changes listen to 'selection_changed' signal. It won't - send any information about new selection as it may be different based on - inheritance changes. - - Args: - parent (QWidget): Parent Qt widget. - """ - - # on model refresh - refresh_triggered = QtCore.Signal() - refreshed = QtCore.Signal() - # on view selection change - selection_changed = QtCore.Signal() - # It was double clicked on view - double_clicked = QtCore.Signal() - - def __init__(self, parent=None): - super(_AssetsWidget, self).__init__(parent=parent) - - # Tree View - model = self._create_source_model() - proxy = self._create_proxy_model(model) - - view = _AssetsView(self) - view.setModel(proxy) - - header_widget = QtWidgets.QWidget(self) - - current_asset_icon = qtawesome.icon( - "fa.arrow-down", color=get_default_tools_icon_color() - ) - current_asset_btn = QtWidgets.QPushButton(header_widget) - current_asset_btn.setIcon(current_asset_icon) - current_asset_btn.setToolTip("Go to Asset from current Session") - # Hide by default - current_asset_btn.setVisible(False) - - refresh_icon = qtawesome.icon( - "fa.refresh", color=get_default_tools_icon_color() - ) - refresh_btn = QtWidgets.QPushButton(header_widget) - refresh_btn.setIcon(refresh_icon) - refresh_btn.setToolTip("Refresh items") - - filter_input = PlaceholderLineEdit(header_widget) - filter_input.setPlaceholderText("Filter folders..") - - # Header - header_layout = QtWidgets.QHBoxLayout(header_widget) - header_layout.setContentsMargins(0, 0, 0, 0) - header_layout.addWidget(filter_input) - header_layout.addWidget(current_asset_btn) - header_layout.addWidget(refresh_btn) - - # Make header widgets expand vertically if there is a place - for widget in ( - current_asset_btn, - refresh_btn, - filter_input, - ): - size_policy = widget.sizePolicy() - size_policy.setVerticalPolicy( - QtWidgets.QSizePolicy.MinimumExpanding) - widget.setSizePolicy(size_policy) - - # Layout - layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.addWidget(header_widget, 0) - layout.addWidget(view, 1) - - # Signals/Slots - filter_input.textChanged.connect(self._on_filter_text_change) - - selection_model = view.selectionModel() - selection_model.selectionChanged.connect(self._on_selection_change) - refresh_btn.clicked.connect(self.refresh) - current_asset_btn.clicked.connect(self._on_current_asset_click) - view.doubleClicked.connect(self.double_clicked) - - self._header_widget = header_widget - self._filter_input = filter_input - self._refresh_btn = refresh_btn - self._current_asset_btn = current_asset_btn - self._model = model - self._proxy = proxy - self._view = view - - self._last_btns_height = None - - self._current_folder_path = None - - self.model_selection = {} - - @property - def header_widget(self): - return self._header_widget - - def get_project_name(self): - self._model.get_project_name() - - def set_project_name(self, project_name, refresh=True): - self._model.set_project_name(project_name, refresh) - - def set_current_asset_name(self, asset_name): - self._current_folder_path = asset_name - - def _create_source_model(self): - model = _AssetModel(parent=self) - model.refreshed.connect(self._on_model_refresh) - return model - - def _create_proxy_model(self, source_model): - proxy = RecursiveSortFilterProxyModel() - proxy.setSourceModel(source_model) - proxy.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) - proxy.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive) - return proxy - - @property - def refreshing(self): - return self._model.refreshing - - def refresh(self): - self._refresh_model() - - def stop_refresh(self): - self._model.stop_refresh() - - def _get_current_folder_path(self): - return self._current_folder_path - - def _on_current_asset_click(self): - """Trigger change of asset to current context asset. - This separation gives ability to override this method and use it - in differnt way. - """ - - self.select_current_asset() - - def select_current_asset(self): - asset_name = self._get_current_folder_path() - if asset_name: - self.select_asset_by_name(asset_name) - - def set_refresh_btn_visibility(self, visible=None): - """Hide set refresh button. - Some tools may have their global refresh button or do not support - refresh at all. - """ - - if visible is None: - visible = not self._refresh_btn.isVisible() - self._refresh_btn.setVisible(visible) - - def set_current_asset_btn_visibility(self, visible=None): - """Hide set current asset button. - - Not all tools support using of current context asset. - """ - - if visible is None: - visible = not self._current_asset_btn.isVisible() - self._current_asset_btn.setVisible(visible) - - def select_asset(self, asset_id): - index = self._model.get_index_by_asset_id(asset_id) - new_index = self._proxy.mapFromSource(index) - self._select_indexes([new_index]) - - def select_asset_by_name(self, asset_name): - index = self._model.get_index_by_asset_name(asset_name) - new_index = self._proxy.mapFromSource(index) - self._select_indexes([new_index]) - - def activate_flick_charm(self): - self._view.activate_flick_charm() - - def deactivate_flick_charm(self): - self._view.deactivate_flick_charm() - - def _on_selection_change(self): - self.selection_changed.emit() - - def _on_filter_text_change(self, new_text): - self._proxy.setFilterFixedString(new_text) - - def _on_model_refresh(self, has_item): - """This method should be triggered on model refresh. - - Default implementation register this callback in '_create_source_model' - so if you're modifying model keep in mind that this method should be - called when refresh is done. - """ - - self._proxy.sort(0) - self._set_loading_state(loading=False, empty=not has_item) - self.refreshed.emit() - - def _refresh_model(self): - # Store selection - self._set_loading_state(loading=True, empty=True) - - # Trigger signal before refresh is called - self.refresh_triggered.emit() - # Refresh model - self._model.refresh() - - def _set_loading_state(self, loading, empty): - self._view.set_loading_state(loading, empty) - - def _clear_selection(self): - selection_model = self._view.selectionModel() - selection_model.clearSelection() - - def _select_indexes(self, indexes): - valid_indexes = [ - index - for index in indexes - if index.isValid() - ] - if not valid_indexes: - return - - selection_model = self._view.selectionModel() - selection_model.clearSelection() - - mode = ( - QtCore.QItemSelectionModel.Select - | QtCore.QItemSelectionModel.Rows - ) - for index in valid_indexes: - self._view.expand(self._proxy.parent(index)) - selection_model.select(index, mode) - self._view.setCurrentIndex(valid_indexes[0]) - - -class SingleSelectAssetsWidget(_AssetsWidget): - """Single selection asset widget. - - Contain single selection specific api methods. - - Deprecated: - This widget will be removed soon. Please do not use it in new code. - """ - - def get_selected_asset_id(self): - """Currently selected asset id.""" - selection_model = self._view.selectionModel() - indexes = selection_model.selectedRows() - for index in indexes: - return index.data(ASSET_ID_ROLE) - return None - - def get_selected_asset_name(self): - """Currently selected asset name.""" - selection_model = self._view.selectionModel() - indexes = selection_model.selectedRows() - for index in indexes: - return index.data(ASSET_NAME_ROLE) - return None From 6afd863fd9d1840fd78fe7cc2c949e2b4ed245fd Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 18:12:09 +0100 Subject: [PATCH 437/573] rename 'SubsetConvertorPlugin' to 'ProductConvertorPlugin' --- .../blender/plugins/create/convert_legacy.py | 4 ++-- .../houdini/plugins/create/convert_legacy.py | 4 ++-- .../hosts/maya/plugins/create/convert_legacy.py | 4 ++-- .../hosts/nuke/plugins/create/convert_legacy.py | 4 ++-- .../tvpaint/plugins/create/convert_legacy.py | 4 ++-- .../ayon_core/pipeline/create/creator_plugins.py | 16 ++++++++-------- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/client/ayon_core/hosts/blender/plugins/create/convert_legacy.py b/client/ayon_core/hosts/blender/plugins/create/convert_legacy.py index 65a5a4a9b6..613574eee0 100644 --- a/client/ayon_core/hosts/blender/plugins/create/convert_legacy.py +++ b/client/ayon_core/hosts/blender/plugins/create/convert_legacy.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- """Converter for legacy Houdini products.""" -from ayon_core.pipeline.create.creator_plugins import SubsetConvertorPlugin +from ayon_core.pipeline.create.creator_plugins import ProductConvertorPlugin from ayon_core.hosts.blender.api.lib import imprint -class BlenderLegacyConvertor(SubsetConvertorPlugin): +class BlenderLegacyConvertor(ProductConvertorPlugin): """Find and convert any legacy products in the scene. This Converter will find all legacy products in the scene and will diff --git a/client/ayon_core/hosts/houdini/plugins/create/convert_legacy.py b/client/ayon_core/hosts/houdini/plugins/create/convert_legacy.py index 008187d9c8..1a4761172a 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/convert_legacy.py +++ b/client/ayon_core/hosts/houdini/plugins/create/convert_legacy.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- """Converter for legacy Houdini products.""" -from ayon_core.pipeline.create.creator_plugins import SubsetConvertorPlugin +from ayon_core.pipeline.create.creator_plugins import ProductConvertorPlugin from ayon_core.hosts.houdini.api.lib import imprint -class HoudiniLegacyConvertor(SubsetConvertorPlugin): +class HoudiniLegacyConvertor(ProductConvertorPlugin): """Find and convert any legacy products in the scene. This Converter will find all legacy products in the scene and will diff --git a/client/ayon_core/hosts/maya/plugins/create/convert_legacy.py b/client/ayon_core/hosts/maya/plugins/create/convert_legacy.py index 9622504244..9907b5f340 100644 --- a/client/ayon_core/hosts/maya/plugins/create/convert_legacy.py +++ b/client/ayon_core/hosts/maya/plugins/create/convert_legacy.py @@ -1,6 +1,6 @@ import ayon_api -from ayon_core.pipeline.create.creator_plugins import SubsetConvertorPlugin +from ayon_core.pipeline.create.creator_plugins import ProductConvertorPlugin from ayon_core.hosts.maya.api import plugin from ayon_core.hosts.maya.api.lib import read @@ -8,7 +8,7 @@ from maya import cmds from maya.app.renderSetup.model import renderSetup -class MayaLegacyConvertor(SubsetConvertorPlugin, +class MayaLegacyConvertor(ProductConvertorPlugin, plugin.MayaCreatorBase): """Find and convert any legacy products in the scene. diff --git a/client/ayon_core/hosts/nuke/plugins/create/convert_legacy.py b/client/ayon_core/hosts/nuke/plugins/create/convert_legacy.py index f113bec887..8fb8abfbbf 100644 --- a/client/ayon_core/hosts/nuke/plugins/create/convert_legacy.py +++ b/client/ayon_core/hosts/nuke/plugins/create/convert_legacy.py @@ -1,5 +1,5 @@ from ayon_core.pipeline import AYON_INSTANCE_ID, AVALON_INSTANCE_ID -from ayon_core.pipeline.create.creator_plugins import SubsetConvertorPlugin +from ayon_core.pipeline.create.creator_plugins import ProductConvertorPlugin from ayon_core.hosts.nuke.api.lib import ( INSTANCE_DATA_KNOB, get_node_data, @@ -11,7 +11,7 @@ from ayon_core.hosts.nuke.api.plugin import convert_to_valid_instaces import nuke -class LegacyConverted(SubsetConvertorPlugin): +class LegacyConverted(ProductConvertorPlugin): identifier = "legacy.converter" def find_instances(self): diff --git a/client/ayon_core/hosts/tvpaint/plugins/create/convert_legacy.py b/client/ayon_core/hosts/tvpaint/plugins/create/convert_legacy.py index 1415adac2b..34fe0ce8f4 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/create/convert_legacy.py +++ b/client/ayon_core/hosts/tvpaint/plugins/create/convert_legacy.py @@ -1,14 +1,14 @@ import collections from ayon_core.pipeline.create.creator_plugins import ( - SubsetConvertorPlugin, + ProductConvertorPlugin, cache_and_get_instances, ) from ayon_core.hosts.tvpaint.api.plugin import SHARED_DATA_KEY from ayon_core.hosts.tvpaint.api.lib import get_groups_data -class TVPaintLegacyConverted(SubsetConvertorPlugin): +class TVPaintLegacyConverted(ProductConvertorPlugin): """Conversion of legacy instances in scene to new creators. This convertor handles only instances created by core creators. diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index 2c184b5ba5..a984b7affe 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -33,7 +33,7 @@ class CreatorError(Exception): @six.add_metaclass(ABCMeta) -class SubsetConvertorPlugin(object): +class ProductConvertorPlugin(object): """Helper for conversion of instances created using legacy creators. Conversion from legacy creators would mean to loose legacy instances, @@ -804,7 +804,7 @@ def discover_creator_plugins(*args, **kwargs): def discover_convertor_plugins(*args, **kwargs): - return discover(SubsetConvertorPlugin, *args, **kwargs) + return discover(ProductConvertorPlugin, *args, **kwargs) def discover_legacy_creator_plugins(): @@ -863,8 +863,8 @@ def register_creator_plugin(plugin): elif issubclass(plugin, LegacyCreator): register_plugin(LegacyCreator, plugin) - elif issubclass(plugin, SubsetConvertorPlugin): - register_plugin(SubsetConvertorPlugin, plugin) + elif issubclass(plugin, ProductConvertorPlugin): + register_plugin(ProductConvertorPlugin, plugin) def deregister_creator_plugin(plugin): @@ -874,20 +874,20 @@ def deregister_creator_plugin(plugin): elif issubclass(plugin, LegacyCreator): deregister_plugin(LegacyCreator, plugin) - elif issubclass(plugin, SubsetConvertorPlugin): - deregister_plugin(SubsetConvertorPlugin, plugin) + elif issubclass(plugin, ProductConvertorPlugin): + deregister_plugin(ProductConvertorPlugin, plugin) def register_creator_plugin_path(path): register_plugin_path(BaseCreator, path) register_plugin_path(LegacyCreator, path) - register_plugin_path(SubsetConvertorPlugin, path) + register_plugin_path(ProductConvertorPlugin, path) def deregister_creator_plugin_path(path): deregister_plugin_path(BaseCreator, path) deregister_plugin_path(LegacyCreator, path) - deregister_plugin_path(SubsetConvertorPlugin, path) + deregister_plugin_path(ProductConvertorPlugin, path) def cache_and_get_instances(creator, shared_key, list_instances_func): From f5e36197ebdab39f4c2174c986fe36882ac7ce92 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Mar 2024 18:13:06 +0100 Subject: [PATCH 438/573] rename 'SubsetLoaderPlugin' to 'ProductLoaderPlugin' --- client/ayon_core/pipeline/__init__.py | 4 ++-- client/ayon_core/pipeline/load/__init__.py | 4 ++-- client/ayon_core/pipeline/load/plugins.py | 2 +- .../plugins/load/delete_old_versions.py | 2 +- client/ayon_core/plugins/load/delivery.py | 2 +- client/ayon_core/plugins/load/push_to_library.py | 2 +- client/ayon_core/tools/loader/models/actions.py | 16 ++++++++-------- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/client/ayon_core/pipeline/__init__.py b/client/ayon_core/pipeline/__init__.py index d5813286da..d1a181a353 100644 --- a/client/ayon_core/pipeline/__init__.py +++ b/client/ayon_core/pipeline/__init__.py @@ -31,7 +31,7 @@ from .load import ( HeroVersionType, IncompatibleLoaderError, LoaderPlugin, - SubsetLoaderPlugin, + ProductLoaderPlugin, discover_loader_plugins, register_loader_plugin, @@ -136,7 +136,7 @@ __all__ = ( "HeroVersionType", "IncompatibleLoaderError", "LoaderPlugin", - "SubsetLoaderPlugin", + "ProductLoaderPlugin", "discover_loader_plugins", "register_loader_plugin", diff --git a/client/ayon_core/pipeline/load/__init__.py b/client/ayon_core/pipeline/load/__init__.py index ca11b26211..e955fa72dc 100644 --- a/client/ayon_core/pipeline/load/__init__.py +++ b/client/ayon_core/pipeline/load/__init__.py @@ -41,7 +41,7 @@ from .utils import ( from .plugins import ( LoaderPlugin, - SubsetLoaderPlugin, + ProductLoaderPlugin, discover_loader_plugins, register_loader_plugin, @@ -94,7 +94,7 @@ __all__ = ( # plugins.py "LoaderPlugin", - "SubsetLoaderPlugin", + "ProductLoaderPlugin", "discover_loader_plugins", "register_loader_plugin", diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 3b60e357af..90affa3ab2 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -245,7 +245,7 @@ class LoaderPlugin(list): return self._fname -class SubsetLoaderPlugin(LoaderPlugin): +class ProductLoaderPlugin(LoaderPlugin): """Load product into host application Arguments: context (dict): avalon-core:context-1.0 diff --git a/client/ayon_core/plugins/load/delete_old_versions.py b/client/ayon_core/plugins/load/delete_old_versions.py index e0670caee5..74e74b38bd 100644 --- a/client/ayon_core/plugins/load/delete_old_versions.py +++ b/client/ayon_core/plugins/load/delete_old_versions.py @@ -20,7 +20,7 @@ # ) # # -# class DeleteOldVersions(load.SubsetLoaderPlugin): +# class DeleteOldVersions(load.ProductLoaderPlugin): # """Deletes specific number of old version""" # # is_multiple_contexts_compatible = True diff --git a/client/ayon_core/plugins/load/delivery.py b/client/ayon_core/plugins/load/delivery.py index 16f315937b..1de822bb6c 100644 --- a/client/ayon_core/plugins/load/delivery.py +++ b/client/ayon_core/plugins/load/delivery.py @@ -22,7 +22,7 @@ from ayon_core.pipeline.delivery import ( ) -class Delivery(load.SubsetLoaderPlugin): +class Delivery(load.ProductLoaderPlugin): """Export selected versions to folder structure from Template""" is_multiple_contexts_compatible = True diff --git a/client/ayon_core/plugins/load/push_to_library.py b/client/ayon_core/plugins/load/push_to_library.py index 067c652cd9..3fcba3ae7a 100644 --- a/client/ayon_core/plugins/load/push_to_library.py +++ b/client/ayon_core/plugins/load/push_to_library.py @@ -6,7 +6,7 @@ from ayon_core.pipeline import load from ayon_core.pipeline.load import LoadError -class PushToLibraryProject(load.SubsetLoaderPlugin): +class PushToLibraryProject(load.ProductLoaderPlugin): """Export selected versions to folder structure from Template""" is_multiple_contexts_compatible = True diff --git a/client/ayon_core/tools/loader/models/actions.py b/client/ayon_core/tools/loader/models/actions.py index 51a21155c9..7925088b4e 100644 --- a/client/ayon_core/tools/loader/models/actions.py +++ b/client/ayon_core/tools/loader/models/actions.py @@ -14,7 +14,7 @@ from ayon_core.client import ( ) from ayon_core.pipeline.load import ( discover_loader_plugins, - SubsetLoaderPlugin, + ProductLoaderPlugin, filter_repre_contexts_by_loader, get_loader_identifier, load_with_repre_context, @@ -317,7 +317,7 @@ class LoaderActionsModel: we want to show loaders for? Returns: - tuple[list[SubsetLoaderPlugin], list[LoaderPlugin]]: Discovered + tuple[list[ProductLoaderPlugin], list[LoaderPlugin]]: Discovered loader plugins. """ @@ -342,7 +342,7 @@ class LoaderActionsModel: identifier = get_loader_identifier(loader_cls) loaders_by_identifier[identifier] = loader_cls - if issubclass(loader_cls, SubsetLoaderPlugin): + if issubclass(loader_cls, ProductLoaderPlugin): product_loaders.append(loader_cls) else: repre_loaders.append(loader_cls) @@ -404,7 +404,7 @@ class LoaderActionsModel: def _contexts_for_versions(self, project_name, version_ids): """Get contexts for given version ids. - Prepare version contexts for 'SubsetLoaderPlugin' and representation + Prepare version contexts for 'ProductLoaderPlugin' and representation contexts for 'LoaderPlugin' for all children representations of given versions. @@ -483,7 +483,7 @@ class LoaderActionsModel: def _contexts_for_representations(self, project_name, repre_ids): """Get contexts for given representation ids. - Prepare version contexts for 'SubsetLoaderPlugin' and representation + Prepare version contexts for 'ProductLoaderPlugin' and representation contexts for 'LoaderPlugin' for all children representations of given versions. @@ -645,18 +645,18 @@ class LoaderActionsModel: ): """Trigger version loader. - This triggers 'load' method of 'SubsetLoaderPlugin' for given version + This triggers 'load' method of 'ProductLoaderPlugin' for given version ids. Note: - Even when the plugin is 'SubsetLoaderPlugin' it actually expects + Even when the plugin is 'ProductLoaderPlugin' it actually expects versions and should be named 'VersionLoaderPlugin'. Because it is planned to refactor load system and introduce 'LoaderAction' plugins it is not relevant to change it anymore. Args: - loader (SubsetLoaderPlugin): Loader plugin to use. + loader (ProductLoaderPlugin): Loader plugin to use. options (dict): Option values for loader. project_name (str): Project name. version_ids (Iterable[str]): Version ids. From a7ad7211b239040f89467c75dee90841f849aab8 Mon Sep 17 00:00:00 2001 From: murphy Date: Tue, 5 Mar 2024 11:56:47 +0100 Subject: [PATCH 439/573] updating tools defaults --- server_addon/applications/server/tools.json | 160 ++++++++++++++++---- server_addon/applications/server/version.py | 2 +- 2 files changed, 128 insertions(+), 34 deletions(-) diff --git a/server_addon/applications/server/tools.json b/server_addon/applications/server/tools.json index 54bee11cf7..3d8f400200 100644 --- a/server_addon/applications/server/tools.json +++ b/server_addon/applications/server/tools.json @@ -1,55 +1,149 @@ { "tool_groups": [ { - "environment": "{\n \"MTOA\": \"{STUDIO_SOFTWARE}/arnold/mtoa_{MAYA_VERSION}_{MTOA_VERSION}\",\n \"MAYA_RENDER_DESC_PATH\": \"{MTOA}\",\n \"MAYA_MODULE_PATH\": \"{MTOA}\",\n \"ARNOLD_PLUGIN_PATH\": \"{MTOA}/shaders\",\n \"MTOA_EXTENSIONS_PATH\": {\n \"darwin\": \"{MTOA}/extensions\",\n \"linux\": \"{MTOA}/extensions\",\n \"windows\": \"{MTOA}/extensions\"\n },\n \"MTOA_EXTENSIONS\": {\n \"darwin\": \"{MTOA}/extensions\",\n \"linux\": \"{MTOA}/extensions\",\n \"windows\": \"{MTOA}/extensions\"\n },\n \"DYLD_LIBRARY_PATH\": {\n \"darwin\": \"{MTOA}/bin\"\n },\n \"PATH\": {\n \"windows\": \"{PATH};{MTOA}/bin\"\n }\n}", - "name": "mtoa", - "label": "Autodesk Arnold", + "name": "htoa", + "label": "Arnold for Houdini (example)", "variants": [ { + "name": "5-4-2-7", + "label": "", + "host_names": [ + "houdini" + ], + "environment": "{\n \"HTOA_VERSION\": \"5.4.2.7\"\n}", + "app_variants": [] + } + ], + "environment": "{\n \"_comment_\": \"{STUDIO_SW} points to software repository. Can be defined in Core addon globally\",\n\n \"HOUDINI_PATH\": [\n \"{STUDIO_SW}/APP/HTOA/{HTOA_VERSION}/HOUDINI{HOUDINI_VERSION}/WINDOWS/htoa-6.1.3.3_rdb15014_houdini-{HTOA_VERSION}\",\n \"{HOUDINI_PATH}\"\n ],\n \"PATH\": {\n \"windows\": [\n \"{STUDIO_SW}/APP/HTOA/{HTOA_VERSION}/HOUDINI{HOUDINI_VERSION}/WINDOWS/htoa-6.1.3.3_rdb15014_houdini-{HTOA_VERSION}/scripts/bin\",\n \"{PATH}\"\n ]\n }\n}" + }, + { + "name": "mtoa", + "label": "Arnold for Maya (example)", + "variants": [ + { + "name": "5-3-1-0", + "label": "", "host_names": [], - "app_variants": [], - "environment": "{\n \"MTOA_VERSION\": \"3.2\"\n}", - "name": "3-2", - "label": "3.2" + "environment": "{\n \"MTOA_VERSION\": \"5.3.1.0\"\n}", + "app_variants": [] }, { + "name": "5-3-4-1", + "label": "", "host_names": [], - "app_variants": [], - "environment": "{\n \"MTOA_VERSION\": \"3.1\"\n}", - "name": "3-1", - "label": "3.1" + "environment": "{\n \"MTOA_VERSION\": \"5.3.4.1\"\n}", + "app_variants": [] } - ] + ], + "environment": "{\n \"_comment_\": \"{STUDIO_SW} points to software repository. Can be defined in Core addon globally\",\n\n \"MTOA\": {\n \"darwin\": \"{STUDIO_SW}/APP/MTOA/{MTOA_VERSION}/MAYA{MAYA_VERSION}/MAC\",\n \"linux\": \"{STUDIO_SW}/APP/MTOA/{MTOA_VERSION}/MAYA{MAYA_VERSION}/LINUX\",\n \"windows\": \"{STUDIO_SW}/APP/MTOA/{MTOA_VERSION}/MAYA{MAYA_VERSION}/WINDOWS\"\n },\n \"MAYA_MODULE_PATH\": [\n \"{STUDIO_SW}/APP/MTOA\",\n \"{MAYA_MODULE_PATH}\"\n ],\n \"DYLD_LIBRARY_PATH\": {\n \"darwin\": \"{MTOA}/bin\"\n },\n \"PATH\": {\n \"windows\": [\n \"{MTOA}/bin\",\n \"{PATH}\"\n ]\n },\n \"XBMLANGPATH\": [\n \"{MTOA}/icons\",\n \"{XBMLANGPATH}\"\n ],\n \"MAYA_RENDER_DESC_PATH\": [\n \"{MTOA}\",\n \"{MAYA_RENDER_DESC_PATH}\"\n ],\n \"MTOA_STARTUP_LOG_VERBOSITY\": \"3\"\n}" }, { - "environment": "{}", - "name": "vray", - "label": "Chaos Group Vray", - "variants": [] - }, - { - "environment": "{}", - "name": "yeti", - "label": "Peregrine Labs Yeti", - "variants": [] - }, - { - "environment": "{}", - "name": "renderman", - "label": "Pixar Renderman", + "name": "redshiftMaya", + "label": "Redshift for Maya (example)", "variants": [ { + "name": "3-5-23", + "label": "", + "host_names": [], + "environment": "{\n \"REDSHIFT_VERSION\": \"3.5.23\"\n}", + "app_variants": [] + } + ], + "environment": "{\n \"_comment_\": \"{STUDIO_SW} points to software repository. Can be defined in Core addon globally\",\n\n \"REDSHIFT_COREDATAPATH\": {\n \"darwin\": \"{STUDIO_SW}/APP/REDSHIFT/{REDSHIFT_VERSION}/MAC\",\n \"linux\": \"{STUDIO_SW}/APP/REDSHIFT/{REDSHIFT_VERSION}/LINUX\",\n \"windows\": \"{STUDIO_SW}/APP/REDSHIFT/{REDSHIFT_VERSION}/WINDOWS\"\n },\n \"REDSHIFT_ABORTONLICENSEFAIL\": \"0\",\n \"MAYA_MODULE_PATH\": [\n \"{STUDIO_SW}/APP/REDSHIFT\",\n \"{MAYA_MODULE_PATH}\"\n ],\n \"MAYA_PLUG_IN_PATH\": {\n \"windows\": [\n \"{REDSHIFT_COREDATAPATH}/Plugins/Maya/{MAYA_VERSION}/nt-x86-64\",\n \"{MAYA_PLUG_IN_PATH}\"\n ],\n \"linux\": [\n \"{REDSHIFT_COREDATAPATH}/redshift4maya/{MAYA_VERSION}\",\n \"{MAYA_PLUG_IN_PATH}\"\n ],\n \"darwin\": [\n \"{REDSHIFT_COREDATAPATH}/redshift4maya/{MAYA_VERSION}\",\n \"{MAYA_PLUG_IN_PATH}\"\n ]\n },\n \"MAYA_SCRIPT_PATH\": {\n \"windows\": [\n \"{REDSHIFT_COREDATAPATH}/Plugins/Maya/Common/scripts\",\n \"{MAYA_SCRIPT_PATH}\"\n ],\n \"linux\": [\n \"{REDSHIFT_COREDATAPATH}/redshift4maya/common/scripts\",\n \"{MAYA_SCRIPT_PATH}\"\n ],\n \"darwin\": [\n \"{REDSHIFT_COREDATAPATH}/redshift4maya/common/scripts\",\n \"{MAYA_SCRIPT_PATH}\"\n ]\n },\n \"REDSHIFT_PROCEDURALSPATH\": {\n \"windows\": [\n \"{REDSHIFT_COREDATAPATH}/Procedurals\",\n \"{REDSHIFT_PROCEDURALSPATH}\"\n ],\n \"linux\": [\n \"{REDSHIFT_COREDATAPATH}/procedurals\",\n \"{REDSHIFT_PROCEDURALSPATH}\"\n ],\n \"darwin\": [\n \"{REDSHIFT_COREDATAPATH}/procedurals\",\n \"{REDSHIFT_PROCEDURALSPATH}\"\n ]\n },\n \"REDSHIFT_MAYAEXTENSIONSPATH\": {\n \"windows\": [\n \"{REDSHIFT_COREDATAPATH}/Plugins/Maya/{MAYA_VERSION}/nt-x86-64/extensions\",\n \"{REDSHIFT_MAYAEXTENSIONSPATH}\"\n ],\n \"linux\": [\n \"{REDSHIFT_COREDATAPATH}/redshift4maya/{MAYA_VERSION}/extensions\",\n \"{REDSHIFT_MAYAEXTENSIONSPATH}\"\n ],\n \"darwin\": [\n \"{REDSHIFT_COREDATAPATH}/redshift4maya/{MAYA_VERSION}/extensions\",\n \"{REDSHIFT_MAYAEXTENSIONSPATH}\"\n ]\n },\n \"XBMLANGPATH\": {\n \"windows\": [\n \"{REDSHIFT_COREDATAPATH}/Plugins/Maya/Common/icons\",\n \"{XBMLANGPATH}\"\n ],\n \"linux\": [\n \"{REDSHIFT_COREDATAPATH}/redshift4maya/common/icons\",\n \"{XBMLANGPATH}\"\n ],\n \"darwin\": [\n \"{REDSHIFT_COREDATAPATH}/redshift4maya/common/icons\",\n \"{XBMLANGPATH}\"\n ]\n },\n \"MAYA_RENDER_DESC_PATH\": {\n \"windows\": [\n \"{REDSHIFT_COREDATAPATH}/Plugins/Maya/Common/rendererDesc\",\n \"{MAYA_RENDER_DESC_PATH}\"\n ],\n \"linux\": [\n \"{REDSHIFT_COREDATAPATH}/redshift4maya/common/rendererDesc\",\n \"{MAYA_RENDER_DESC_PATH}\"\n ],\n \"darwin\": [\n \"{REDSHIFT_COREDATAPATH}/redshift4maya/common/rendererDesc\",\n \"{MAYA_RENDER_DESC_PATH}\"\n ]\n },\n \"MAYA_CUSTOM_TEMPLATE_PATH\": {\n \"windows\": [\n \"{REDSHIFT_COREDATAPATH}/Plugins/Maya/Common/scripts/NETemplates\",\n \"{MAYA_CUSTOM_TEMPLATE_PATH}\"\n ],\n \"linux\": [\n \"{REDSHIFT_COREDATAPATH}/redshift4maya/common/scripts/NETemplates\",\n \"{MAYA_CUSTOM_TEMPLATE_PATH}\"\n ],\n \"darwin\": [\n \"{REDSHIFT_COREDATAPATH}/redshift4maya/common/scripts/NETemplates\",\n \"{MAYA_CUSTOM_TEMPLATE_PATH}\"\n ]\n },\n \"PATH\": {\n \"windows\": [\n \"{REDSHIFT_COREDATAPATH}/bin\",\n \"{PATH}\"\n ]\n }\n}" + }, + { + "name": "redshift3dsmax", + "label": "Redshift for 3dsmax (example)", + "variants": [ + { + "name": "3-5-19", + "label": "", + "host_names": [ + "max" + ], + "environment": "{\n \"REDSHIFT_VERSION\": \"3.5.19\"\n}", + "app_variants": [] + } + ], + "environment": "{\n \"_comment_\": \"{STUDIO_SW} points to software repository. Can be defined in Core addon globally\",\n\n \"REDSHIFT_COREDATAPATH\": {\n \"darwin\": \"{STUDIO_SW}/APP/REDSHIFT/{REDSHIFT_VERSION}/MAC\",\n \"linux\": \"{STUDIO_SW}/APP/REDSHIFT/{REDSHIFT_VERSION}/LINUX\",\n \"windows\": \"{STUDIO_SW}/APP/REDSHIFT/{REDSHIFT_VERSION}/WINDOWS\"\n },\n \"REDSHIFT_ABORTONLICENSEFAIL\": \"0\",\n \"REDSHIFT_PROCEDURALSPATH\": {\n \"windows\": [\n \"{REDSHIFT_COREDATAPATH}/Procedurals\",\n \"{REDSHIFT_PROCEDURALSPATH}\"\n ],\n \"linux\": [\n \"{REDSHIFT_COREDATAPATH}/procedurals\",\n \"{REDSHIFT_PROCEDURALSPATH}\"\n ],\n \"darwin\": [\n \"{REDSHIFT_COREDATAPATH}/procedurals\",\n \"{REDSHIFT_PROCEDURALSPATH}\"\n ]\n },\n \"PATH\": {\n \"windows\": [\n \"{REDSHIFT_COREDATAPATH}/bin\",\n \"{PATH}\"\n ]\n }\n}" + }, + { + "name": "rendermanMaya", + "label": "Renderman for Maya (example)", + "variants": [ + { + "name": "24-3-maya", + "label": "24.3 RFM", "host_names": [ "maya" ], + "environment": "{\n \"RFMTREE\": {\n \"windows\": \"C:\\\\Program Files\\\\Pixar\\\\RenderManForMaya-24.3\",\n \"darwin\": \"/Applications/Pixar/RenderManForMaya-24.3\",\n \"linux\": \"/opt/pixar/RenderManForMaya-24.3\"\n },\n \"RMANTREE\": {\n \"windows\": \"C:\\\\Program Files\\\\Pixar\\\\RenderManProServer-24.3\",\n \"darwin\": \"/Applications/Pixar/RenderManProServer-24.3\",\n \"linux\": \"/opt/pixar/RenderManProServer-24.3\"\n }\n}", "app_variants": [ "maya/2022" - ], - "environment": "{\n \"RFMTREE\": {\n \"windows\": \"C:\\\\Program Files\\\\Pixar\\\\RenderManForMaya-24.3\",\n \"darwin\": \"/Applications/Pixar/RenderManForMaya-24.3\",\n \"linux\": \"/opt/pixar/RenderManForMaya-24.3\"\n },\n \"RMANTREE\": {\n \"windows\": \"C:\\\\Program Files\\\\Pixar\\\\RenderManProServer-24.3\",\n \"darwin\": \"/Applications/Pixar/RenderManProServer-24.3\",\n \"linux\": \"/opt/pixar/RenderManProServer-24.3\"\n }\n}", - "name": "24-3-maya", - "label": "24.3 RFM" + ] } - ] + ], + "environment": "{\n \"_comment_\": \"{STUDIO_SW} points to software repository. Can be defined in Core addon globally\",\n\n \"RFMTREE\": {\n \"darwin\": \"{STUDIO_SW}/APP/RENDERMAN/{RM_VERSION}/MAC/MAYA\",\n \"linux\": \"{STUDIO_SW}/APP/RENDERMAN/{RM_VERSION}/LINUX/MAYA\",\n \"windows\": \"{STUDIO_SW}/APP/RENDERMAN/{RM_VERSION}/WINDOWS/MAYA\"\n },\n \"RMANTREE\": {\n \"darwin\": \"{STUDIO_SW}/APP/RENDERMAN/{RM_VERSION}/MAC/RenderManProServer-{RM_VERSION}\",\n \"linux\": \"{STUDIO_SW}/APP/RENDERMAN/{RM_VERSION}/LINUX/RenderManProServer-{RM_VERSION}\",\n \"windows\": \"{STUDIO_SW}/APP/RENDERMAN/{RM_VERSION}/WINDOWS/RenderManProServer-{RM_VERSION}\"\n },\n \"MAYA_MODULE_PATH\": [\n \"{STUDIO_SW}/APP/RENDERMAN\",\n \"{MAYA_MODULE_PATH}\"\n ],\n \"PIXAR_LICENSE_FILE\": \"{STUDIO_SW}/APP/RENDERMAN/pixar.license\",\n \"RFM_DO_NOT_CREATE_MODULE_FILE\": \"1\"\n}" + }, + { + "name": "mGear", + "label": "mGear for Maya (example)", + "variants": [ + { + "name": "4-0-7", + "label": "", + "host_names": [], + "environment": "{\n \"MGEAR_VERSION\": \"4.0.7\"\n}", + "app_variants": [] + } + ], + "environment": "{\n \"_comment_\": \"{STUDIO_SW} points to software repository. Can be defined in Core addon globally\",\n\n \"MGEAR_ROOT\": \"{STUDIO_SW}/APP/MGEAR/{MGEAR_VERSION}/MAYA{MAYA_VERSION}/windows/x64\",\n \"MAYA_MODULE_PATH\": [\n \"{STUDIO_SW}/APP/MGEAR/{MGEAR_VERSION}/release\",\n \"{MAYA_MODULE_PATH}\"\n ]\n}" + }, + { + "name": "yetiMaya", + "label": "Yeti for Maya (example)", + "variants": [ + { + "name": "4.2.11", + "label": "", + "host_names": [], + "environment": "{\n \"YETI_VERSION\": \"4.2.11\"\n}", + "app_variants": [] + } + ], + "environment": "{\n \"_comment_\": \"{STUDIO_SW} points to software repository. Can be defined in Core addon globally\",\n\n \"YETI_HOME\": {\n \"darwin\": \"{STUDIO_SW}/APP/YETI/{YETI_VERSION}/MAYA{MAYA_VERSION}/MAC\",\n \"linux\": \"{STUDIO_SW}/APP/YETI/{YETI_VERSION}/MAYA{MAYA_VERSION}/LINUX\",\n \"windows\": \"{STUDIO_SW}/APP/YETI/{YETI_VERSION}/MAYA{MAYA_VERSION}/WINDOWS\"\n },\n \"YETI_TMP\": {\n \"windows\": \"C:/temp\",\n \"darwin\": \"/tmp\",\n \"linux\": \"/tmp\"\n },\n \"peregrinel_LICENSE\": \"4202@35.158.197.250\",\n \"MAYA_MODULE_PATH\": [\n \"{STUDIO_SW}/APP/YETI\",\n \"{MAYA_MODULE_PATH}\"\n ]\n}" + }, + { + "name": "vrayMaya", + "label": "Vray for Maya (example)", + "variants": [ + { + "name": "6.10.01", + "label": "", + "host_names": [ + "" + ], + "environment": "{\n \"VRAY_VERSION\": \"6.10.01\"\n}", + "app_variants": [] + } + ], + "environment": "{\n \"_comment_\": \"{STUDIO_SW} points to software repository. Can be defined in Core addon globally\",\n\n \"MAYA_MODULE_PATH\": {\n \"windows\": [\n \"{STUDIO_SW}/APP/VRAY/{VRAY_VERSION}/MAYA{MAYA_VERSION}/WINDOWS/maya_root/modules\",\n \"{MAYA_MODULE_PATH}\"\n ],\n \"linux\": [\n \"{STUDIO_SW}/APP/VRAY/{VRAY_VERSION}/MAYA{MAYA_VERSION}/LINUX/maya_root/modules\",\n \"{MAYA_MODULE_PATH}\"\n ],\n \"darwin\": [\n \"{STUDIO_SW}/APP/VRAY/{VRAY_VERSION}/MAYA{MAYA_VERSION}/MAC/maya_root/modules\",\n \"{MAYA_MODULE_PATH}\"\n ]\n },\n \"VRAY_AUTH_CLIENT_FILE_PATH\": \"{STUDIO_SW}/APP/VRAY\"\n}" + }, + { + "name": "vraynuke", + "label": "Vray for Nuke (example)", + "variants": [ + { + "name": "5-20-00", + "label": "", + "host_names": [ + "nuke" + ], + "environment": "{\n \"VRAYNUKE_VERSION\": \"5.20.00\"\n}", + "app_variants": [] + } + ], + "environment": "{\n \"_comment_\": \"{STUDIO_SW} points to software repository. Can be defined in Core addon globally\",\n\n \"VRAY_FOR_NUKE_13_0_PLUGINS\": {\n \"windows\": \"{STUDIO_SW}/APP/VRAYNUKE/{VRAYNUKE_VERSION}/NUKE{NUKE_VRAY_VERSION}/WINDOWS/nuke_vray/plugins/vray\"\n },\n \"NUKE_PATH\": {\n \"windows\": [\n \"{STUDIO_SW}/APP/VRAYNUKE/{VRAYNUKE_VERSION}/NUKE{NUKE_VRAY_VERSION}/WINDOWS/nuke_root\",\n \"{NUKE_PATH}\"\n ]\n },\n \"PATH\": {\n \"windows\": [\n \"{STUDIO_SW}/APP/VRAYNUKE/{VRAYNUKE_VERSION}/NUKE{NUKE_VRAY_VERSION}/WINDOWS/nuke_vray\",\n \"{PATH}\"\n ]\n },\n \"VRAY_AUTH_CLIENT_FILE_PATH\": \"{STUDIO_SW}/APP/VRAY\"\n}" } ] -} +} \ No newline at end of file diff --git a/server_addon/applications/server/version.py b/server_addon/applications/server/version.py index 0a8da88258..f1380eede2 100644 --- a/server_addon/applications/server/version.py +++ b/server_addon/applications/server/version.py @@ -1 +1 @@ -__version__ = "0.1.6" +__version__ = "0.1.7" From 630f3258074c2aae2e13dd5a075d25ff760b7fd2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 5 Mar 2024 13:29:04 +0100 Subject: [PATCH 440/573] removed assetbuild logic from hiero --- client/ayon_core/hosts/hiero/api/tags.py | 22 +------ .../collect_assetbuilds.py | 64 ------------------- .../plugins/publish/collect_hierarchy.py | 5 -- 3 files changed, 1 insertion(+), 90 deletions(-) delete mode 100644 client/ayon_core/hosts/hiero/plugins/publish_old_workflow/collect_assetbuilds.py diff --git a/client/ayon_core/hosts/hiero/api/tags.py b/client/ayon_core/hosts/hiero/api/tags.py index 5d13dd98c3..32620aa2f5 100644 --- a/client/ayon_core/hosts/hiero/api/tags.py +++ b/client/ayon_core/hosts/hiero/api/tags.py @@ -1,10 +1,9 @@ import json import re -import os import hiero import ayon_api -from ayon_core.client import get_assets + from ayon_core.lib import Logger from ayon_core.pipeline import get_current_project_name @@ -160,25 +159,6 @@ def add_tags_to_workfile(): } } - # Get project assets. Currently Ftrack specific to differentiate between - # asset builds and shots. - if int(os.getenv("TAG_ASSETBUILD_STARTUP", 0)) == 1: - nks_pres_tags["[AssetBuilds]"] = {} - for asset in get_assets( - project_name, fields=["name", "data.entityType"] - ): - if asset["data"]["entityType"] == "AssetBuild": - nks_pres_tags["[AssetBuilds]"][asset["name"]] = { - "editable": "1", - "note": "", - "icon": { - "path": "icons:TagActor.png" - }, - "metadata": { - "productType": "assetbuild" - } - } - # loop through tag data dict and create deep tag structure for _k, _val in nks_pres_tags.items(): # check if key is not decorated with [] so it is defined as bin diff --git a/client/ayon_core/hosts/hiero/plugins/publish_old_workflow/collect_assetbuilds.py b/client/ayon_core/hosts/hiero/plugins/publish_old_workflow/collect_assetbuilds.py deleted file mode 100644 index 96d471115a..0000000000 --- a/client/ayon_core/hosts/hiero/plugins/publish_old_workflow/collect_assetbuilds.py +++ /dev/null @@ -1,64 +0,0 @@ -from pyblish import api - -from ayon_core.client import get_assets, get_asset_name_identifier - - -class CollectAssetBuilds(api.ContextPlugin): - """Collect asset from tags. - - Tag is expected to have name of the asset and metadata: - { - "productType": "assetbuild" - } - """ - - # Run just after CollectClip - order = api.CollectorOrder + 0.02 - label = "Collect AssetBuilds" - hosts = ["hiero"] - - def process(self, context): - project_name = context.data["projectName"] - asset_builds = {} - for asset_doc in get_assets(project_name): - if asset_doc["data"].get("entityType") != "AssetBuild": - continue - - asset_name = get_asset_name_identifier(asset_doc) - self.log.debug("Found \"{}\" in database.".format(asset_doc)) - asset_builds[asset_name] = asset_doc - - for instance in context: - if instance.data["productType"] != "clip": - continue - - # Exclude non-tagged instances. - tagged = False - asset_names = [] - - for tag in instance.data["tags"]: - t_metadata = dict(tag.metadata()) - t_product_type = t_metadata.get("tag.productType") - if t_product_type is None: - t_product_type = t_metadata.get("tag.family", "") - - if t_product_type.lower() == "assetbuild": - asset_names.append(tag["name"]) - tagged = True - - if not tagged: - self.log.debug( - "Skipping \"{}\" because its not tagged with " - "\"assetbuild\"".format(instance) - ) - continue - - # Collect asset builds. - data = {"assetbuilds": []} - for name in asset_names: - data["assetbuilds"].append(asset_builds[name]) - self.log.debug( - "Found asset builds: {}".format(data["assetbuilds"]) - ) - - instance.data.update(data) diff --git a/client/ayon_core/plugins/publish/collect_hierarchy.py b/client/ayon_core/plugins/publish/collect_hierarchy.py index 3c1d19e827..7387b1865b 100644 --- a/client/ayon_core/plugins/publish/collect_hierarchy.py +++ b/client/ayon_core/plugins/publish/collect_hierarchy.py @@ -41,11 +41,6 @@ class CollectHierarchy(pyblish.api.ContextPlugin): if not instance.data.get("heroTrack"): continue - # get asset build data if any available - shot_data["inputs"] = [ - x["_id"] for x in instance.data.get("assetbuilds", []) - ] - # suppose that all instances are Shots shot_data['entity_type'] = 'Shot' shot_data['tasks'] = instance.data.get("tasks") or {} From dad4bbe9d41cc9a6af50bad4b5ca3a7ad52c7685 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 5 Mar 2024 13:36:24 +0100 Subject: [PATCH 441/573] Update plugin settings title and version number. - Updated a plugin setting title to "Product types" from "Sync workfile versions for families". - Increased the version number from "0.1.9" to "0.1.10". --- server_addon/nuke/server/settings/publish_plugins.py | 2 +- server_addon/nuke/server/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server_addon/nuke/server/settings/publish_plugins.py b/server_addon/nuke/server/settings/publish_plugins.py index 02ee9b3bab..7d9c914fee 100644 --- a/server_addon/nuke/server/settings/publish_plugins.py +++ b/server_addon/nuke/server/settings/publish_plugins.py @@ -56,7 +56,7 @@ class CollectInstanceDataModel(BaseSettingsModel): sync_workfile_version_on_product_types: list[str] = SettingsField( default_factory=list, enum_resolver=nuke_product_types_enum, - title="Sync workfile versions for familes" + title="Product types" ) diff --git a/server_addon/nuke/server/version.py b/server_addon/nuke/server/version.py index c11f861afb..569b1212f7 100644 --- a/server_addon/nuke/server/version.py +++ b/server_addon/nuke/server/version.py @@ -1 +1 @@ -__version__ = "0.1.9" +__version__ = "0.1.10" From e242e8094e292c28f049eacb4eed0dbfd61222db Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 5 Mar 2024 13:57:13 +0100 Subject: [PATCH 442/573] pipeline is using product entities --- client/ayon_core/lib/path_tools.py | 2 +- .../ayon_core/pipeline/create/product_name.py | 2 +- client/ayon_core/pipeline/create/utils.py | 38 +++-- client/ayon_core/pipeline/load/__init__.py | 12 +- client/ayon_core/pipeline/load/plugins.py | 26 ++-- client/ayon_core/pipeline/load/utils.py | 132 +++++++++--------- client/ayon_core/pipeline/usdlib.py | 46 +++--- .../pipeline/workfile/build_workfile.py | 79 +++++------ .../workfile/workfile_template_builder.py | 9 +- 9 files changed, 169 insertions(+), 177 deletions(-) diff --git a/client/ayon_core/lib/path_tools.py b/client/ayon_core/lib/path_tools.py index 74475adfe3..a65f0f8e13 100644 --- a/client/ayon_core/lib/path_tools.py +++ b/client/ayon_core/lib/path_tools.py @@ -78,7 +78,7 @@ def collect_frames(files): files(list) or (set with single value): list of source paths Returns: - dict: {'/folder/subset_v001.0001.png': '0001', ....} + dict: {'/folder/product_v001.0001.png': '0001', ....} """ patterns = [clique.PATTERNS["frames"]] diff --git a/client/ayon_core/pipeline/create/product_name.py b/client/ayon_core/pipeline/create/product_name.py index 7eaa338c91..8976fb2ff0 100644 --- a/client/ayon_core/pipeline/create/product_name.py +++ b/client/ayon_core/pipeline/create/product_name.py @@ -33,7 +33,7 @@ def get_product_name_template( Args: project_name (str): Project on which the context lives. - product_type (str): Product type for which the subset name is + product_type (str): Product type for which the product name is calculated. host_name (str): Name of host in which the product name is calculated. task_name (str): Name of task in which context the product is created. diff --git a/client/ayon_core/pipeline/create/utils.py b/client/ayon_core/pipeline/create/utils.py index 223a30997c..c94fbe5cb0 100644 --- a/client/ayon_core/pipeline/create/utils.py +++ b/client/ayon_core/pipeline/create/utils.py @@ -2,10 +2,7 @@ import collections import ayon_api -from ayon_core.client import ( - get_subsets, - get_last_versions, -) +from ayon_core.client import get_last_versions def get_last_versions_for_instances( @@ -66,35 +63,36 @@ def get_last_versions_for_instances( if not folder_paths_by_id: return output - subset_docs = get_subsets( + product_entities = ayon_api.get_products( project_name, - asset_ids=folder_paths_by_id.keys(), - subset_names=product_names, - fields=["_id", "name", "parent"] + folder_ids=folder_paths_by_id.keys(), + product_names=product_names, + fields={"id", "name", "folderId"} ) - subset_docs_by_id = {} - for subset_doc in subset_docs: - # Filter subset docs by subset names under parent - folder_id = subset_doc["parent"] + product_entities_by_id = {} + for product_entity in product_entities: + # Filter product entities by names under parent + folder_id = product_entity["folderId"] + product_name = product_entity["name"] folder_path = folder_paths_by_id[folder_id] - product_name = subset_doc["name"] if product_name not in product_names_by_folder_path[folder_path]: continue - subset_docs_by_id[subset_doc["_id"]] = subset_doc + product_entities_by_id[product_entity["id"]] = product_entity - if not subset_docs_by_id: + if not product_entities_by_id: return output last_versions_by_product_id = get_last_versions( project_name, - subset_docs_by_id.keys(), + product_entities_by_id.keys(), fields=["name", "parent"] ) - for subset_id, version_doc in last_versions_by_product_id.items(): - subset_doc = subset_docs_by_id[subset_id] - folder_id = subset_doc["parent"] + for product_id, version_doc in last_versions_by_product_id.items(): + product_entity = product_entities_by_id[product_id] + product_name = product_entity["name"] + folder_id = product_entity["folderId"] folder_path = folder_paths_by_id[folder_id] - _instances = instances_by_hierarchy[folder_path][subset_doc["name"]] + _instances = instances_by_hierarchy[folder_path][product_name] for instance in _instances: output[instance.id] = version_doc["name"] diff --git a/client/ayon_core/pipeline/load/__init__.py b/client/ayon_core/pipeline/load/__init__.py index e955fa72dc..0e2768dbe1 100644 --- a/client/ayon_core/pipeline/load/__init__.py +++ b/client/ayon_core/pipeline/load/__init__.py @@ -9,12 +9,12 @@ from .utils import ( get_repres_contexts, get_contexts_for_repre_docs, - get_subset_contexts, + get_product_contexts, get_representation_context, load_with_repre_context, - load_with_subset_context, - load_with_subset_contexts, + load_with_product_context, + load_with_product_contexts, load_container, remove_container, @@ -63,12 +63,12 @@ __all__ = ( "get_repres_contexts", "get_contexts_for_repre_docs", - "get_subset_contexts", + "get_product_contexts", "get_representation_context", "load_with_repre_context", - "load_with_subset_context", - "load_with_subset_contexts", + "load_with_product_context", + "load_with_product_contexts", "load_container", "remove_container", diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 90affa3ab2..797737966d 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -33,7 +33,7 @@ class LoaderPlugin(list): options = [] - log = logging.getLogger("SubsetLoader") + log = logging.getLogger("ProductLoader") log.propagate = True @classmethod @@ -130,10 +130,10 @@ class LoaderPlugin(list): """ plugin_repre_names = cls.get_representations() - plugin_families = cls.families + plugin_product_types = cls.families if ( not plugin_repre_names - or not plugin_families + or not plugin_product_types or not cls.extensions ): return False @@ -152,24 +152,14 @@ class LoaderPlugin(list): if not cls.has_valid_extension(repre_doc): return False - plugin_families = set(plugin_families) - if "*" in plugin_families: + plugin_product_types = set(plugin_product_types) + if "*" in plugin_product_types: return True - subset_doc = context["subset"] - maj_version, _ = schema.get_schema_version(subset_doc["schema"]) - if maj_version < 3: - families = context["version"]["data"].get("families") - else: - families = subset_doc["data"].get("families") - if families is None: - family = subset_doc["data"].get("family") - if family: - families = [family] + product_entity = context["product"] + product_type = product_entity["productType"] - if not families: - return False - return any(family in plugin_families for family in families) + return product_type in plugin_product_types @classmethod def get_representations(cls): diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index f1b467fff5..ceb4038b6e 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -10,8 +10,6 @@ import ayon_api from ayon_core.host import ILoadHost from ayon_core.client import ( - get_subsets, - get_subset_by_id, get_versions, get_version_by_id, get_last_version_by_subset_id, @@ -97,8 +95,8 @@ def get_repres_contexts(representation_ids, project_name=None): Returns: dict: The full representation context by representation id. - keys are repre_id, value is dictionary with full documents of - asset, subset, version and representation. + keys are repre_id, value is dictionary with entities of + folder, product, version and representation. """ from ayon_core.pipeline import get_current_project_name @@ -131,13 +129,13 @@ def get_contexts_for_repre_docs(project_name, repre_docs): version_docs_by_id = {} hero_version_docs = [] versions_for_hero = set() - subset_ids = set() + product_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"]) + product_ids.add(version_doc["parent"]) if versions_for_hero: _version_docs = get_versions(project_name, versions_for_hero) @@ -152,12 +150,14 @@ def get_contexts_for_repre_docs(project_name, repre_docs): version_data = copy.deepcopy(_version_data_by_id[version_id]) version_docs_by_id[hero_version_id]["data"] = version_data - subset_docs = get_subsets(project_name, subset_ids) - subset_docs_by_id = {} + product_entities = ayon_api.get_products( + project_name, product_ids=product_ids + ) + product_entities_by_id = {} folder_ids = set() - for subset_doc in subset_docs: - subset_docs_by_id[subset_doc["_id"]] = subset_doc - folder_ids.add(subset_doc["parent"]) + for product_entity in product_entities: + product_entities_by_id[product_entity["id"]] = product_entity + folder_ids.add(product_entity["folderId"]) folder_entities_by_id = { folder_entity["id"]: folder_entity @@ -170,12 +170,12 @@ def get_contexts_for_repre_docs(project_name, repre_docs): 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"]] - folder_entity = folder_entities_by_id[subset_doc["parent"]] + product_entity = product_entities_by_id[version_doc["parent"]] + folder_entity = folder_entities_by_id[product_entity["folderId"]] context = { "project": project_entity, "folder": folder_entity, - "subset": subset_doc, + "product": product_entity, "version": version_doc, "representation": repre_doc, } @@ -184,13 +184,13 @@ def get_contexts_for_repre_docs(project_name, repre_docs): return contexts -def get_subset_contexts(subset_ids, project_name=None): - """Return parenthood context for subset. +def get_product_contexts(product_ids, project_name=None): + """Return parenthood context for product. - Provides context on subset granularity - less detail than + Provides context on product granularity - less detail than 'get_repre_contexts'. Args: - subset_ids (list): The subset ids. + product_ids (list): The product ids. project_name (Optional[str]): Project name. Returns: dict: The full representation context by representation id. @@ -198,17 +198,19 @@ def get_subset_contexts(subset_ids, project_name=None): from ayon_core.pipeline import get_current_project_name contexts = {} - if not subset_ids: + if not product_ids: return contexts if not project_name: project_name = get_current_project_name() - subset_docs = get_subsets(project_name, subset_ids) - subset_docs_by_id = {} + product_entities = ayon_api.get_products( + project_name, product_ids=product_ids + ) + product_entities_by_id = {} folder_ids = set() - for subset_doc in subset_docs: - subset_docs_by_id[subset_doc["_id"]] = subset_doc - folder_ids.add(subset_doc["parent"]) + for product_entity in product_entities: + product_entities_by_id[product_entity["id"]] = product_entity + folder_ids.add(product_entity["folderId"]) folder_entities_by_id = { folder_entity["id"]: folder_entity @@ -219,14 +221,14 @@ def get_subset_contexts(subset_ids, project_name=None): project_entity = ayon_api.get_project(project_name) - for subset_id, subset_doc in subset_docs_by_id.items(): - folder_entity = folder_entities_by_id[subset_doc["parent"]] + for product_id, product_entity in product_entities_by_id.items(): + folder_entity = folder_entities_by_id[product_entity["folderId"]] context = { "project": project_entity, "folder": folder_entity, - "subset": subset_doc + "product": product_entity } - contexts[subset_id] = context + contexts[product_id] = context return contexts @@ -256,14 +258,14 @@ def get_representation_context(representation): ( version_doc, - subset_doc, + product_entity, folder_entity, project_entity ) = get_representation_parents(project_name, representation) if not version_doc: raise AssertionError("Version was not found in database") - if not subset_doc: - raise AssertionError("Subset was not found in database") + if not product_entity: + raise AssertionError("Product was not found in database") if not folder_entity: raise AssertionError("Folder was not found in database") if not project_entity: @@ -272,7 +274,7 @@ def get_representation_context(representation): context = { "project": project_entity, "folder": folder_entity, - "subset": subset_doc, + "product": product_entity, "version": version_doc, "representation": representation, } @@ -288,7 +290,7 @@ def load_with_repre_context( if not is_compatible_loader(Loader, repre_context): raise IncompatibleLoaderError( "Loader {} is incompatible with {}".format( - Loader.__name__, repre_context["subset"]["name"] + Loader.__name__, repre_context["product"]["name"] ) ) @@ -298,9 +300,9 @@ def load_with_repre_context( assert isinstance(options, dict), "Options must be a dictionary" - # Fallback to subset when name is None + # Fallback to product when name is None if name is None: - name = repre_context["subset"]["name"] + name = repre_context["product"]["name"] log.info( "Running '%s' on '%s'" % ( @@ -318,8 +320,8 @@ def load_with_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 +def load_with_product_context( + Loader, product_context, namespace=None, name=None, options=None, **kwargs ): # Ensure options is a dictionary when no explicit options provided @@ -328,21 +330,21 @@ def load_with_subset_context( assert isinstance(options, dict), "Options must be a dictionary" - # Fallback to subset when name is None + # Fallback to product when name is None if name is None: - name = subset_context["subset"]["name"] + name = product_context["product"]["name"] log.info( "Running '%s' on '%s'" % ( - Loader.__name__, subset_context["folder"]["path"] + Loader.__name__, product_context["folder"]["path"] ) ) - return Loader().load(subset_context, name, namespace, options) + return Loader().load(product_context, name, namespace, options) -def load_with_subset_contexts( - Loader, subset_contexts, namespace=None, name=None, options=None, **kwargs +def load_with_product_contexts( + Loader, product_contexts, namespace=None, name=None, options=None, **kwargs ): # Ensure options is a dictionary when no explicit options provided @@ -351,19 +353,21 @@ def load_with_subset_contexts( 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 + # Fallback to product when name is None + joined_product_names = " | ".join( + context["product"]["name"] + for context in product_contexts ) if name is None: - name = joined_subset_names + name = joined_product_names log.info( - "Running '{}' on '{}'".format(Loader.__name__, joined_subset_names) + "Running '{}' on '{}'".format( + Loader.__name__, joined_product_names + ) ) - return Loader().load(subset_contexts, name, namespace, options) + return Loader().load(product_contexts, name, namespace, options) def load_container( @@ -376,7 +380,7 @@ def load_container( representation (str or 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. + name (str, Optional): The name to assign. Defaults to product name. options (dict, Optional): Additional options to pass on to the loader. Returns: @@ -477,9 +481,11 @@ def update_container(container, version=-1): new_version = get_version_by_name( project_name, version, current_version["parent"], fields=["_id"] ) - subset_doc = get_subset_by_id(project_name, current_version["parent"]) + product_entity = ayon_api.get_product_by_id( + project_name, current_version["parent"] + ) folder_entity = ayon_api.get_folder_by_id( - project_name, subset_doc["parent"] + project_name, product_entity["folderId"] ) assert new_version is not None, "This is a bug" @@ -503,7 +509,7 @@ def update_container(container, version=-1): context = { "project": project_entity, "folder": folder_entity, - "subset": subset_doc, + "product": product_entity, "version": new_version, "representation": new_representation, } @@ -551,7 +557,7 @@ def switch_container(container, representation, loader_plugin=None): if not is_compatible_loader(loader_plugin, new_context): raise IncompatibleLoaderError( "Loader {} is incompatible with {}".format( - loader_plugin.__name__, new_context["subset"]["name"] + loader_plugin.__name__, new_context["product"]["name"] ) ) @@ -849,7 +855,7 @@ def filter_containers(containers, project_name): repre_docs_by_str_id[repre_id] = repre_doc repre_docs_by_version_id[version_id].append(repre_doc) - # Query version docs to get it's subset ids + # Query version docs to get it's product ids # - also query hero version to be able identify if representation # belongs to existing version version_docs = get_versions( @@ -859,29 +865,29 @@ def filter_containers(containers, project_name): fields=["_id", "parent", "type"] ) verisons_by_id = {} - versions_by_subset_id = collections.defaultdict(list) + versions_by_product_id = collections.defaultdict(list) hero_version_ids = set() for version_doc in version_docs: version_id = version_doc["_id"] # Store versions by their ids verisons_by_id[version_id] = version_doc - # There's no need to query subsets for hero versions + # There's no need to query products for hero versions # - they are considered as latest? if version_doc["type"] == "hero_version": hero_version_ids.add(version_id) continue - subset_id = version_doc["parent"] - versions_by_subset_id[subset_id].append(version_doc) + product_id = version_doc["parent"] + versions_by_product_id[product_id].append(version_doc) last_versions = get_last_versions( project_name, - subset_ids=versions_by_subset_id.keys(), + subset_ids=versions_by_product_id.keys(), fields=["_id"] ) # Figure out which versions are outdated outdated_version_ids = set() - for subset_id, last_version_doc in last_versions.items(): - for version_doc in versions_by_subset_id[subset_id]: + for product_id, last_version_doc in last_versions.items(): + for version_doc in versions_by_product_id[product_id]: version_id = version_doc["_id"] if version_id != last_version_doc["_id"]: outdated_version_ids.add(version_id) diff --git a/client/ayon_core/pipeline/usdlib.py b/client/ayon_core/pipeline/usdlib.py index 7d4105f06f..dedcee6f99 100644 --- a/client/ayon_core/pipeline/usdlib.py +++ b/client/ayon_core/pipeline/usdlib.py @@ -119,7 +119,7 @@ def create_shot(filepath, layers, create_layers=False): return filepath -def create_model(filename, folder_path, variant_subsets): +def create_model(filename, folder_path, variant_product_names): """Create a USD Model file. For each of the variation paths it will payload the path and set its @@ -132,19 +132,19 @@ def create_model(filename, folder_path, variant_subsets): assert folder_entity, "Folder not found: %s" % folder_path variants = [] - for subset in variant_subsets: + for product_name in variant_product_names: prefix = "usdModel" - if subset.startswith(prefix): + if product_name.startswith(prefix): # Strip off `usdModel_` - variant = subset[len(prefix):] + variant = product_name[len(prefix):] else: raise ValueError( - "Model subsets must start " "with usdModel: %s" % subset + "Model products must start with usdModel: %s" % product_name ) path = get_usd_master_path( folder_entity=folder_entity, - product_name=subset, + product_name=product_name, representation="usd" ) variants.append((variant, path)) @@ -172,11 +172,11 @@ def create_model(filename, folder_path, variant_subsets): stage.GetRootLayer().Save() -def create_shade(filename, folder_path, variant_subsets): +def create_shade(filename, folder_path, variant_product_names): """Create a master USD shade file for an asset. For each available model variation this should generate a reference - to a `usdShade_{modelVariant}` subset. + to a `usdShade_{modelVariant}` product. """ @@ -186,20 +186,22 @@ def create_shade(filename, folder_path, variant_subsets): variants = [] - for subset in variant_subsets: + for product_name in variant_product_names: prefix = "usdModel" - if subset.startswith(prefix): + if product_name.startswith(prefix): # Strip off `usdModel_` - variant = subset[len(prefix):] + variant = product_name[len(prefix):] else: raise ValueError( - "Model subsets must start " "with usdModel: %s" % subset + "Model products must start " "with usdModel: %s" % product_name ) - shade_subset = re.sub("^usdModel", "usdShade", subset) + shade_product_name = re.sub( + "^usdModel", "usdShade", product_name + ) path = get_usd_master_path( folder_entity=folder_entity, - product_name=shade_subset, + product_name=shade_product_name, representation="usd" ) variants.append((variant, path)) @@ -224,12 +226,12 @@ def create_shade_variation(filename, folder_path, model_variant, shade_variants) variants = [] for variant in shade_variants: - subset = "usdShade_{model}_{shade}".format( + product_name = "usdShade_{model}_{shade}".format( model=model_variant, shade=variant ) path = get_usd_master_path( folder_entity=folder_entity, - product_name=subset, + product_name=product_name, representation="usd" ) variants.append((variant, path)) @@ -314,13 +316,13 @@ def _create_variants_file( def get_usd_master_path(folder_entity, product_name, representation): - """Get the filepath for a .usd file of a subset. + """Get the filepath for a .usd file of a product. This will return the path to an unversioned master file generated by `usd_master_file.py`. Args: - folder (Union[str, dict]): Folder path or entity. + folder_entity (Union[str, dict]): Folder entity. product_name (str): Product name. representation (str): Representation name. """ @@ -343,16 +345,16 @@ def get_usd_master_path(folder_entity, product_name, representation): path = template_obj.format_strict(template_data) # Remove the version folder - subset_folder = os.path.dirname(os.path.dirname(path)) - master_folder = os.path.join(subset_folder, "master") + product_folder = os.path.dirname(os.path.dirname(path)) + master_folder = os.path.join(product_folder, "master") fname = "{0}.{1}".format(product_name, representation) return os.path.join(master_folder, fname).replace("\\", "/") def parse_avalon_uri(uri): - # URI Pattern: avalon://{folder}/{subset}.{ext} - pattern = r"avalon://(?P[^/.]*)/(?P[^/]*)\.(?P.*)" + # URI Pattern: avalon://{folder}/{product}.{ext} + pattern = r"avalon://(?P[^/.]*)/(?P[^/]*)\.(?P.*)" if uri.startswith("avalon://"): match = re.match(pattern, uri) if match: diff --git a/client/ayon_core/pipeline/workfile/build_workfile.py b/client/ayon_core/pipeline/workfile/build_workfile.py index 50273dce9e..ec8681d09f 100644 --- a/client/ayon_core/pipeline/workfile/build_workfile.py +++ b/client/ayon_core/pipeline/workfile/build_workfile.py @@ -16,7 +16,6 @@ import json import ayon_api from ayon_core.client import ( - get_subsets, get_last_versions, get_representations, ) @@ -48,17 +47,11 @@ class BuildWorkfile: return self._log @staticmethod - def map_products_by_type(subset_docs): + def map_products_by_type(product_entities): products_by_type = collections.defaultdict(list) - for subset_doc in subset_docs: - product_type = subset_doc["data"].get("family") - if not product_type: - families = subset_doc["data"].get("families") - if not families: - continue - product_type = families[0] - - products_by_type[product_type].append(subset_doc) + for product_entity in product_entities: + product_type = product_entity["productType"] + products_by_type[product_type].append(product_entity) return products_by_type def process(self): @@ -380,7 +373,7 @@ class BuildWorkfile: project_name, folder_ids=linked_folder_ids )) - def _prepare_profile_for_products(self, subset_docs, profiles): + def _prepare_profile_for_products(self, product_entities, profiles): """Select profile for each product by it's data. Profiles are filtered for each product individually. @@ -391,7 +384,7 @@ class BuildWorkfile: matching profile. Args: - subset_docs (List[Dict[str, Any]]): Subset documents. + product_entities (List[Dict[str, Any]]): product entities. profiles (List[Dict[str, Any]]): Build profiles. Returns: @@ -399,10 +392,10 @@ class BuildWorkfile: """ # Prepare products - products_by_type = self.map_products_by_type(subset_docs) + products_by_type = self.map_products_by_type(product_entities) profiles_by_product_id = {} - for product_type, subset_docs in products_by_type.items(): + for product_type, product_entities in products_by_type.items(): product_type_low = product_type.lower() for profile in profiles: # Skip profile if does not contain product type @@ -418,19 +411,19 @@ class BuildWorkfile: profile_regexes = _profile_regexes # TODO prepare regex compilation - for subset_doc in subset_docs: + for product_entity in product_entities: # Verify regex filtering (optional) if profile_regexes: valid = False for pattern in profile_regexes: - if re.match(pattern, subset_doc["name"]): + if re.match(pattern, product_entity["name"]): valid = True break if not valid: continue - profiles_by_product_id[subset_doc["_id"]] = profile + profiles_by_product_id[product_entity["id"]] = profile # break profiles loop on finding the first matching profile break @@ -473,9 +466,9 @@ class BuildWorkfile: products_by_id = {} version_by_product_id = {} repres_by_version_id = {} - for product_id, in_data in linked_folder_data["subsets"].items(): - subset_doc = in_data["subset_doc"] - products_by_id[subset_doc["_id"]] = subset_doc + for product_id, in_data in linked_folder_data["products"].items(): + product_entity = in_data["product_entity"] + products_by_id[product_entity["id"]] = product_entity version_data = in_data["version"] version_doc = version_data["version_doc"] @@ -514,9 +507,9 @@ class BuildWorkfile: folder_entity["path"] ) for product_id, repres in valid_repres_by_product_id.items(): - subset_doc = products_by_id[product_id] + product_entity = products_by_id[product_id] msg += "\n# Product Name/ID: `{}`/{}".format( - subset_doc["name"], product_id + product_entity["name"], product_id ) for repre in repres: msg += "\n## Repre name: `{}`".format(repre["name"]) @@ -545,13 +538,13 @@ class BuildWorkfile: If product has representation matching representation name each loader is tried to load it until any is successful. If none of them was successful then next representation name is tried. - Subset process loop ends when any representation is loaded or + Product process loop ends when any representation is loaded or all matching representations were already tried. Args: repres_by_product_id (Dict[str, Dict[str, Any]]): Available representations mapped by their parent (product) id. - products_by_id (Dict[str, Dict[str, Any]]): Subset documents + products_by_id (Dict[str, Dict[str, Any]]): Product entities mapped by their id. profiles_by_product_id (Dict[str, Dict[str, Any]]): Build profiles mapped by product id. @@ -570,9 +563,9 @@ class BuildWorkfile: product_ids_ordered = [] for preset in build_presets: for product_type in preset["product_types"]: - for product_id, subset_doc in products_by_id.items(): + for product_id, product_entity in products_by_id.items(): # TODO 'families' is not available on product - families = subset_doc["data"].get("families") or [] + families = product_entity["data"].get("families") or [] if product_type not in families: continue @@ -674,9 +667,9 @@ class BuildWorkfile: { : { "folder_entity": , - "subsets": { + "products": { : { - "subset_doc": , + "product_entity": , "version": { "version_doc": , "repres": [ @@ -689,7 +682,7 @@ class BuildWorkfile: }, ... } - output[folder_id]["subsets"][product_id]["version"]["repres"] + output[folder_id]["products"][product_id]["version"]["repres"] ``` """ @@ -705,16 +698,16 @@ class BuildWorkfile: } project_name = get_current_project_name() - subset_docs = list(get_subsets( - project_name, asset_ids=folder_entities_by_id.keys() + product_entities = list(ayon_api.get_products( + project_name, folder_ids=folder_entities_by_id.keys() )) - subset_docs_by_id = { - subset_doc["_id"]: subset_doc - for subset_doc in subset_docs + product_entities_by_id = { + product_entity["id"]: product_entity + for product_entity in product_entities } last_version_by_product_id = get_last_versions( - project_name, subset_docs_by_id.keys() + project_name, product_entities_by_id.keys() ) last_version_docs_by_id = { version["_id"]: version @@ -729,27 +722,27 @@ class BuildWorkfile: version_doc = last_version_docs_by_id[version_id] product_id = version_doc["parent"] - subset_doc = subset_docs_by_id[product_id] + product_entity = product_entities_by_id[product_id] - folder_id = subset_doc["parent"] + folder_id = product_entity["folderId"] folder_entity = folder_entities_by_id[folder_id] if folder_id not in output: output[folder_id] = { "folder_entity": folder_entity, - "subsets": {} + "products": {} } - if product_id not in output[folder_id]["subsets"]: - output[folder_id]["subsets"][product_id] = { - "subset_doc": subset_doc, + if product_id not in output[folder_id]["products"]: + output[folder_id]["products"][product_id] = { + "product_entity": product_entity, "version": { "version_doc": version_doc, "repres": [] } } - output[folder_id]["subsets"][product_id]["version"]["repres"].append( + output[folder_id]["products"][product_id]["version"]["repres"].append( repre_doc ) diff --git a/client/ayon_core/pipeline/workfile/workfile_template_builder.py b/client/ayon_core/pipeline/workfile/workfile_template_builder.py index a39c5dd31d..07287b45c6 100644 --- a/client/ayon_core/pipeline/workfile/workfile_template_builder.py +++ b/client/ayon_core/pipeline/workfile/workfile_template_builder.py @@ -1577,6 +1577,8 @@ class PlaceholderLoadMixin(object): mapping = {} # TODO use representation context with entities + # - using 'asset', 'subset' and 'version' from context on + # representation is danger for repre_doc in representations: repre_context = repre_doc["context"] @@ -1652,16 +1654,17 @@ class PlaceholderLoadMixin(object): failed = False for repre_load_context in repre_load_contexts.values(): + folder_path = repre_load_context["folder"]["path"] + product_name = repre_load_context["product"]["name"] representation = repre_load_context["representation"] - repre_context = representation["context"] self._before_repre_load( placeholder, representation ) self.log.info( "Loading {} from {} with loader {}\n" "Loader arguments used : {}".format( - repre_context["subset"], - repre_context["asset"], + product_name, + folder_path, loader_name, placeholder.data["loader_args"], ) From 297b22bade186b7ac23c788d20b2c25850c74092 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 5 Mar 2024 13:57:49 +0100 Subject: [PATCH 443/573] push to project using product entities --- .../tools/push_to_project/control.py | 26 +++---- .../tools/push_to_project/models/integrate.py | 75 ++++++++++--------- 2 files changed, 50 insertions(+), 51 deletions(-) diff --git a/client/ayon_core/tools/push_to_project/control.py b/client/ayon_core/tools/push_to_project/control.py index 1e1434c27e..1bc03d2285 100644 --- a/client/ayon_core/tools/push_to_project/control.py +++ b/client/ayon_core/tools/push_to_project/control.py @@ -3,7 +3,6 @@ import threading import ayon_api from ayon_core.client import ( - get_subset_by_id, get_version_by_id, get_representations, ) @@ -35,7 +34,7 @@ class PushToContextController: self._src_version_id = None self._src_folder_entity = None self._src_folder_task_entities = {} - self._src_subset_doc = None + self._src_product_entity = None self._src_version_doc = None self._src_label = None @@ -75,17 +74,19 @@ class PushToContextController: self._src_label = None folder_entity = None task_entities = {} - subset_doc = None + product_entity = None version_doc = None if project_name and version_id: version_doc = get_version_by_id(project_name, version_id) if version_doc: - subset_doc = get_subset_by_id(project_name, version_doc["parent"]) + product_entity = ayon_api.get_product_by_id( + project_name, version_doc["parent"] + ) - if subset_doc: + if product_entity: folder_entity = ayon_api.get_folder_by_id( - project_name, subset_doc["parent"] + project_name, product_entity["folderId"] ) if folder_entity: @@ -98,7 +99,7 @@ class PushToContextController: self._src_folder_entity = folder_entity self._src_folder_task_entities = task_entities - self._src_subset_doc = subset_doc + self._src_product_entity = product_entity self._src_version_doc = version_doc if folder_entity: self._user_values.set_new_folder_name(folder_entity["name"]) @@ -216,12 +217,12 @@ class PushToContextController: return "Source is invalid" folder_path = folder_entity["path"] - subset_doc = self._src_subset_doc + product_entity = self._src_product_entity version_doc = self._src_version_doc return "Source: {}{}/{}/v{:0>3}".format( self._src_project_name, folder_path, - subset_doc["name"], + product_entity["name"], version_doc["name"] ) @@ -265,10 +266,7 @@ class PushToContextController: ) project_settings = get_project_settings(project_name) - subset_doc = self._src_subset_doc - product_type = subset_doc["data"].get("family") - if not product_type: - product_type = subset_doc["data"]["families"][0] + product_type = self._src_product_entity["productType"] template = get_product_name_template( self._src_project_name, product_type, @@ -302,7 +300,7 @@ class PushToContextController: print("Failed format", exc) return "" - product_name = self._src_subset_doc["name"] + product_name = self._src_product_entity["name"] if ( (product_s and not product_name.startswith(product_s)) or (product_e and not product_name.endswith(product_e)) diff --git a/client/ayon_core/tools/push_to_project/models/integrate.py b/client/ayon_core/tools/push_to_project/models/integrate.py index 0c37055a2b..3fe5fa5ea1 100644 --- a/client/ayon_core/tools/push_to_project/models/integrate.py +++ b/client/ayon_core/tools/push_to_project/models/integrate.py @@ -11,8 +11,6 @@ import uuid import ayon_api from ayon_core.client import ( - get_subset_by_id, - get_subset_by_name, get_version_by_id, get_last_version_by_subset_id, get_version_by_name, @@ -20,7 +18,6 @@ from ayon_core.client import ( ) from ayon_core.client.operations import ( OperationsSession, - new_subset_document, new_version_doc, new_representation_doc, prepare_version_update_data, @@ -447,7 +444,7 @@ class ProjectPushItemProcess: self._item = item self._src_folder_entity = None - self._src_subset_doc = None + self._src_product_entity = None self._src_version_doc = None self._src_repre_items = None @@ -455,7 +452,7 @@ class ProjectPushItemProcess: self._anatomy = None self._folder_entity = None self._task_info = None - self._subset_doc = None + self._product_entity = None self._version_doc = None self._product_type = None @@ -494,7 +491,7 @@ class ProjectPushItemProcess: self._determine_product_type() self._determine_publish_template_name() self._determine_product_name() - self._make_sure_subset_exists() + self._make_sure_product_exists() self._make_sure_version_exists() self._log_info("Prerequirements were prepared") self._integrate_representations() @@ -582,15 +579,15 @@ class ProjectPushItemProcess: raise PushToProjectError(self._status.fail_reason) product_id = version_doc["parent"] - subset_doc = get_subset_by_id(src_project_name, product_id) - if not subset_doc: + product_entity = ayon_api.get_product_by_id(src_project_name, product_id) + if not product_entity: self._status.set_failed(( f"Could find product with id \"{product_id}\"" f" in project \"{src_project_name}\"" )) raise PushToProjectError(self._status.fail_reason) - folder_id = subset_doc["parent"] + folder_id = product_entity["folderId"] folder_entity = ayon_api.get_folder_by_id( src_project_name, folder_id, own_attributes=True ) @@ -623,7 +620,7 @@ class ProjectPushItemProcess: raise PushToProjectError(self._status.fail_reason) self._src_folder_entity = folder_entity - self._src_subset_doc = subset_doc + self._src_product_entity = product_entity self._src_version_doc = version_doc self._src_repre_items = repre_items @@ -805,12 +802,8 @@ class ProjectPushItemProcess: self._task_info = task_info def _determine_product_type(self): - subset_doc = self._src_subset_doc - product_type = subset_doc["data"].get("family") - families = subset_doc["data"].get("families") - if not product_type and families: - product_type = families[0] - + product_entity = self._src_product_entity + product_type = product_entity["productType"] if not product_type: self._status.set_failed( "Couldn't figure out product type from source product" @@ -855,24 +848,34 @@ class ProjectPushItemProcess: ) self._product_name = product_name - def _make_sure_subset_exists(self): + def _make_sure_product_exists(self): project_name = self._item.dst_project_name folder_id = self._folder_entity["id"] product_name = self._product_name product_type = self._product_type - subset_doc = get_subset_by_name(project_name, product_name, folder_id) - if subset_doc: - self._subset_doc = subset_doc - return subset_doc - - data = { - "families": [product_type] - } - subset_doc = new_subset_document( - product_name, product_type, folder_id, data + product_entity = ayon_api.get_product_by_name( + project_name, product_name, folder_id ) - self._operations.create_entity(project_name, "subset", subset_doc) - self._subset_doc = subset_doc + if product_entity: + self._product_entity = product_entity + return product_entity + + create_data = { + "name": product_name, + "productType": product_type, + "folderId": folder_id, + } + response = ayon_api.post( + "projects/{}/products".format(project_name), + **create_data + ) + product_entity = ayon_api.get_product_by_id( + project_name, response.data["id"] + ) + # self._operations.create_entity( + # project_name, "product", data + # ) + self._product_entity = product_entity def _make_sure_version_exists(self): """Make sure version document exits in database.""" @@ -880,15 +883,13 @@ class ProjectPushItemProcess: project_name = self._item.dst_project_name version = self._item.dst_version src_version_doc = self._src_version_doc - subset_doc = self._subset_doc - product_id = subset_doc["_id"] + product_entity = self._product_entity + product_id = product_entity["id"] src_data = src_version_doc["data"] - families = subset_doc["data"].get("families") - if not families: - families = [subset_doc["data"]["family"]] + product_type = product_entity["productType"] version_data = { - "families": list(families), + "families": [product_type], "fps": src_data.get("fps"), "source": src_data.get("source"), "machine": socket.gethostname(), @@ -908,8 +909,8 @@ class ProjectPushItemProcess: self.host_name, task_name=self._task_info["name"], task_type=self._task_info["type"], - product_type=families[0], - product_name=subset_doc["name"] + product_type=product_type, + product_name=product_entity["name"] ) existing_version_doc = get_version_by_name( From fd4d499fa10dc13dbc2148fa1592a3b0866f0b4b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 5 Mar 2024 13:59:27 +0100 Subject: [PATCH 444/573] publisher using product entities --- client/ayon_core/tools/publisher/control.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/tools/publisher/control.py b/client/ayon_core/tools/publisher/control.py index 32f3a5f9dc..aaca0fea10 100644 --- a/client/ayon_core/tools/publisher/control.py +++ b/client/ayon_core/tools/publisher/control.py @@ -12,10 +12,8 @@ from abc import ABCMeta, abstractmethod import six import arrow import pyblish.api +import ayon_api -from ayon_core.client import ( - get_subsets, -) from ayon_core.lib.events import QueuedEventSystem from ayon_core.lib.attribute_definitions import ( UIDef, @@ -1775,14 +1773,14 @@ class PublisherController(BasePublisherController): if not folder_item: return None - subset_docs = get_subsets( + product_entities = ayon_api.get_products( project_name, - asset_ids=[folder_item.entity_id], - fields=["name"] + folder_ids={folder_item.entity_id}, + fields={"name"} ) return { - subset_doc["name"] - for subset_doc in subset_docs + product_entity["name"] + for product_entity in product_entities } def reset(self): From 3f50cc601fd31a41126ae94f6cfdb458a7bbf583 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 5 Mar 2024 14:00:27 +0100 Subject: [PATCH 445/573] use product entities in loader --- .../ayon_core/tools/loader/models/actions.py | 103 ++++++++++-------- 1 file changed, 55 insertions(+), 48 deletions(-) diff --git a/client/ayon_core/tools/loader/models/actions.py b/client/ayon_core/tools/loader/models/actions.py index 7925088b4e..d5da2ca80e 100644 --- a/client/ayon_core/tools/loader/models/actions.py +++ b/client/ayon_core/tools/loader/models/actions.py @@ -8,7 +8,6 @@ import uuid import ayon_api from ayon_core.client import ( - get_subsets, get_versions, get_representations, ) @@ -18,8 +17,8 @@ from ayon_core.pipeline.load import ( filter_repre_contexts_by_loader, get_loader_identifier, load_with_repre_context, - load_with_subset_context, - load_with_subset_contexts, + load_with_product_context, + load_with_product_contexts, LoadError, IncompatibleLoaderError, ) @@ -436,10 +435,12 @@ class LoaderActionsModel: version_docs_by_product_id[product_id].append(version_doc) _product_ids = set(version_docs_by_product_id.keys()) - _product_docs = get_subsets(project_name, subset_ids=_product_ids) - product_docs_by_id = {p["_id"]: p for p in _product_docs} + _product_entities = ayon_api.get_products( + project_name, product_ids=_product_ids + ) + product_entities_by_id = {p["id"]: p for p in _product_entities} - _folder_ids = {p["parent"] for p in product_docs_by_id.values()} + _folder_ids = {p["folderId"] for p in product_entities_by_id.values()} _folder_entities = ayon_api.get_folders( project_name, folder_ids=_folder_ids ) @@ -450,13 +451,13 @@ class LoaderActionsModel: for version_doc in version_docs: version_id = version_doc["_id"] product_id = version_doc["parent"] - product_doc = product_docs_by_id[product_id] - folder_id = product_doc["parent"] + product_entity = product_entities_by_id[product_id] + folder_id = product_entity["folderId"] folder_entity = folder_entities_by_id[folder_id] version_context_by_id[version_id] = { "project": project_entity, "folder": folder_entity, - "subset": product_doc, + "product": product_entity, "version": version_doc, } @@ -466,14 +467,14 @@ class LoaderActionsModel: version_id = repre_doc["parent"] version_doc = version_docs_by_id[version_id] product_id = version_doc["parent"] - product_doc = product_docs_by_id[product_id] - folder_id = product_doc["parent"] + product_entity = product_entities_by_id[product_id] + folder_id = product_entity["folderId"] folder_entity = folder_entities_by_id[folder_id] repre_context_by_id[repre_doc["_id"]] = { "project": project_entity, "folder": folder_entity, - "subset": product_doc, + "product": product_entity, "version": version_doc, "representation": repre_doc, } @@ -514,12 +515,14 @@ class LoaderActionsModel: } product_ids = {v["parent"] for v in version_docs_by_id.values()} - product_docs = get_subsets(project_name, subset_ids=product_ids) - product_docs_by_id = { - p["_id"]: p for p in product_docs + product_entities = ayon_api.get_products( + project_name, product_ids=product_ids + ) + product_entities_by_id = { + p["id"]: p for p in product_entities } - folder_ids = {p["parent"] for p in product_docs_by_id.values()} + folder_ids = {p["folderId"] for p in product_entities_by_id.values()} folder_entities = ayon_api.get_folders( project_name, folder_ids=folder_ids ) @@ -529,27 +532,27 @@ class LoaderActionsModel: project_entity = ayon_api.get_project(project_name) - for product_id, product_doc in product_docs_by_id.items(): - folder_id = product_doc["parent"] + for product_id, product_entity in product_entities_by_id.items(): + folder_id = product_entity["folderId"] folder_entity = folder_entities_by_id[folder_id] product_context_by_id[product_id] = { "project": project_entity, "folder": folder_entity, - "subset": product_doc, + "product": product_entity, } for repre_doc in repre_docs: version_id = repre_doc["parent"] version_doc = version_docs_by_id[version_id] product_id = version_doc["parent"] - product_doc = product_docs_by_id[product_id] - folder_id = product_doc["parent"] + product_entity = product_entities_by_id[product_id] + folder_id = product_entity["folderId"] folder_entity = folder_entities_by_id[folder_id] repre_context_by_id[repre_doc["_id"]] = { "project": project_entity, "folder": folder_entity, - "subset": product_doc, + "product": product_entity, "version": version_doc, "representation": repre_doc, } @@ -597,7 +600,7 @@ class LoaderActionsModel: repre_folder_ids = set() for repre_context in filtered_repre_contexts: repre_ids.add(repre_context["representation"]["_id"]) - repre_product_ids.add(repre_context["subset"]["_id"]) + repre_product_ids.add(repre_context["product"]["id"]) repre_version_ids.add(repre_context["version"]["_id"]) repre_folder_ids.add(repre_context["folder"]["id"]) @@ -613,12 +616,12 @@ class LoaderActionsModel: ) action_items.append(item) - # Subset Loaders. + # Product Loaders. version_ids = set(version_context_by_id.keys()) product_folder_ids = set() product_ids = set() for product_context in version_context_by_id.values(): - product_ids.add(product_context["subset"]["_id"]) + product_ids.add(product_context["product"]["id"]) product_folder_ids.add(product_context["folder"]["id"]) version_contexts = list(version_context_by_id.values()) @@ -666,9 +669,11 @@ class LoaderActionsModel: version_docs = self._get_version_docs(project_name, version_ids) product_ids = {v["parent"] for v in version_docs} - product_docs = get_subsets(project_name, subset_ids=product_ids) - product_docs_by_id = {f["_id"]: f for f in product_docs} - folder_ids = {p["parent"] for p in product_docs_by_id.values()} + product_entities = ayon_api.get_products( + project_name, product_ids=product_ids + ) + product_entities_by_id = {p["id"]: p for p in product_entities} + folder_ids = {p["folderId"] for p in product_entities_by_id.values()} folder_entities = ayon_api.get_folders( project_name, folder_ids=folder_ids ) @@ -676,13 +681,13 @@ class LoaderActionsModel: product_contexts = [] for version_doc in version_docs: product_id = version_doc["parent"] - product_doc = product_docs_by_id[product_id] - folder_id = product_doc["parent"] + product_entity = product_entities_by_id[product_id] + folder_id = product_entity["folderId"] folder_entity = folder_entities_by_id[folder_id] product_contexts.append({ "project": project_entity, "folder": folder_entity, - "subset": product_doc, + "product": product_entity, "version": version_doc, }) @@ -718,25 +723,27 @@ class LoaderActionsModel: version_docs = self._get_version_docs(project_name, version_ids) version_docs_by_id = {v["_id"]: v for v in version_docs} product_ids = {v["parent"] for v in version_docs_by_id.values()} - product_docs = get_subsets(project_name, subset_ids=product_ids) - product_docs_by_id = {p["_id"]: p for p in product_docs} - folder_ids = {p["parent"] for p in product_docs_by_id.values()} + product_entities = ayon_api.get_products( + project_name, product_ids=product_ids + ) + product_entities_by_id = {p["id"]: p for p in product_entities} + folder_ids = {p["folderId"] for p in product_entities_by_id.values()} folder_entities = ayon_api.get_folders( project_name, folder_ids=folder_ids ) - folder_entities_by_id = {f["_id"]: f for f in folder_entities} + folder_entities_by_id = {f["id"]: f for f in folder_entities} repre_contexts = [] for repre_doc in repre_docs: version_id = repre_doc["parent"] version_doc = version_docs_by_id[version_id] product_id = version_doc["parent"] - product_doc = product_docs_by_id[product_id] - folder_id = product_doc["parent"] + product_entity = product_entities_by_id[product_id] + folder_id = product_entity["folderId"] folder_entity = folder_entities_by_id[folder_id] repre_contexts.append({ "project": project_entity, "folder": folder_entity, - "subset": product_doc, + "product": product_entity, "version": version_doc, "representation": repre_doc, }) @@ -751,7 +758,7 @@ class LoaderActionsModel: Args: loader (LoaderPlugin): Loader plugin to use. repre_contexts (list[dict]): Full info about selected - representations, containing repre, version, subset, folder and + representations, containing repre, version, product, folder and project documents. options (dict): Data from options. """ @@ -776,7 +783,7 @@ class LoaderActionsModel: "Incompatible Loader", None, repre_context["representation"]["name"], - repre_context["subset"]["name"], + repre_context["product"]["name"], version_name )) @@ -792,20 +799,20 @@ class LoaderActionsModel: str(exc), formatted_traceback, repre_context["representation"]["name"], - repre_context["subset"]["name"], + repre_context["product"]["name"], version_name )) return error_info def _load_products_by_loader(self, loader, version_contexts, options): - """Triggers load with SubsetLoader type of loaders. + """Triggers load with ProductLoader type of loaders. Warning: - Plugin is named 'SubsetLoader' but version is passed to context + Plugin is named 'ProductLoader' but version is passed to context too. Args: - loader (SubsetLoder): Loader used to load. + loader (ProductLoader): Loader used to load. version_contexts (list[dict[str, Any]]): For context for each version. options (dict[str, Any]): Options for loader that user could fill. @@ -815,10 +822,10 @@ class LoaderActionsModel: if loader.is_multiple_contexts_compatible: product_names = [] for context in version_contexts: - product_name = context.get("subset", {}).get("name") or "N/A" + product_name = context.get("product", {}).get("name") or "N/A" product_names.append(product_name) try: - load_with_subset_contexts( + load_with_product_contexts( loader, version_contexts, options=options @@ -841,10 +848,10 @@ class LoaderActionsModel: else: for version_context in version_contexts: product_name = ( - version_context.get("subset", {}).get("name") or "N/A" + version_context.get("product", {}).get("name") or "N/A" ) try: - load_with_subset_context( + load_with_product_context( loader, version_context, options=options From 16ad88e626d5f5f9dc2569058e1511373bba5050 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 5 Mar 2024 14:03:17 +0100 Subject: [PATCH 446/573] creator tool using product entities --- client/ayon_core/tools/creator/window.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/tools/creator/window.py b/client/ayon_core/tools/creator/window.py index a537fe551e..e461e2e2e0 100644 --- a/client/ayon_core/tools/creator/window.py +++ b/client/ayon_core/tools/creator/window.py @@ -5,7 +5,6 @@ import re import ayon_api from qtpy import QtWidgets, QtCore -from ayon_core.client import get_subsets from ayon_core import style from ayon_core.settings import get_current_project_settings from ayon_core.tools.utils.lib import qt_app_context @@ -277,12 +276,12 @@ class CreatorWindow(QtWidgets.QDialog): self._product_name_input.setText(product_name) # Get all products of the current folder - subset_docs = get_subsets( - project_name, asset_ids=[folder_id], fields=["name"] + product_entities = ayon_api.get_products( + project_name, folder_ids={folder_id}, fields={"name"} ) existing_product_names = { - subset_doc["name"] - for subset_doc in subset_docs + product_entity["name"] + for product_entity in product_entities } existing_product_names_low = set( _name.lower() From c6246a91e923a5f3572632093231667991db750f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 5 Mar 2024 14:05:55 +0100 Subject: [PATCH 447/573] use product entity in load plugins and inventory actions --- .../plugins/load/load_background.py | 3 +- .../aftereffects/plugins/load/load_file.py | 4 +-- client/ayon_core/hosts/blender/api/plugin.py | 12 ++++---- .../blender/plugins/load/import_workfile.py | 2 +- .../hosts/blender/plugins/load/load_abc.py | 4 +-- .../hosts/blender/plugins/load/load_action.py | 2 +- .../hosts/blender/plugins/load/load_audio.py | 4 +-- .../hosts/blender/plugins/load/load_blend.py | 6 ++-- .../blender/plugins/load/load_blendscene.py | 6 ++-- .../blender/plugins/load/load_camera_abc.py | 4 +-- .../blender/plugins/load/load_camera_fbx.py | 4 +-- .../hosts/blender/plugins/load/load_fbx.py | 4 +-- .../blender/plugins/load/load_layout_json.py | 4 +-- .../hosts/blender/plugins/load/load_look.py | 4 +-- .../flame/plugins/load/load_clip_batch.py | 10 +++---- .../hosts/harmony/plugins/load/load_audio.py | 4 +-- .../harmony/plugins/load/load_background.py | 2 +- .../plugins/load/load_imagesequence.py | 2 +- .../harmony/plugins/load/load_palette.py | 3 +- .../harmony/plugins/load/load_template.py | 2 +- .../plugins/load/load_template_workfile.py | 2 +- client/ayon_core/hosts/hiero/api/plugin.py | 13 ++++----- client/ayon_core/hosts/maya/api/lib.py | 10 +++---- client/ayon_core/hosts/maya/api/plugin.py | 12 ++++---- .../plugins/inventory/connect_geometry.py | 21 ++++++++------ .../maya/plugins/inventory/connect_xgen.py | 28 +++++++++++-------- .../plugins/inventory/connect_yeti_rig.py | 23 ++++++++++----- .../hosts/nuke/plugins/load/load_clip.py | 3 +- .../hosts/nuke/plugins/load/load_effects.py | 4 +-- .../hosts/nuke/plugins/load/load_image.py | 3 +- .../photoshop/plugins/load/load_image.py | 2 +- .../photoshop/plugins/load/load_reference.py | 2 +- client/ayon_core/hosts/resolve/api/plugin.py | 2 +- .../plugins/load/load_reference_image.py | 2 +- .../plugins/load/load_geometrycache_abc.py | 8 ++---- .../plugins/load/load_skeletalmesh_abc.py | 4 +-- .../plugins/load/load_skeletalmesh_fbx.py | 4 +-- .../plugins/load/load_staticmesh_abc.py | 4 +-- .../plugins/load/load_staticmesh_fbx.py | 4 +-- .../hosts/unreal/plugins/load/load_uasset.py | 4 +-- 40 files changed, 118 insertions(+), 123 deletions(-) diff --git a/client/ayon_core/hosts/aftereffects/plugins/load/load_background.py b/client/ayon_core/hosts/aftereffects/plugins/load/load_background.py index eec69e57c0..4e608efef8 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/load/load_background.py +++ b/client/ayon_core/hosts/aftereffects/plugins/load/load_background.py @@ -60,10 +60,9 @@ class BackgroundLoader(api.AfterEffectsLoader): """ Switch asset or change version """ stub = self.get_stub() folder_name = context["folder"]["name"] - subset_doc = context["subset"] + product_name = context["product"]["name"] repre_doc = context["representation"] - product_name = subset_doc["name"] _ = container.pop("layer") # without iterator number (_001, 002...) diff --git a/client/ayon_core/hosts/aftereffects/plugins/load/load_file.py b/client/ayon_core/hosts/aftereffects/plugins/load/load_file.py index 9f05a55628..4d376f058b 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/load/load_file.py +++ b/client/ayon_core/hosts/aftereffects/plugins/load/load_file.py @@ -73,11 +73,9 @@ class FileLoader(api.AfterEffectsLoader): layer = container.pop("layer") folder_name = context["folder"]["name"] - subset_doc = context["subset"] + product_name = context["product"]["name"] repre_doc = context["representation"] - product_name = subset_doc["name"] - namespace_from_container = re.sub(r'_\d{3}$', '', container["namespace"]) layer_name = "{}_{}".format(folder_name, product_name) diff --git a/client/ayon_core/hosts/blender/api/plugin.py b/client/ayon_core/hosts/blender/api/plugin.py index f8742a8113..6c9bfb6569 100644 --- a/client/ayon_core/hosts/blender/api/plugin.py +++ b/client/ayon_core/hosts/blender/api/plugin.py @@ -220,9 +220,9 @@ class BaseCreator(Creator): Create new instance and store it. Args: - product_name(str): Subset name of created instance. - instance_data(dict): Instance base data. - pre_create_data(dict): Data based on pre creation attributes. + product_name (str): Product name of created instance. + instance_data (dict): Instance base data. + pre_create_data (dict): Data based on pre creation attributes. Those may affect how creator works. """ # Get Instance Container or create it if it does not exist @@ -346,7 +346,7 @@ class BaseCreator(Creator): """Fill instance data with required items. Args: - product_name(str): Subset name of created instance. + product_name(str): Product name of created instance. instance_data(dict): Instance base data. instance_node(bpy.types.ID): Instance node in blender scene. """ @@ -466,7 +466,7 @@ class AssetLoader(LoaderPlugin): assert Path(filepath).exists(), f"{filepath} doesn't exist." folder_name = context["folder"]["name"] - product_name = context["subset"]["name"] + product_name = context["product"]["name"] unique_number = get_unique_number( folder_name, product_name ) @@ -499,7 +499,7 @@ class AssetLoader(LoaderPlugin): # ) # folder_name = context["folder"]["name"] - # product_name = context["subset"]["name"] + # product_name = context["product"]["name"] # instance_name = prepare_scene_name( # folder_name, product_name, unique_number # ) + '_CON' diff --git a/client/ayon_core/hosts/blender/plugins/load/import_workfile.py b/client/ayon_core/hosts/blender/plugins/load/import_workfile.py index d919dff9b3..7144c132e8 100644 --- a/client/ayon_core/hosts/blender/plugins/load/import_workfile.py +++ b/client/ayon_core/hosts/blender/plugins/load/import_workfile.py @@ -5,7 +5,7 @@ from ayon_core.hosts.blender.api import plugin def append_workfile(context, fname, do_import): folder_name = context["folder"]["name"] - product_name = context["subset"]["name"] + product_name = context["product"]["name"] group_name = plugin.prepare_scene_name(folder_name, product_name) diff --git a/client/ayon_core/hosts/blender/plugins/load/load_abc.py b/client/ayon_core/hosts/blender/plugins/load/load_abc.py index 6225d404eb..976697984c 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_abc.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_abc.py @@ -135,7 +135,7 @@ class CacheModelLoader(plugin.AssetLoader): libpath = self.filepath_from_context(context) folder_name = context["folder"]["name"] - product_name = context["subset"]["name"] + product_name = context["product"]["name"] asset_name = plugin.prepare_scene_name(folder_name, product_name) unique_number = plugin.get_unique_number(folder_name, product_name) @@ -161,7 +161,7 @@ class CacheModelLoader(plugin.AssetLoader): self._link_objects(objects, asset_group, containers, asset_group) - product_type = context["subset"]["data"]["family"] + product_type = context["product"]["productType"] asset_group[AVALON_PROPERTY] = { "schema": "openpype:container-2.0", "id": AVALON_CONTAINER_ID, diff --git a/client/ayon_core/hosts/blender/plugins/load/load_action.py b/client/ayon_core/hosts/blender/plugins/load/load_action.py index be61c4f20b..c28040e614 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_action.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_action.py @@ -45,7 +45,7 @@ class BlendActionLoader(plugin.AssetLoader): libpath = self.filepath_from_context(context) folder_name = context["folder"]["name"] - product_name = context["subset"]["name"] + product_name = context["product"]["name"] lib_container = plugin.prepare_scene_name(folder_name, product_name) container_name = plugin.prepare_scene_name( folder_name, product_name, namespace diff --git a/client/ayon_core/hosts/blender/plugins/load/load_audio.py b/client/ayon_core/hosts/blender/plugins/load/load_audio.py index d745c51f5c..d3cafe710e 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_audio.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_audio.py @@ -40,7 +40,7 @@ class AudioLoader(plugin.AssetLoader): """ libpath = self.filepath_from_context(context) folder_name = context["folder"]["name"] - product_name = context["subset"]["name"] + product_name = context["product"]["name"] asset_name = plugin.prepare_scene_name(folder_name, product_name) unique_number = plugin.get_unique_number(folder_name, product_name) @@ -87,7 +87,7 @@ class AudioLoader(plugin.AssetLoader): "libpath": libpath, "asset_name": asset_name, "parent": str(context["representation"]["parent"]), - "productType": context["subset"]["data"]["family"], + "productType": context["product"]["productType"], "objectName": group_name, "audio": audio } diff --git a/client/ayon_core/hosts/blender/plugins/load/load_blend.py b/client/ayon_core/hosts/blender/plugins/load/load_blend.py index acbe6c485d..c8c01be49e 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_blend.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_blend.py @@ -128,10 +128,10 @@ class BlendLoader(plugin.AssetLoader): """ libpath = self.filepath_from_context(context) folder_name = context["folder"]["name"] - product_name = context["subset"]["name"] + product_name = context["product"]["name"] try: - product_type = context["subset"]["data"]["family"] + product_type = context["product"]["productType"] except ValueError: product_type = "model" @@ -166,7 +166,7 @@ class BlendLoader(plugin.AssetLoader): "libpath": libpath, "asset_name": asset_name, "parent": str(context["representation"]["parent"]), - "productType": context["subset"]["data"]["family"], + "productType": context["product"]["productType"], "objectName": group_name, "members": members, } diff --git a/client/ayon_core/hosts/blender/plugins/load/load_blendscene.py b/client/ayon_core/hosts/blender/plugins/load/load_blendscene.py index 2a0a3313c1..107b586604 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_blendscene.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_blendscene.py @@ -83,10 +83,10 @@ class BlendSceneLoader(plugin.AssetLoader): """ libpath = self.filepath_from_context(context) folder_name = context["folder"]["name"] - product_name = context["subset"]["name"] + product_name = context["product"]["name"] try: - product_type = context["subset"]["data"]["family"] + product_type = context["product"]["productType"] except ValueError: product_type = "model" @@ -118,7 +118,7 @@ class BlendSceneLoader(plugin.AssetLoader): "libpath": libpath, "asset_name": asset_name, "parent": str(context["representation"]["parent"]), - "productType": context["subset"]["data"]["family"], + "productType": context["product"]["productType"], "objectName": group_name, "members": members, } diff --git a/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py b/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py index ff84231f27..54e466127e 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py @@ -85,7 +85,7 @@ class AbcCameraLoader(plugin.AssetLoader): libpath = self.filepath_from_context(context) folder_name = context["folder"]["name"] - product_name = context["subset"]["name"] + product_name = context["product"]["name"] asset_name = plugin.prepare_scene_name(folder_name, product_name) unique_number = plugin.get_unique_number(folder_name, product_name) @@ -123,7 +123,7 @@ class AbcCameraLoader(plugin.AssetLoader): "libpath": libpath, "asset_name": asset_name, "parent": str(context["representation"]["parent"]), - "productType": context["subset"]["data"]["family"], + "productType": context["product"]["productType"], "objectName": group_name, } diff --git a/client/ayon_core/hosts/blender/plugins/load/load_camera_fbx.py b/client/ayon_core/hosts/blender/plugins/load/load_camera_fbx.py index 4ba7b65b9d..b96e8fb46d 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_camera_fbx.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_camera_fbx.py @@ -88,7 +88,7 @@ class FbxCameraLoader(plugin.AssetLoader): """ libpath = self.filepath_from_context(context) folder_name = context["folder"]["name"] - product_name = context["subset"]["name"] + product_name = context["product"]["name"] asset_name = plugin.prepare_scene_name(folder_name, product_name) unique_number = plugin.get_unique_number(folder_name, product_name) @@ -126,7 +126,7 @@ class FbxCameraLoader(plugin.AssetLoader): "libpath": libpath, "asset_name": asset_name, "parent": str(context["representation"]["parent"]), - "productType": context["subset"]["data"]["family"], + "productType": context["product"]["productType"], "objectName": group_name } diff --git a/client/ayon_core/hosts/blender/plugins/load/load_fbx.py b/client/ayon_core/hosts/blender/plugins/load/load_fbx.py index 7feeec528c..64d37d5e8b 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_fbx.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_fbx.py @@ -132,7 +132,7 @@ class FbxModelLoader(plugin.AssetLoader): """ libpath = self.filepath_from_context(context) folder_name = context["folder"]["name"] - product_name = context["subset"]["name"] + product_name = context["product"]["name"] asset_name = plugin.prepare_scene_name(folder_name, product_name) unique_number = plugin.get_unique_number(folder_name, product_name) @@ -170,7 +170,7 @@ class FbxModelLoader(plugin.AssetLoader): "libpath": libpath, "asset_name": asset_name, "parent": str(context["representation"]["parent"]), - "productType": context["subset"]["data"]["family"], + "productType": context["product"]["productType"], "objectName": group_name } diff --git a/client/ayon_core/hosts/blender/plugins/load/load_layout_json.py b/client/ayon_core/hosts/blender/plugins/load/load_layout_json.py index 5381a7b3b9..80424bb610 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_layout_json.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_layout_json.py @@ -149,7 +149,7 @@ class JsonLayoutLoader(plugin.AssetLoader): """ libpath = self.filepath_from_context(context) folder_name = context["folder"]["name"] - product_name = context["subset"]["name"] + product_name = context["product"]["name"] asset_name = plugin.prepare_scene_name(folder_name, product_name) unique_number = plugin.get_unique_number(folder_name, product_name) @@ -181,7 +181,7 @@ class JsonLayoutLoader(plugin.AssetLoader): "libpath": libpath, "asset_name": asset_name, "parent": str(context["representation"]["parent"]), - "productType": context["subset"]["data"]["family"], + "productType": context["product"]["productType"], "objectName": group_name } diff --git a/client/ayon_core/hosts/blender/plugins/load/load_look.py b/client/ayon_core/hosts/blender/plugins/load/load_look.py index 0e79be5720..2c84415861 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_look.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_look.py @@ -94,7 +94,7 @@ class BlendLookLoader(plugin.AssetLoader): libpath = self.filepath_from_context(context) folder_name = context["folder"]["name"] - product_name = context["subset"]["name"] + product_name = context["product"]["name"] lib_container = plugin.prepare_scene_name( folder_name, product_name @@ -131,7 +131,7 @@ class BlendLookLoader(plugin.AssetLoader): metadata["materials"] = materials metadata["parent"] = str(context["representation"]["parent"]) - metadata["product_type"] = context["subset"]["data"]["family"] + metadata["product_type"] = context["product"]["productType"] nodes = list(container.objects) nodes.append(container) diff --git a/client/ayon_core/hosts/flame/plugins/load/load_clip_batch.py b/client/ayon_core/hosts/flame/plugins/load/load_clip_batch.py index 2ad1ec6b30..83ad47c855 100644 --- a/client/ayon_core/hosts/flame/plugins/load/load_clip_batch.py +++ b/client/ayon_core/hosts/flame/plugins/load/load_clip_batch.py @@ -60,7 +60,7 @@ class LoadClipBatch(opfapi.ClipLoader): "output", "representation") folder_entity = context["folder"] - subset_doc = context["subset"] + product_entity = context["product"] formatting_data = deepcopy(context["representation"]["context"]) formatting_data["batch"] = self.batch.name.get_value() formatting_data.update({ @@ -68,11 +68,11 @@ class LoadClipBatch(opfapi.ClipLoader): "folder": { "name": folder_entity["name"], }, - "subset": subset_doc["name"], - "family": subset_doc["data"]["family"], + "subset": product_entity["name"], + "family": product_entity["productType"], "product": { - "name": subset_doc["name"], - "type": subset_doc["data"]["family"], + "name": product_entity["name"], + "type": product_entity["productType"], } }) diff --git a/client/ayon_core/hosts/harmony/plugins/load/load_audio.py b/client/ayon_core/hosts/harmony/plugins/load/load_audio.py index b73c82197a..0f8aab6d57 100644 --- a/client/ayon_core/hosts/harmony/plugins/load/load_audio.py +++ b/client/ayon_core/hosts/harmony/plugins/load/load_audio.py @@ -42,10 +42,10 @@ class ImportAudioLoader(load.LoaderPlugin): def load(self, context, name=None, namespace=None, data=None): wav_file = get_representation_path(context["representation"]) harmony.send( - {"function": func, "args": [context["subset"]["name"], wav_file]} + {"function": func, "args": [context["product"]["name"], wav_file]} ) - product_name = context["subset"]["name"] + product_name = context["product"]["name"] return harmony.containerise( product_name, diff --git a/client/ayon_core/hosts/harmony/plugins/load/load_background.py b/client/ayon_core/hosts/harmony/plugins/load/load_background.py index bf454a9ec7..64d5d7ad0d 100644 --- a/client/ayon_core/hosts/harmony/plugins/load/load_background.py +++ b/client/ayon_core/hosts/harmony/plugins/load/load_background.py @@ -254,7 +254,7 @@ class BackgroundLoader(load.LoaderPlugin): bg_folder = os.path.dirname(path) - product_name = context["subset"]["name"] + product_name = context["product"]["name"] # read_node_name += "_{}".format(uuid.uuid4()) container_nodes = [] diff --git a/client/ayon_core/hosts/harmony/plugins/load/load_imagesequence.py b/client/ayon_core/hosts/harmony/plugins/load/load_imagesequence.py index 473fbf5f17..de5a196bf5 100644 --- a/client/ayon_core/hosts/harmony/plugins/load/load_imagesequence.py +++ b/client/ayon_core/hosts/harmony/plugins/load/load_imagesequence.py @@ -47,7 +47,7 @@ class ImageSequenceLoader(load.LoaderPlugin): files.append(fname.parent.joinpath(remainder[0]).as_posix()) folder_name = context["folder"]["name"] - product_name = context["subset"]["name"] + product_name = context["product"]["name"] group_id = str(uuid.uuid4()) read_node = harmony.send( diff --git a/client/ayon_core/hosts/harmony/plugins/load/load_palette.py b/client/ayon_core/hosts/harmony/plugins/load/load_palette.py index f9ce888f93..7ef15fad52 100644 --- a/client/ayon_core/hosts/harmony/plugins/load/load_palette.py +++ b/client/ayon_core/hosts/harmony/plugins/load/load_palette.py @@ -27,9 +27,8 @@ class ImportPaletteLoader(load.LoaderPlugin): ) def load_palette(self, context): - subset_doc = context["subset"] + product_name = context["product"]["name"] repre_doc = context["representation"] - product_name = subset_doc["name"] name = product_name.replace("palette", "") # Overwrite palette on disk. diff --git a/client/ayon_core/hosts/harmony/plugins/load/load_template.py b/client/ayon_core/hosts/harmony/plugins/load/load_template.py index f26b09fb42..82bc53a3d5 100644 --- a/client/ayon_core/hosts/harmony/plugins/load/load_template.py +++ b/client/ayon_core/hosts/harmony/plugins/load/load_template.py @@ -53,7 +53,7 @@ class TemplateLoader(load.LoaderPlugin): "function": f"PypeHarmony.Loaders.{self_name}.loadContainer", "args": [template_path, context["folder"]["name"], - context["subset"]["name"], + context["product"]["name"], group_id] } )["result"] diff --git a/client/ayon_core/hosts/harmony/plugins/load/load_template_workfile.py b/client/ayon_core/hosts/harmony/plugins/load/load_template_workfile.py index 1b127c5bc4..59d66b7cfc 100644 --- a/client/ayon_core/hosts/harmony/plugins/load/load_template_workfile.py +++ b/client/ayon_core/hosts/harmony/plugins/load/load_template_workfile.py @@ -40,7 +40,7 @@ class ImportTemplateLoader(load.LoaderPlugin): shutil.rmtree(temp_dir) - product_name = context["subset"]["name"] + product_name = context["product"]["name"] return harmony.containerise( product_name, diff --git a/client/ayon_core/hosts/hiero/api/plugin.py b/client/ayon_core/hosts/hiero/api/plugin.py index d2c0310f1e..667dc2af6c 100644 --- a/client/ayon_core/hosts/hiero/api/plugin.py +++ b/client/ayon_core/hosts/hiero/api/plugin.py @@ -448,9 +448,10 @@ class ClipLoader: # create name repr = self.context["representation"] repr_cntx = repr["context"] - folder_name = str(repr_cntx["asset"]) - product_name = str(repr_cntx["subset"]) - representation = str(repr_cntx["representation"]) + folder_path = self.context["folder"]["path"] + folder_name = self.context["folder"]["name"] + product_name = self.context["product"]["name"] + representation = repr["name"] self.data["clip_name"] = self.clip_name_template.format(**repr_cntx) self.data["track_name"] = "_".join([product_name, representation]) self.data["versionData"] = self.context["version"]["data"] @@ -468,11 +469,7 @@ class ClipLoader: self._fix_path_hashes() # solve project bin structure path - hierarchy = str("/".join(( - "Loader", - repr_cntx["hierarchy"].replace("\\", "/"), - folder_name - ))) + hierarchy = "Loader{}".format(folder_path) self.data["binPath"] = hierarchy diff --git a/client/ayon_core/hosts/maya/api/lib.py b/client/ayon_core/hosts/maya/api/lib.py index 8b8d83a155..8cecc33dba 100644 --- a/client/ayon_core/hosts/maya/api/lib.py +++ b/client/ayon_core/hosts/maya/api/lib.py @@ -3977,7 +3977,7 @@ def get_capture_preset( Args: task_name (str): Task name. task_type (str): Task type. - product_name (str): Subset name. + product_name (str): Product name. project_settings (dict): Project settings. log (logging.Logger): Logging object. """ @@ -4129,11 +4129,9 @@ def create_rig_animation_instance( assert roots, "No root nodes in rig, this is a bug." folder_entity = context["folder"] - product_type = ( - context["subset"]["data"].get("family") - or context["subset"]["data"]["families"][0] - ) - product_name = context["subset"]["name"] + product_entity = context["product"] + product_type = product_entity["productType"] + product_name = product_entity["name"] custom_product_name = options.get("animationProductName") if custom_product_name: diff --git a/client/ayon_core/hosts/maya/api/plugin.py b/client/ayon_core/hosts/maya/api/plugin.py index af3546ac24..48ed8213b5 100644 --- a/client/ayon_core/hosts/maya/api/plugin.py +++ b/client/ayon_core/hosts/maya/api/plugin.py @@ -679,20 +679,18 @@ class Loader(LoaderPlugin): options["attach_to_root"] = False folder_entity = context["folder"] - subset_doc = context["subset"] - product_type = ( - subset_doc["data"].get("family") - or subset_doc["data"]["families"][0] - ) + product_entity = context["product"] + product_name = product_entity["name"] + product_type = product_entity["productType"] formatting_data = { "asset_name": folder_entity["name"], "asset_type": "asset", "folder": { "name": folder_entity["name"], }, - "subset": subset_doc["name"], + "subset": product_name, "product": { - "name": subset_doc["name"], + "name": product_name, "type": product_type, }, "family": product_type diff --git a/client/ayon_core/hosts/maya/plugins/inventory/connect_geometry.py b/client/ayon_core/hosts/maya/plugins/inventory/connect_geometry.py index 054c84bea2..839a4dad90 100644 --- a/client/ayon_core/hosts/maya/plugins/inventory/connect_geometry.py +++ b/client/ayon_core/hosts/maya/plugins/inventory/connect_geometry.py @@ -1,6 +1,6 @@ from maya import cmds -from ayon_core.pipeline import InventoryAction, get_representation_context +from ayon_core.pipeline import InventoryAction, get_repres_contexts from ayon_core.hosts.maya.api.lib import get_id @@ -28,14 +28,19 @@ class ConnectGeometry(InventoryAction): # Categorize containers by family. containers_by_product_type = {} + repre_ids = { + container["representation"] + for container in containers + } + repre_contexts_by_id = get_repres_contexts(repre_ids) for container in containers: - product_type = get_representation_context( - container["representation"] - )["subset"]["data"]["family"] - try: - containers_by_product_type[product_type].append(container) - except KeyError: - containers_by_product_type[product_type] = [container] + repre_id = container["representation"] + repre_context = repre_contexts_by_id[repre_id] + + product_type = repre_context["prouct"]["productType"] + + containers_by_product_type.setdefault(product_type, []) + containers_by_product_type[product_type].append(container) # Validate to only 1 source container. source_containers = containers_by_product_type.get("animation", []) diff --git a/client/ayon_core/hosts/maya/plugins/inventory/connect_xgen.py b/client/ayon_core/hosts/maya/plugins/inventory/connect_xgen.py index fa6440fc37..bf9e679928 100644 --- a/client/ayon_core/hosts/maya/plugins/inventory/connect_xgen.py +++ b/client/ayon_core/hosts/maya/plugins/inventory/connect_xgen.py @@ -2,7 +2,9 @@ from maya import cmds import xgenm from ayon_core.pipeline import ( - InventoryAction, get_representation_context, get_representation_path + InventoryAction, + get_repres_contexts, + get_representation_path, ) @@ -25,14 +27,19 @@ class ConnectXgen(InventoryAction): # Categorize containers by product type. containers_by_product_type = {} + repre_ids = { + container["representation"] + for container in containers + } + repre_contexts_by_id = get_repres_contexts(repre_ids) for container in containers: - product_type = get_representation_context( - container["representation"] - )["subset"]["data"]["family"] - try: - containers_by_product_type[product_type].append(container) - except KeyError: - containers_by_product_type[product_type] = [container] + repre_id = container["representation"] + repre_context = repre_contexts_by_id[repre_id] + + product_type = repre_context["prouct"]["productType"] + + containers_by_product_type.setdefault(product_type, []) + containers_by_product_type[product_type].append(container) # Validate to only 1 source container. source_containers = containers_by_product_type.get("animation", []) @@ -51,13 +58,12 @@ class ConnectXgen(InventoryAction): return source_container = source_containers[0] + source_repre_id = source_container["representation"] source_object = source_container["objectName"] # Validate source representation is an alembic. source_path = get_representation_path( - get_representation_context( - source_container["representation"] - )["representation"] + repre_contexts_by_id[source_repre_id]["representation"] ).replace("\\", "/") message = "Animation container \"{}\" is not an alembic:\n{}".format( source_container["namespace"], source_path diff --git a/client/ayon_core/hosts/maya/plugins/inventory/connect_yeti_rig.py b/client/ayon_core/hosts/maya/plugins/inventory/connect_yeti_rig.py index 66807e9d5d..5916bf7b97 100644 --- a/client/ayon_core/hosts/maya/plugins/inventory/connect_yeti_rig.py +++ b/client/ayon_core/hosts/maya/plugins/inventory/connect_yeti_rig.py @@ -5,7 +5,9 @@ from collections import defaultdict from maya import cmds from ayon_core.pipeline import ( - InventoryAction, get_representation_context, get_representation_path + InventoryAction, + get_repres_contexts, + get_representation_path, ) from ayon_core.hosts.maya.api.lib import get_container_members, get_id @@ -28,10 +30,18 @@ class ConnectYetiRig(InventoryAction): # Categorize containers by product type. containers_by_product_type = defaultdict(list) + repre_ids = { + container["representation"] + for container in containers + } + repre_contexts_by_id = get_repres_contexts(repre_ids) for container in containers: - product_type = get_representation_context( - container["representation"] - )["subset"]["data"]["family"] + repre_id = container["representation"] + repre_context = repre_contexts_by_id[repre_id] + + product_type = repre_context["prouct"]["productType"] + + containers_by_product_type.setdefault(product_type, []) containers_by_product_type[product_type].append(container) # Validate to only 1 source container. @@ -66,11 +76,10 @@ class ConnectYetiRig(InventoryAction): for container in yeti_rig_containers: target_ids.update(self.nodes_by_id(container)) + repre_id = container["representation"] maya_file = get_representation_path( - get_representation_context( - container["representation"] - )["representation"] + repre_contexts_by_id[repre_id]["representation"] ) _, ext = os.path.splitext(maya_file) settings_file = maya_file.replace(ext, ".rigsettings") diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py index 32d67910df..f6c3387b15 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py @@ -440,11 +440,10 @@ class LoadClip(plugin.NukeLoader): def _get_node_name(self, context): folder_entity = context["folder"] - subset_doc = context["subset"] + product_name = context["product"]["name"] repre_doc = context["representation"] folder_name = folder_entity["name"] - product_name = subset_doc["name"] repre_cont = repre_doc["context"] name_data = { "folder": { diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_effects.py b/client/ayon_core/hosts/nuke/plugins/load/load_effects.py index 389ad7d057..514e64369c 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_effects.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_effects.py @@ -266,7 +266,7 @@ class LoadEffects(load.LoaderPlugin): self.log.info("updated to version: {}".format(version_doc.get("name"))) - def connect_read_node(self, group_node, namespace, subset): + def connect_read_node(self, group_node, namespace, product_name): """ Finds read node and selects it @@ -277,7 +277,7 @@ class LoadEffects(load.LoaderPlugin): nuke node: node is selected None: if nothing found """ - search_name = "{0}_{1}".format(namespace, subset) + search_name = "{0}_{1}".format(namespace, product_name) node = [ n for n in nuke.allNodes(filter="Read") diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_image.py b/client/ayon_core/hosts/nuke/plugins/load/load_image.py index 71c957fc6b..b2d6098a71 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_image.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_image.py @@ -245,11 +245,10 @@ class LoadImage(load.LoaderPlugin): def _get_node_name(self, context): folder_entity = context["folder"] - subset_doc = context["subset"] + product_name = context["product"]["name"] repre_doc = context["representation"] folder_name = folder_entity["name"] - product_name = subset_doc["name"] repre_cont = repre_doc["context"] name_data = { "folder": { diff --git a/client/ayon_core/hosts/photoshop/plugins/load/load_image.py b/client/ayon_core/hosts/photoshop/plugins/load/load_image.py index 9e935e238c..674410195b 100644 --- a/client/ayon_core/hosts/photoshop/plugins/load/load_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/load/load_image.py @@ -44,7 +44,7 @@ class ImageLoader(photoshop.PhotoshopLoader): repre_doc = context["representation"] folder_name = context["folder"]["name"] - product_name = context["subset"]["name"] + product_name = context["product"]["name"] namespace_from_container = re.sub(r'_\d{3}$', '', container["namespace"]) diff --git a/client/ayon_core/hosts/photoshop/plugins/load/load_reference.py b/client/ayon_core/hosts/photoshop/plugins/load/load_reference.py index 80c4a4e514..d468e296d0 100644 --- a/client/ayon_core/hosts/photoshop/plugins/load/load_reference.py +++ b/client/ayon_core/hosts/photoshop/plugins/load/load_reference.py @@ -43,7 +43,7 @@ class ReferenceLoader(photoshop.PhotoshopLoader): layer = container.pop("layer") folder_name = context["folder"]["name"] - product_name = context["subset"]["name"] + product_name = context["product"]["name"] repre_doc = context["representation"] namespace_from_container = re.sub(r'_\d{3}$', '', diff --git a/client/ayon_core/hosts/resolve/api/plugin.py b/client/ayon_core/hosts/resolve/api/plugin.py index 5ac0fc76fb..e4340dc64a 100644 --- a/client/ayon_core/hosts/resolve/api/plugin.py +++ b/client/ayon_core/hosts/resolve/api/plugin.py @@ -356,7 +356,7 @@ class ClipLoader: """ # create name folder_entity = self.context["folder"] - product_name = self.context["subset"]["name"] + product_name = self.context["product"]["name"] repre_doc = self.context["representation"] folder_name = folder_entity["name"] diff --git a/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py b/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py index 08356dde75..995243bfea 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py +++ b/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py @@ -83,7 +83,7 @@ class LoadImage(plugin.Loader): # Prepare layer name folder_name = context["folder"]["name"] - product_name = context["subset"]["name"] + product_name = context["product"]["name"] layer_name = self.get_unique_layer_name(folder_name, product_name) path = self.filepath_from_context(context) diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py b/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py index 21ec5df1ae..7f6dd147fe 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py @@ -164,14 +164,12 @@ class PointCacheAlembicLoader(plugin.Loader): return asset_content def update(self, container, context): - folder_name = context["asset"]["name"] - subset_doc = context["subset"] + # Create directory for folder and Ayon container + folder_name = context["folder"]["name"] + product_name = context["product"]["name"] version_doc = context["version"] repre_doc = context["representation"] - # Create directory for asset and Ayon container - product_name = subset_doc["name"] - suffix = "_CON" asset_name = product_name if folder_name: diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py index d7060a4c1c..4408e5a090 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py @@ -146,12 +146,10 @@ class SkeletalMeshAlembicLoader(plugin.Loader): def update(self, container, context): folder_name = context["folder"]["name"] - subset_doc = context["subset"] + product_name = context["product"]["name"] version_doc = context["version"] repre_doc = context["representation"] - product_name = subset_doc["name"] - # Create directory for folder and Ayon container suffix = "_CON" asset_name = product_name diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py index 282e9a7f17..43b2b4c4f1 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py @@ -148,12 +148,10 @@ class SkeletalMeshFBXLoader(plugin.Loader): def update(self, container, context): folder_name = context["folder"]["name"] - subset_doc = context["subset"] + product_name = context["product"]["name"] version_doc = context["version"] repre_doc = context["representation"] - product_name = subset_doc["name"] - # Create directory for asset and Ayon container suffix = "_CON" asset_name = product_name diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_abc.py b/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_abc.py index 5bff74c1e9..9056a471d8 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_abc.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_abc.py @@ -147,11 +147,9 @@ class StaticMeshAlembicLoader(plugin.Loader): def update(self, container, context): folder_name = context["folder"]["name"] - subset_doc = context["subset"] + product_name = context["product"]["name"] repre_doc = context["representation"] - product_name = subset_doc["name"] - # Create directory for asset and Ayon container suffix = "_CON" asset_name = product_name diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py b/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py index 4ad255a346..373fc988ea 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py @@ -136,12 +136,10 @@ class StaticMeshFBXLoader(plugin.Loader): def update(self, container, context): folder_name = context["folder"]["name"] - subset_doc = context["subset"] + product_name = context["product"]["name"] version_doc = context["version"] repre_doc = context["representation"] - product_name = subset_doc["name"] - # Create directory for asset and Ayon container suffix = "_CON" asset_name = product_name diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_uasset.py b/client/ayon_core/hosts/unreal/plugins/load/load_uasset.py index 9710d213ee..d7a79616b7 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_uasset.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_uasset.py @@ -101,11 +101,9 @@ class UAssetLoader(plugin.Loader): asset_dir = container["namespace"] - subset_doc = context["subset"] + product_name = context["product"]["name"] repre_doc = context["representation"] - product_name = subset_doc["name"] - unique_number = container["container_name"].split("_")[-2] destination_path = asset_dir.replace( From 3ba6edc4835e370299c6812cc906ad20638694dc Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 5 Mar 2024 14:08:53 +0100 Subject: [PATCH 448/573] use ayon queries for product entities --- .../houdini/plugins/create/create_hda.py | 9 ++-- .../plugins/publish/collect_usd_bootstrap.py | 5 +-- .../plugins/publish/extract_usd_layered.py | 9 ++-- .../validate_usd_shade_model_exists.py | 8 ++-- client/ayon_core/hosts/maya/api/lib.py | 33 +++++++-------- client/ayon_core/hosts/maya/api/setdress.py | 8 ++-- .../maya/plugins/publish/collect_review.py | 10 ++--- .../publish/validate_renderlayer_aovs.py | 6 +-- .../hosts/maya/tools/mayalookassigner/app.py | 6 +-- .../hosts/traypublisher/api/plugin.py | 41 +++++++++---------- .../plugins/create/create_online.py | 5 +-- .../plugins/publish/validate_online_file.py | 8 ++-- .../pipeline/farm/pyblish_functions.py | 6 +-- .../plugins/load/delete_old_versions.py | 22 +++++----- .../publish/collect_anatomy_instance_data.py | 19 ++++----- .../plugins/publish/collect_audio.py | 15 ++++--- .../publish/help/validate_unique_subsets.xml | 2 +- 17 files changed, 103 insertions(+), 109 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py index 654ddfc6d6..c16c95a270 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py @@ -2,7 +2,6 @@ """Creator plugin for creating publishable Houdini Digital Assets.""" import ayon_api -from ayon_core.client import get_subsets from ayon_core.hosts.houdini.api import plugin import hou @@ -24,12 +23,12 @@ class CreateHDA(plugin.HoudiniCreator): folder_entity = ayon_api.get_folder_by_path( project_name, folder_path, fields={"id"} ) - subset_docs = get_subsets( - project_name, asset_ids=[folder_entity["id"]], fields=["name"] + product_entities = ayon_api.get_products( + project_name, folder_ids={folder_entity["id"]}, fields={"name"} ) existing_product_names_low = { - subset_doc["name"].lower() - for subset_doc in subset_docs + product_entity["name"].lower() + for product_entity in product_entities } return product_name.lower() in existing_product_names_low diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py index 161b0aa8d4..cd82f1679a 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py @@ -1,7 +1,6 @@ import pyblish.api import ayon_api -from ayon_core.client import get_subset_by_name from ayon_core.pipeline import usdlib, KnownPublishError @@ -117,8 +116,8 @@ class CollectUsdBootstrap(pyblish.api.InstancePlugin): # Or, if they already exist in the database we can # skip them too. - if get_subset_by_name( - project_name, product_name, folder_entity["id"], fields=["_id"] + if ayon_api.get_product_by_name( + project_name, product_name, folder_entity["id"], fields={"id"} ): return True return False diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py index 60eedcbe7e..fc9feadd40 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py @@ -8,7 +8,6 @@ import ayon_api import pyblish.api from ayon_core.client import ( - get_subset_by_name, get_last_version_by_subset_id, get_representation_by_name, ) @@ -287,19 +286,19 @@ class ExtractUSDLayered(publish.Extractor): folder_entity = ayon_api.get_folder_by_path( project_name, dependency.data["folderPath"], fields={"id"} ) - subset_doc = get_subset_by_name( + product_entity = ayon_api.get_product_by_name( project_name, dependency.data["productName"], folder_entity["id"], - fields=["_id"] + fields={"id"} ) - if not subset_doc: + if not product_entity: # Subset doesn't exist yet. Definitely new file self.log.debug("No existing product..") return False version_doc = get_last_version_by_subset_id( - project_name, subset_doc["_id"], fields=["_id"] + project_name, product_entity["id"], fields=["_id"] ) if not version_doc: self.log.debug("No existing version..") diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_model_exists.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_model_exists.py index 74ded05cdb..048d675c00 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_model_exists.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_model_exists.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- import re +import ayon_api import pyblish.api -from ayon_core.client import get_subset_by_name from ayon_core.pipeline.publish import ( ValidateContentsOrder, KnownPublishError, @@ -36,13 +36,13 @@ class ValidateUSDShadeModelExists(pyblish.api.InstancePlugin): "Folder entity is not filled on instance." ) - subset_doc = get_subset_by_name( + product_entity = ayon_api.get_product_by_name( project_name, model_product_name, folder_entity["id"], - fields=["_id"] + fields={"id"} ) - if not subset_doc: + if not product_entity: raise PublishValidationError( ("USD Model product not found: " "{} ({})").format(model_product_name, folder_path), diff --git a/client/ayon_core/hosts/maya/api/lib.py b/client/ayon_core/hosts/maya/api/lib.py index 8cecc33dba..dac4c6a672 100644 --- a/client/ayon_core/hosts/maya/api/lib.py +++ b/client/ayon_core/hosts/maya/api/lib.py @@ -22,7 +22,6 @@ from maya.api import OpenMaya import ayon_api from ayon_core.client import ( - get_subsets, get_last_versions, get_representation_by_name, ) @@ -1885,11 +1884,13 @@ def list_looks(project_name, folder_id): # the name associated with the asset # TODO this should probably look for product type 'look' instead of # checking product name that can not start with product type - subset_docs = get_subsets(project_name, asset_ids=[folder_id]) + product_entities = ayon_api.get_products( + project_name, folder_ids=[folder_id] + ) return [ - subset_doc - for subset_doc in subset_docs - if subset_doc["name"].startswith("look") + product_entity + for product_entity in product_entities + if product_entity["name"].startswith("look") ] @@ -1973,16 +1974,16 @@ def assign_look(nodes, product_name="lookDefault"): grouped[parts[0]].append(node) project_name = get_current_project_name() - subset_docs = get_subsets( - project_name, subset_names=[product_name], asset_ids=grouped.keys() + product_entities = ayon_api.get_products( + project_name, product_names=[product_name], folder_ids=grouped.keys() ) - subset_docs_by_folder_id = { - str(subset_doc["parent"]): subset_doc - for subset_doc in subset_docs + product_entities_by_folder_id = { + product_entity["folderId"]: product_entity + for product_entity in product_entities } product_ids = { - subset_doc["_id"] - for subset_doc in subset_docs_by_folder_id.values() + product_entity["id"] + for product_entity in product_entities_by_folder_id.values() } last_version_docs = get_last_versions( project_name, @@ -1995,15 +1996,15 @@ def assign_look(nodes, product_name="lookDefault"): } for folder_id, asset_nodes in grouped.items(): - # create objectId for database - subset_doc = subset_docs_by_folder_id.get(folder_id) - if not subset_doc: + product_entity = product_entities_by_folder_id.get(folder_id) + if not product_entity: log.warning(( "No product '{}' found for {}" ).format(product_name, folder_id)) continue - last_version = last_version_docs_by_product_id.get(subset_doc["_id"]) + product_id = product_entity["id"] + last_version = last_version_docs_by_product_id.get(product_id) if not last_version: log.warning(( "Not found last version for product '{}' on folder with id {}" diff --git a/client/ayon_core/hosts/maya/api/setdress.py b/client/ayon_core/hosts/maya/api/setdress.py index c1f900110d..e070130e15 100644 --- a/client/ayon_core/hosts/maya/api/setdress.py +++ b/client/ayon_core/hosts/maya/api/setdress.py @@ -299,18 +299,18 @@ def update_package_version(container, version): ( version_doc, - subset_doc, + product_entity, folder_entity, project_entity ) = get_representation_parents(project_name, current_representation) if version == -1: new_version = get_last_version_by_subset_id( - project_name, subset_doc["_id"] + project_name, product_entity["id"] ) else: new_version = get_version_by_name( - project_name, version, subset_doc["_id"] + project_name, version, product_entity["id"] ) assert new_version is not None, "This is a bug" @@ -324,7 +324,7 @@ def update_package_version(container, version): new_context = { "project": project_entity, "folder": folder_entity, - "subset": subset_doc, + "product": product_entity, "version": version_doc, "representation": new_representation, } diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_review.py b/client/ayon_core/hosts/maya/plugins/publish/collect_review.py index 588ed862e1..4e35b3bcc2 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/collect_review.py +++ b/client/ayon_core/hosts/maya/plugins/publish/collect_review.py @@ -1,8 +1,8 @@ from maya import cmds, mel +import ayon_api import pyblish.api -from ayon_core.client import get_subset_by_name from ayon_core.pipeline import KnownPublishError from ayon_core.hosts.maya.api import lib @@ -67,7 +67,7 @@ class CollectReview(pyblish.api.InstancePlugin): reviewable_product = reviewable_products[0] self.log.debug( - "Subset attached to review: {}".format(reviewable_product) + "Product attached to review: {}".format(reviewable_product) ) # Find the relevant publishing instance in the current context @@ -120,13 +120,13 @@ class CollectReview(pyblish.api.InstancePlugin): folder_entity = instance.context.data["folderEntity"] task = instance.context.data["task"] legacy_product_name = task + 'Review' - subset_doc = get_subset_by_name( + product_entity = ayon_api.get_product_by_name( project_name, legacy_product_name, folder_entity["id"], - fields=["_id"] + fields={"id"} ) - if subset_doc: + if product_entity: self.log.debug("Existing products found, keep legacy name.") instance.data["productName"] = legacy_product_name diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_renderlayer_aovs.py b/client/ayon_core/hosts/maya/plugins/publish/validate_renderlayer_aovs.py index ba7d674e00..90f256ad45 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_renderlayer_aovs.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_renderlayer_aovs.py @@ -1,7 +1,7 @@ +import ayon_api import pyblish.api import ayon_core.hosts.maya.api.action -from ayon_core.client import get_subset_by_name from ayon_core.pipeline.publish import PublishValidationError @@ -53,6 +53,6 @@ class ValidateRenderLayerAOVs(pyblish.api.InstancePlugin): ): """Check if product is registered in the database under the folder""" - return get_subset_by_name( - project_name, product_name, folder_entity["id"], fields=["_id"] + return ayon_api.get_product_by_name( + project_name, product_name, folder_entity["id"], fields={"id"} ) diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/app.py b/client/ayon_core/hosts/maya/tools/mayalookassigner/app.py index 0969666484..5158542a8f 100644 --- a/client/ayon_core/hosts/maya/tools/mayalookassigner/app.py +++ b/client/ayon_core/hosts/maya/tools/mayalookassigner/app.py @@ -227,9 +227,9 @@ class MayaLookAssignerWindow(QtWidgets.QWidget): # (since assigning multiple to the same nodes makes no sense) assign_look = next( ( - subset_doc - for subset_doc in item["looks"] - if subset_doc["name"] in looks + product_entity + for product_entity in item["looks"] + if product_entity["name"] in looks ), None ) diff --git a/client/ayon_core/hosts/traypublisher/api/plugin.py b/client/ayon_core/hosts/traypublisher/api/plugin.py index bc7d352b10..1039b570ee 100644 --- a/client/ayon_core/hosts/traypublisher/api/plugin.py +++ b/client/ayon_core/hosts/traypublisher/api/plugin.py @@ -1,9 +1,6 @@ import ayon_api -from ayon_core.client import ( - get_subsets, - get_last_versions, -) +from ayon_core.client import get_last_versions from ayon_core.lib.attribute_definitions import ( FileDef, BoolDef, @@ -118,9 +115,9 @@ class SettingsCreator(TrayPublishCreator): # Fill 'version_to_use' if version control is enabled if self.allow_version_control: folder_path = data["folderPath"] - subset_docs_by_folder_path = self._prepare_next_versions( + product_entities_by_folder_path = self._prepare_next_versions( [folder_path], [product_name]) - version = subset_docs_by_folder_path[folder_path].get( + version = product_entities_by_folder_path[folder_path].get( product_name ) pre_create_data["version_to_use"] = version @@ -157,7 +154,7 @@ class SettingsCreator(TrayPublishCreator): # Prepare all versions for all combinations to '1' # TODO use 'ayon_core.pipeline.version_start' logic - subset_docs_by_folder_path = { + product_entities_by_folder_path = { folder_path: { product_name: 1 for product_name in product_names @@ -165,7 +162,7 @@ class SettingsCreator(TrayPublishCreator): for folder_path in folder_paths } if not folder_paths or not product_names: - return subset_docs_by_folder_path + return product_entities_by_folder_path folder_entities = ayon_api.get_folders( self.project_name, @@ -176,30 +173,32 @@ class SettingsCreator(TrayPublishCreator): folder_entity["id"]: folder_entity["path"] for folder_entity in folder_entities } - subset_docs = list(get_subsets( + product_entities = list(ayon_api.get_products( self.project_name, - asset_ids=folder_paths_by_id.keys(), - subset_names=product_names, - fields=["_id", "name", "parent"] + folder_ids=folder_paths_by_id.keys(), + product_names=product_names, + fields={"id", "name", "folderId"} )) - product_ids = {subset_doc["_id"] for subset_doc in subset_docs} + product_ids = {p["id"] for p in product_entities} last_versions = get_last_versions( self.project_name, product_ids, fields=["name", "parent"]) - for subset_doc in subset_docs: - folder_id = subset_doc["parent"] + for product_entity in product_entities: + product_id = product_entity["id"] + product_name = product_entity["name"] + folder_id = product_entity["folderId"] folder_path = folder_paths_by_id[folder_id] - product_name = subset_doc["name"] - product_id = subset_doc["_id"] last_version = last_versions.get(product_id) version = 0 if last_version is not None: version = last_version["name"] - subset_docs_by_folder_path[folder_path][product_name] += version - return subset_docs_by_folder_path + product_entities_by_folder_path[folder_path][product_name] += ( + version + ) + return product_entities_by_folder_path def _fill_next_versions(self, instances_data): """Fill next version for instances. @@ -231,13 +230,13 @@ class SettingsCreator(TrayPublishCreator): product_names = { instance["productName"] for instance in filtered_instance_data} - subset_docs_by_folder_path = self._prepare_next_versions( + product_entities_by_folder_path = self._prepare_next_versions( folder_paths, product_names ) for instance in filtered_instance_data: folder_path = instance["folderPath"] product_name = instance["productName"] - version = subset_docs_by_folder_path[folder_path][product_name] + version = product_entities_by_folder_path[folder_path][product_name] instance["creator_attributes"]["version_to_use"] = version instance["_previous_last_version"] = version diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_online.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_online.py index a832254ad9..f48037701e 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_online.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_online.py @@ -9,7 +9,6 @@ from pathlib import Path # import ayon_api -# from ayon_core.client import get_subset_by_name from ayon_core.lib.attribute_definitions import FileDef, BoolDef from ayon_core.pipeline import ( CreatedInstance, @@ -57,9 +56,9 @@ class OnlineCreator(TrayPublishCreator): folder_entity = ayon_api.get_folder_by_path( self.project_name, instance_data["folderPath"], fields={"id"}) - if get_subset_by_name( + if ayon_api.get_product_by_name( self.project_name, origin_basename, folder_entity["id"], - fields=["_id"]): + fields={"id"}): raise CreatorError(f"product with {origin_basename} already " "exists in selected folder") """ diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_online_file.py b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_online_file.py index 4eb0503006..e9add2369b 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_online_file.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_online_file.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import ayon_api import pyblish.api from ayon_core.pipeline.publish import ( @@ -6,7 +7,6 @@ from ayon_core.pipeline.publish import ( PublishValidationError, OptionalPyblishPluginMixin, ) -from ayon_core.client import get_subset_by_name class ValidateOnlineFile(OptionalPyblishPluginMixin, @@ -24,11 +24,11 @@ class ValidateOnlineFile(OptionalPyblishPluginMixin, return project_name = instance.context.data["projectName"] folder_id = instance.data["folderEntity"]["id"] - subset_doc = get_subset_by_name( + product_entity = ayon_api.get_product_by_name( project_name, instance.data["productName"], folder_id) - if subset_doc: + if product_entity: raise PublishValidationError( - "Subset to be published already exists.", + "Product to be published already exists.", title=self.label ) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index c669d95c1e..3fce6a465c 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -626,7 +626,7 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, new_instance = deepcopy(skeleton) new_instance["productName"] = product_name - new_instance["subsetGroup"] = group_name + new_instance["productGroup"] = group_name # explicitly disable review by user preview = preview and not do_not_add_review @@ -1088,8 +1088,8 @@ def attach_instances_to_product(attach_to, instances): new_inst["productType"] = attach_instance.get("productType") new_inst["family"] = attach_instance.get("family") new_inst["append"] = True - # don't set subsetGroup if we are attaching - new_inst.pop("subsetGroup") + # don't set productGroup if we are attaching + new_inst.pop("productGroup") new_instances.append(new_inst) return new_instances diff --git a/client/ayon_core/plugins/load/delete_old_versions.py b/client/ayon_core/plugins/load/delete_old_versions.py index 74e74b38bd..4c5bed899e 100644 --- a/client/ayon_core/plugins/load/delete_old_versions.py +++ b/client/ayon_core/plugins/load/delete_old_versions.py @@ -196,13 +196,13 @@ # msgBox.exec_() # # def get_data(self, context, versions_count): -# subset_doc = context["subset"] +# product_entity = context["product"] # folder_entity = context["folder"] # project_name = context["project"]["name"] # anatomy = Anatomy(project_name) # # versions = list(get_versions( -# project_name, subset_ids=[subset_doc["_id"]] +# project_name, subset_ids=[product_entity["id"]] # )) # # versions_by_parent = collections.defaultdict(list) @@ -240,9 +240,9 @@ # versions_to_pop.append(version) # # for version in versions_to_pop: -# msg = "Folder: \"{}\" | Subset: \"{}\" | Version: \"{}\"".format( +# msg = "Folder: \"{}\" | Product: \"{}\" | Version: \"{}\"".format( # folder_entity["path"], -# subset_doc["name"], +# product_entity["name"], # version["name"] # ) # self.log.debug(( @@ -258,7 +258,7 @@ # # if not version_ids: # msg = "Skipping processing. Nothing to delete on {}/{}".format( -# folder_entity["path"], subset_doc["name"] +# folder_entity["path"], product_entity["name"] # ) # self.log.info(msg) # print(msg) @@ -319,8 +319,8 @@ # "file_paths_by_dir": file_paths_by_dir, # "versions": versions, # "folder": folder_entity, -# "subset": subset_doc, -# "archive_subset": versions_count == 0 +# "product": product_entity, +# "archive_product": versions_count == 0 # } # # def main(self, project_name, data, remove_publish_folder): @@ -350,10 +350,10 @@ # update_data = {"$set": {"data.tags": version_tags}} # mongo_changes_bulk.append(UpdateOne(update_query, update_data)) # -# if data["archive_subset"]: +# if data["archive_product"]: # mongo_changes_bulk.append(UpdateOne( # { -# "_id": data["subset"]["_id"], +# "id": data["product"]["id"], # "type": "subset" # }, # {"$set": {"type": "archived_subset"}} @@ -381,7 +381,7 @@ # "not published" which cause that they're invisible. # # Args: -# data (dict): Data sent to subset loader with full context. +# data (dict): Data sent to product loader with full context. # """ # # # First check for ftrack id on folder entity @@ -403,7 +403,7 @@ # import ftrack_api # # session = ftrack_api.Session() -# product_name = data["subset"]["name"] +# product_name = data["product"]["name"] # versions = { # '"{}"'.format(version_doc["name"]) # for version_doc in data["versions"] diff --git a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py index 1b49d8288d..2f28f00ac5 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py @@ -34,7 +34,6 @@ import pyblish.api import ayon_api from ayon_core.client import ( - get_subsets, get_last_versions, ) from ayon_core.pipeline.version_start import get_versioning_start @@ -261,28 +260,28 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): hierarchy[folder_id][product_name].append(instance) names_by_folder_ids[folder_id].add(product_name) - subset_docs = [] + product_entities = [] if names_by_folder_ids: - subset_docs = list(get_subsets( - project_name, names_by_asset_ids=names_by_folder_ids + product_entities = list(ayon_api.get_products( + project_name, names_by_folder_ids=names_by_folder_ids )) product_ids = { - subset_doc["_id"] - for subset_doc in subset_docs + product_entity["id"] + for product_entity in product_entities } last_version_docs_by_product_id = get_last_versions( project_name, product_ids, fields=["name"] ) - for subset_doc in subset_docs: - product_id = subset_doc["_id"] + for product_entity in product_entities: + product_id = product_entity["id"] last_version_doc = last_version_docs_by_product_id.get(product_id) if last_version_doc is None: continue - folder_id = subset_doc["parent"] - product_name = subset_doc["name"] + folder_id = product_entity["folderId"] + product_name = product_entity["name"] _instances = hierarchy[folder_id][product_name] for _instance in _instances: _instance.data["latestVersion"] = last_version_doc["name"] diff --git a/client/ayon_core/plugins/publish/collect_audio.py b/client/ayon_core/plugins/publish/collect_audio.py index c818ebd147..920cc950fb 100644 --- a/client/ayon_core/plugins/publish/collect_audio.py +++ b/client/ayon_core/plugins/publish/collect_audio.py @@ -4,7 +4,6 @@ import ayon_api import pyblish.api from ayon_core.client import ( - get_subsets, get_last_versions, get_representations, ) @@ -133,16 +132,16 @@ class CollectAudio(pyblish.api.ContextPlugin): # Query products with name define by 'audio_product_name' attr # - one or none products with the name should be available on # an folder - subset_docs = get_subsets( + product_entities = ayon_api.get_products( project_name, - subset_names=[self.audio_product_name], - asset_ids=folder_ids, - fields=["_id", "parent"] + product_names=[self.audio_product_name], + folder_ids=folder_ids, + fields={"id", "folderId"} ) product_id_by_folder_id = {} - for subset_doc in subset_docs: - folder_id = subset_doc["parent"] - product_id_by_folder_id[folder_id] = subset_doc["_id"] + for product_entity in product_entities: + folder_id = product_entity["folderId"] + product_id_by_folder_id[folder_id] = product_entity["id"] product_ids = set(product_id_by_folder_id.values()) if not product_ids: diff --git a/client/ayon_core/plugins/publish/help/validate_unique_subsets.xml b/client/ayon_core/plugins/publish/help/validate_unique_subsets.xml index a4b289d848..e163fc39fe 100644 --- a/client/ayon_core/plugins/publish/help/validate_unique_subsets.xml +++ b/client/ayon_core/plugins/publish/help/validate_unique_subsets.xml @@ -1,7 +1,7 @@ -Subset not unique +Product not unique ## Clashing product names found From 805c199614cf280d2daea3108876c11b591e276e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 5 Mar 2024 14:09:34 +0100 Subject: [PATCH 449/573] rename 'subsetGroup' > 'productGroup' --- .../publish/collect_instances_usd_layered.py | 2 +- .../plugins/publish/collect_usd_layers.py | 2 +- .../plugins/publish/collect_textureset_images.py | 2 +- client/ayon_core/plugins/publish/integrate.py | 10 +++++----- .../plugins/publish/integrate_product_group.py | 16 ++++++++-------- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_instances_usd_layered.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_instances_usd_layered.py index 738d5306d1..9377a9fcd0 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_instances_usd_layered.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_instances_usd_layered.py @@ -89,7 +89,7 @@ class CollectInstancesUsdLayered(pyblish.api.ContextPlugin): # For now group ALL of them into USD Layer product group # Allow this product to be grouped into a USD Layer on creation - data["subsetGroup"] = "USD Layer" + data["productGroup"] = "USD Layer" instances = list() dependencies = [] diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_layers.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_layers.py index f085b6ca41..93add6806e 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_layers.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_layers.py @@ -64,4 +64,4 @@ class CollectUsdLayers(pyblish.api.InstancePlugin): layer_inst.append((layer, save_path)) # Allow this product to be grouped into a USD Layer on creation - layer_inst.data["subsetGroup"] = "USD Layer" + layer_inst.data["productGroup"] = "USD Layer" diff --git a/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py b/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py index 859a941882..7f7a0acd75 100644 --- a/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py +++ b/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py @@ -132,7 +132,7 @@ class CollectTextureSet(pyblish.api.InstancePlugin): image_instance.data["representations"] = [representation] # Group the textures together in the loader - image_instance.data["subsetGroup"] = image_product_name + image_instance.data["productGroup"] = image_product_name # Store the texture set name and stack name on the instance image_instance.data["textureSetName"] = texture_set_name diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index 1e295d2763..b61ac457ad 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -428,14 +428,14 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "families": get_instance_families(instance) } - subset_group = instance.data.get("subsetGroup") + subset_group = instance.data.get("productGroup") if subset_group: - data["subsetGroup"] = subset_group + data["productGroup"] = subset_group elif existing_subset_doc: # Preserve previous subset group if new version does not set it - if "subsetGroup" in existing_subset_doc.get("data", {}): - subset_group = existing_subset_doc["data"]["subsetGroup"] - data["subsetGroup"] = subset_group + if "productGroup" in existing_subset_doc.get("data", {}): + subset_group = existing_subset_doc["data"]["productGroup"] + data["productGroup"] = subset_group subset_id = None if existing_subset_doc: diff --git a/client/ayon_core/plugins/publish/integrate_product_group.py b/client/ayon_core/plugins/publish/integrate_product_group.py index f69e7744d9..90887a359d 100644 --- a/client/ayon_core/plugins/publish/integrate_product_group.py +++ b/client/ayon_core/plugins/publish/integrate_product_group.py @@ -1,10 +1,10 @@ -"""Produces instance.data["subsetGroup"] data used during integration. +"""Produces instance.data["productGroup"] data used during integration. Requires: dict -> context["anatomyData"] *(pyblish.api.CollectorOrder + 0.49) Provides: - instance -> subsetGroup (str) + instance -> productGroup (str) """ import pyblish.api @@ -18,7 +18,7 @@ from ayon_core.lib import ( class IntegrateProductGroup(pyblish.api.InstancePlugin): - """Integrate Subset Group for publish.""" + """Integrate Product Group for publish.""" # Run after CollectAnatomyInstanceData order = pyblish.api.IntegratorOrder - 0.1 @@ -37,11 +37,11 @@ class IntegrateProductGroup(pyblish.api.InstancePlugin): if not self.product_grouping_profiles: return - if instance.data.get("subsetGroup"): - # If subsetGroup is already set then allow that value to remain + if instance.data.get("productGroup"): + # If productGroup is already set then allow that value to remain self.log.debug(( "Skipping collect product group due to existing value: {}" - ).format(instance.data["subsetGroup"])) + ).format(instance.data["productGroup"])) return # Skip if there is no matching profile @@ -79,11 +79,11 @@ class IntegrateProductGroup(pyblish.api.InstancePlugin): except (KeyError, TemplateUnsolved): keys = fill_pairs.keys() self.log.warning(( - "Subset grouping failed. Only {} are expected in Settings" + "Product grouping failed. Only {} are expected in Settings" ).format(','.join(keys))) if filled_template: - instance.data["subsetGroup"] = filled_template + instance.data["productGroup"] = filled_template def get_profile_filter_criteria(self, instance): """Return filter criteria for `filter_profiles`""" From 7bcbf0f668f1174249f25c2321e1306944c96160 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 5 Mar 2024 14:10:07 +0100 Subject: [PATCH 450/573] use product naming over subset --- client/ayon_core/host/host.py | 2 +- .../plugins/publish/help/validate_instance_asset.xml | 2 +- .../hosts/flame/plugins/create/create_shot_clip.py | 4 ++-- client/ayon_core/hosts/fusion/api/plugin.py | 2 +- .../fusion/plugins/publish/validate_unique_subsets.py | 2 +- client/ayon_core/hosts/harmony/api/README.md | 2 +- .../plugins/publish/help/validate_instances.xml | 2 +- .../houdini/plugins/publish/validate_subset_name.py | 10 +++++----- .../maya/plugins/publish/determine_future_version.py | 2 +- client/ayon_core/hosts/nuke/api/plugin.py | 2 +- .../plugins/publish/extract_review_intermediates.py | 2 +- .../ayon_core/hosts/nuke/startup/custom_write_node.py | 4 ++-- .../photoshop/plugins/publish/help/validate_naming.xml | 4 ++-- .../traypublisher/plugins/create/create_editorial.py | 2 +- .../hosts/tvpaint/plugins/create/create_render.py | 2 +- .../plugins/publish/help/validate_asset_name.xml | 2 +- .../plugins/publish/collect_custom_staging_dir.py | 2 +- .../plugins/publish/validate_unique_subsets.py | 4 ++-- 18 files changed, 26 insertions(+), 26 deletions(-) diff --git a/client/ayon_core/host/host.py b/client/ayon_core/host/host.py index b815dadfe1..081aafdbe3 100644 --- a/client/ayon_core/host/host.py +++ b/client/ayon_core/host/host.py @@ -18,7 +18,7 @@ class HostBase(object): Compared to 'avalon' concept: What was before considered as functions in host implementation folder. The host implementation should primarily care about adding ability of creation - (mark subsets to be published) and optionally about referencing published + (mark products to be published) and optionally about referencing published representations as containers. Host may need extend some functionality like working with workfiles diff --git a/client/ayon_core/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml b/client/ayon_core/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml index 1507ba8d7b..23e1b50551 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml +++ b/client/ayon_core/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml @@ -1,7 +1,7 @@ -Subset context +Product context ## Invalid product context diff --git a/client/ayon_core/hosts/flame/plugins/create/create_shot_clip.py b/client/ayon_core/hosts/flame/plugins/create/create_shot_clip.py index c73ee7510c..e8eb2b9fab 100644 --- a/client/ayon_core/hosts/flame/plugins/create/create_shot_clip.py +++ b/client/ayon_core/hosts/flame/plugins/create/create_shot_clip.py @@ -207,14 +207,14 @@ class CreateShotClip(opfapi.Creator): "value": ["[ track name ]", "main", "bg", "fg", "bg", "animatic"], "type": "QComboBox", - "label": "Subset Name", + "label": "Product Name", "target": "ui", "toolTip": "chose product name pattern, if [ track name ] is selected, name of track layer will be used", # noqa "order": 0}, "productType": { "value": ["plate", "take"], "type": "QComboBox", - "label": "Subset Family", + "label": "Product Type", "target": "ui", "toolTip": "What use of this product is for", # noqa "order": 1}, "reviewTrack": { diff --git a/client/ayon_core/hosts/fusion/api/plugin.py b/client/ayon_core/hosts/fusion/api/plugin.py index 95db8126e7..f63b5eaec3 100644 --- a/client/ayon_core/hosts/fusion/api/plugin.py +++ b/client/ayon_core/hosts/fusion/api/plugin.py @@ -138,7 +138,7 @@ class GenericCreateSaver(Creator): # get output format ext = data["creator_attributes"]["image_format"] - # Subset change detected + # Product change detected product_type = formatting_data["productType"] f_product_name = formatting_data["productName"] diff --git a/client/ayon_core/hosts/fusion/plugins/publish/validate_unique_subsets.py b/client/ayon_core/hosts/fusion/plugins/publish/validate_unique_subsets.py index d64a29286d..bcd9abd8b0 100644 --- a/client/ayon_core/hosts/fusion/plugins/publish/validate_unique_subsets.py +++ b/client/ayon_core/hosts/fusion/plugins/publish/validate_unique_subsets.py @@ -10,7 +10,7 @@ class ValidateUniqueSubsets(pyblish.api.ContextPlugin): """Ensure all instances have a unique product name""" order = pyblish.api.ValidatorOrder - label = "Validate Unique Subsets" + label = "Validate Unique Products" families = ["render", "image"] hosts = ["fusion"] actions = [SelectInvalidAction] diff --git a/client/ayon_core/hosts/harmony/api/README.md b/client/ayon_core/hosts/harmony/api/README.md index 151b2bce9e..055381928c 100644 --- a/client/ayon_core/hosts/harmony/api/README.md +++ b/client/ayon_core/hosts/harmony/api/README.md @@ -597,7 +597,7 @@ class ImageSequenceLoader(load.LoaderPlugin): read_node = harmony.send( { "function": copy_files + import_files, - "args": ["Top", files, context["version"]["data"]["subset"], 1] + "args": ["Top", files, context["product"]["name"], 1] } )["result"] diff --git a/client/ayon_core/hosts/harmony/plugins/publish/help/validate_instances.xml b/client/ayon_core/hosts/harmony/plugins/publish/help/validate_instances.xml index 072faf6030..8c2b523e29 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/help/validate_instances.xml +++ b/client/ayon_core/hosts/harmony/plugins/publish/help/validate_instances.xml @@ -1,7 +1,7 @@ -Subset context +Product context ## Invalid product context diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py index e32f7be08a..8e62b85650 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py @@ -15,21 +15,21 @@ from ayon_core.pipeline.create import get_product_name import hou -class FixSubsetNameAction(RepairAction): - label = "Fix Subset Name" +class FixProductNameAction(RepairAction): + label = "Fix Product Name" class ValidateSubsetName(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): - """Validate Subset name. + """Validate Product name. """ families = ["staticMesh"] hosts = ["houdini"] - label = "Validate Subset Name" + label = "Validate Product Name" order = ValidateContentsOrder + 0.1 - actions = [FixSubsetNameAction, SelectInvalidAction] + actions = [FixProductNameAction, SelectInvalidAction] optional = True diff --git a/client/ayon_core/hosts/maya/plugins/publish/determine_future_version.py b/client/ayon_core/hosts/maya/plugins/publish/determine_future_version.py index 47fb4f03fe..5b597f2707 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/determine_future_version.py +++ b/client/ayon_core/hosts/maya/plugins/publish/determine_future_version.py @@ -5,7 +5,7 @@ class DetermineFutureVersion(pyblish.api.InstancePlugin): """ This will determine version of product if we want render to be attached to. """ - label = "Determine Subset Version" + label = "Determine Product Version" order = pyblish.api.IntegratorOrder hosts = ["maya"] families = ["renderlayer"] diff --git a/client/ayon_core/hosts/nuke/api/plugin.py b/client/ayon_core/hosts/nuke/api/plugin.py index b36dfc56e6..7f016d9c66 100644 --- a/client/ayon_core/hosts/nuke/api/plugin.py +++ b/client/ayon_core/hosts/nuke/api/plugin.py @@ -95,7 +95,7 @@ class NukeCreator(NewCreator): any node having instance data knob. Arguments: - product_name (str): Subset name + product_name (str): Product name """ for node in nuke.allNodes(recurseGroups=True): diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py b/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py index 8ac07c641c..8d7a3ec311 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py @@ -109,7 +109,7 @@ class ExtractReviewIntermediates(publish.Extractor): if f_task_types and task_type not in f_task_types: continue - # test subsets from filter + # test products from filter if product_names and not any( re.search(p, product_name) for p in product_names ): diff --git a/client/ayon_core/hosts/nuke/startup/custom_write_node.py b/client/ayon_core/hosts/nuke/startup/custom_write_node.py index 84e99f34c4..075c8e7a17 100644 --- a/client/ayon_core/hosts/nuke/startup/custom_write_node.py +++ b/client/ayon_core/hosts/nuke/startup/custom_write_node.py @@ -145,8 +145,8 @@ class WriteNodeKnobSettingPanel(nukescripts.PythonPanel): for setting in settings: # TODO change 'subsets' to 'product_names' in settings - for subset in setting["subsets"]: - preset_name.append(subset) + for product_name in setting["subsets"]: + preset_name.append(product_name) return preset_name, knobs_nodes diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/help/validate_naming.xml b/client/ayon_core/hosts/photoshop/plugins/publish/help/validate_naming.xml index 28c2c2c773..28c2329c8a 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/help/validate_naming.xml +++ b/client/ayon_core/hosts/photoshop/plugins/publish/help/validate_naming.xml @@ -1,11 +1,11 @@ -Subset name +Product name ## Invalid product or layer name -Subset or layer name cannot contain specific characters (spaces etc) which could cause issue when product name is used in a published file name. +Product or layer name cannot contain specific characters (spaces etc) which could cause issue when product name is used in a published file name. {msg} ### How to repair? diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py index df35f54291..a9ee343dfb 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py @@ -569,7 +569,7 @@ or updating already created. Publishing will create OTIO file. return c_instance def _make_product_naming(self, product_type_preset, instance_data): - """Subset name maker + """Product name maker Args: product_type_preset (dict): single preset item diff --git a/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py b/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py index 9714dc8b8f..8d91afc74e 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py +++ b/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py @@ -220,7 +220,7 @@ class CreateRenderlayer(TVPaintCreator): creator_attributes["group_id"] = group_id creator_attributes["mark_for_review"] = mark_for_review - self.log.info(f"Subset name is {product_name}") + self.log.info(f"Product name is {product_name}") new_instance = CreatedInstance( self.product_type, product_name, diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml b/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml index 5ac8d7fdcb..bba0104c54 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml +++ b/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml @@ -1,7 +1,7 @@ -Subset context +Product context ## Invalid product context Context of the given product doesn't match your current scene. diff --git a/client/ayon_core/plugins/publish/collect_custom_staging_dir.py b/client/ayon_core/plugins/publish/collect_custom_staging_dir.py index e42f34b0ae..49c3a98dd2 100644 --- a/client/ayon_core/plugins/publish/collect_custom_staging_dir.py +++ b/client/ayon_core/plugins/publish/collect_custom_staging_dir.py @@ -28,7 +28,7 @@ class CollectCustomStagingDir(pyblish.api.InstancePlugin): Location of the folder is configured in `project_anatomy/templates/others`. ('transient' key is expected, with 'folder' key) - Which family/task type/subset is applicable is configured in: + Which family/task type/product is applicable is configured in: `project_settings/global/tools/publish/custom_staging_dir_profiles` """ diff --git a/client/ayon_core/plugins/publish/validate_unique_subsets.py b/client/ayon_core/plugins/publish/validate_unique_subsets.py index bda40b25b5..4badeb8112 100644 --- a/client/ayon_core/plugins/publish/validate_unique_subsets.py +++ b/client/ayon_core/plugins/publish/validate_unique_subsets.py @@ -7,7 +7,7 @@ from ayon_core.pipeline.publish import ( ) -class ValidateSubsetUniqueness(pyblish.api.ContextPlugin): +class ValidateProductUniqueness(pyblish.api.ContextPlugin): """Validate all product names are unique. This only validates whether the instances currently set to publish from @@ -23,7 +23,7 @@ class ValidateSubsetUniqueness(pyblish.api.ContextPlugin): """ - label = "Validate Subset Uniqueness" + label = "Validate Product Uniqueness" order = pyblish.api.ValidatorOrder families = ["*"] From 511e2047defb0bbd11bbb7d41dc13a047ba7c2e0 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Tue, 5 Mar 2024 16:03:50 +0000 Subject: [PATCH 451/573] Update client/ayon_core/hosts/nuke/plugins/load/load_clip.py --- client/ayon_core/hosts/nuke/plugins/load/load_clip.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py index 51cf5941ea..686cd6eac6 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py @@ -502,8 +502,6 @@ class LoadClip(plugin.NukeLoader): f"Colorspace from representation colorspaceData: {colorspace}" ) - print(f"Colorspace found: {colorspace}") - # check if any filerules are not applicable new_parsed_colorspace = get_imageio_file_rules_colorspace_from_filepath( # noqa filepath, "nuke", get_current_project_name() From 6308c251e28640e723a6bcdb27eaac1660d0390b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 6 Mar 2024 10:52:09 +0100 Subject: [PATCH 452/573] pipeline code using version entities --- client/ayon_core/pipeline/context_tools.py | 7 +- client/ayon_core/pipeline/create/utils.py | 10 +- .../pipeline/farm/pyblish_functions.py | 44 ++++--- client/ayon_core/pipeline/load/utils.py | 124 ++++++++---------- .../pipeline/workfile/build_workfile.py | 33 +++-- 5 files changed, 101 insertions(+), 117 deletions(-) diff --git a/client/ayon_core/pipeline/context_tools.py b/client/ayon_core/pipeline/context_tools.py index bda994490d..5c41996aab 100644 --- a/client/ayon_core/pipeline/context_tools.py +++ b/client/ayon_core/pipeline/context_tools.py @@ -12,10 +12,7 @@ from pyblish.lib import MessageHandler from ayon_core import AYON_CORE_ROOT from ayon_core.host import HostBase -from ayon_core.client import ( - version_is_latest, - get_ayon_server_api_connection, -) +from ayon_core.client import get_ayon_server_api_connection from ayon_core.lib import is_in_tests from ayon_core.lib.events import emit_event from ayon_core.addon import load_addons, AddonsManager @@ -460,7 +457,7 @@ def is_representation_from_latest(representation): """ project_name = get_current_project_name() - return version_is_latest(project_name, representation["parent"]) + return ayon_api.version_is_latest(project_name, representation["parent"]) def get_template_data_from_session(session=None, settings=None): diff --git a/client/ayon_core/pipeline/create/utils.py b/client/ayon_core/pipeline/create/utils.py index c94fbe5cb0..b43741e183 100644 --- a/client/ayon_core/pipeline/create/utils.py +++ b/client/ayon_core/pipeline/create/utils.py @@ -2,8 +2,6 @@ import collections import ayon_api -from ayon_core.client import get_last_versions - def get_last_versions_for_instances( project_name, instances, use_value_for_missing=False @@ -82,19 +80,19 @@ def get_last_versions_for_instances( if not product_entities_by_id: return output - last_versions_by_product_id = get_last_versions( + last_versions_by_product_id = ayon_api.get_last_versions( project_name, product_entities_by_id.keys(), - fields=["name", "parent"] + fields={"version", "productId"} ) - for product_id, version_doc in last_versions_by_product_id.items(): + for product_id, version_entity in last_versions_by_product_id.items(): product_entity = product_entities_by_id[product_id] product_name = product_entity["name"] folder_id = product_entity["folderId"] folder_path = folder_paths_by_id[folder_id] _instances = instances_by_hierarchy[folder_path][product_name] for instance in _instances: - output[instance.id] = version_doc["name"] + output[instance.id] = version_entity["version"] return output diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 3fce6a465c..23db1e37be 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -1,21 +1,20 @@ -import copy -import attr -import pyblish.api import os -import clique -from copy import deepcopy +import copy import re import warnings +from copy import deepcopy + +import attr +import ayon_api +import pyblish.api +import clique from ayon_core.pipeline import ( get_current_project_name, get_representation_path, Anatomy, ) -from ayon_core.client import ( - get_last_version_by_subset_name, - get_representations -) +from ayon_core.client import get_representations from ayon_core.lib import Logger from ayon_core.pipeline.publish import KnownPublishError from ayon_core.pipeline.farm.patterning import match_aov_pattern @@ -78,16 +77,19 @@ def extend_frames(folder_path, product_name, start, end): prev_end = None project_name = get_current_project_name() - version = get_last_version_by_subset_name( + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path, fields={"id"} + ) + version_entity = ayon_api.get_last_version_by_product_name( project_name, product_name, - asset_name=folder_path + folder_entity["id"] ) # Set prev start / end frames for comparison if not prev_start and not prev_end: - prev_start = version["data"]["frameStart"] - prev_end = version["data"]["frameEnd"] + prev_start = version_entity["attrib"]["frameStart"] + prev_end = version_entity["attrib"]["frameEnd"] updated_start = min(start, prev_start) updated_end = max(end, prev_end) @@ -692,7 +694,7 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, return instances -def get_resources(project_name, version, extension=None): +def get_resources(project_name, version_entity, extension=None): """Get the files from the specific version. This will return all get all files from representation. @@ -710,7 +712,7 @@ def get_resources(project_name, version, extension=None): Args: project_name (str): Name of the project. - version (dict): Version document. + version_entity (dict): Version entity. extension (str): extension used to filter representations. @@ -728,7 +730,7 @@ def get_resources(project_name, version, extension=None): # there is a `context_filter` argument that won't probably work in # final release of AYON. SO we'll rather not use it repre_docs = list(get_representations( - project_name, version_ids=[version["_id"]])) + project_name, version_ids=[version_entity["id"]])) filtered = [] for doc in repre_docs: @@ -1005,18 +1007,22 @@ def copy_extend_frames(instance, representation): project_name = instance.context.data["project"] anatomy = instance.context.data["anatomy"] # type: Anatomy + folder_entity = ayon_api.get_folder_by_path( + project_name, instance.data.get("folderPath") + ) + # get latest version of product # this will stop if product wasn't published yet - version = get_last_version_by_subset_name( + version_entity = ayon_api.get_last_version_by_product_name( project_name, instance.data.get("productName"), - asset_name=instance.data.get("folderPath") + folder_entity["id"] ) # get its files based on extension product_resources = get_resources( - project_name, version, representation.get("ext") + project_name, version_entity, representation.get("ext") ) r_col, _ = clique.assemble(product_resources) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index ceb4038b6e..ed0a7c4848 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -1,6 +1,5 @@ import os import platform -import copy import logging import inspect import collections @@ -10,12 +9,6 @@ import ayon_api from ayon_core.host import ILoadHost from ayon_core.client import ( - get_versions, - get_version_by_id, - get_last_version_by_subset_id, - get_hero_version_by_subset_id, - get_version_by_name, - get_last_versions, get_representations, get_representation_by_id, get_representation_by_name, @@ -122,33 +115,15 @@ def get_contexts_for_repre_docs(project_name, repre_docs): version_ids.add(repre_doc["parent"]) repre_docs_by_id[repre_doc["_id"]] = repre_doc - version_docs = get_versions( - project_name, version_ids, hero=True + version_entities = ayon_api.get_versions( + project_name, version_ids=version_ids ) - version_docs_by_id = {} - hero_version_docs = [] - versions_for_hero = set() + version_entities_by_id = {} product_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 - product_ids.add(version_doc["parent"]) - - if versions_for_hero: - _version_docs = get_versions(project_name, 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 + for version_entity in version_entities: + version_entities_by_id[version_entity["id"]] = version_entity + product_ids.add(version_entity["productId"]) product_entities = ayon_api.get_products( project_name, product_ids=product_ids @@ -169,14 +144,14 @@ def get_contexts_for_repre_docs(project_name, repre_docs): project_entity = ayon_api.get_project(project_name) for repre_id, repre_doc in repre_docs_by_id.items(): - version_doc = version_docs_by_id[repre_doc["parent"]] - product_entity = product_entities_by_id[version_doc["parent"]] + version_entity = version_entities_by_id[repre_doc["parent"]] + product_entity = product_entities_by_id[version_entity["productId"]] folder_entity = folder_entities_by_id[product_entity["folderId"]] context = { "project": project_entity, "folder": folder_entity, "product": product_entity, - "version": version_doc, + "version": version_entity, "representation": repre_doc, } contexts[repre_id] = context @@ -257,12 +232,12 @@ def get_representation_context(representation): raise AssertionError("Representation was not found in database") ( - version_doc, + version_entity, product_entity, folder_entity, project_entity ) = get_representation_parents(project_name, representation) - if not version_doc: + if not version_entity: raise AssertionError("Version was not found in database") if not product_entity: raise AssertionError("Product was not found in database") @@ -275,7 +250,7 @@ def get_representation_context(representation): "project": project_entity, "folder": folder_entity, "product": product_entity, - "version": version_doc, + "version": version_entity, "representation": representation, } @@ -464,39 +439,48 @@ def update_container(container, version=-1): assert current_representation is not None, "This is a bug" - current_version = get_version_by_id( - project_name, current_representation["parent"], fields=["parent"] + current_version_id = current_representation["parent"] + current_version = ayon_api.get_version_by_id( + project_name, current_version_id, fields={"productId"} ) - if version == -1: - new_version = get_last_version_by_subset_id( - project_name, current_version["parent"], fields=["_id"] + if isinstance(version, HeroVersionType): + new_version = ayon_api.get_hero_version_by_product_id( + project_name, current_version["productId"], fields={"id"} ) - - elif isinstance(version, HeroVersionType): - new_version = get_hero_version_by_subset_id( - project_name, current_version["parent"], fields=["_id"] + elif version == -1: + new_version = ayon_api.get_last_version_by_product_id( + project_name, current_version["productId"], fields={"id"} ) else: - new_version = get_version_by_name( - project_name, version, current_version["parent"], fields=["_id"] + new_version = ayon_api.get_version_by_name( + project_name, version, current_version["productId"], fields={"id"} ) + + if new_version is None: + raise ValueError("Failed to find matching version") + product_entity = ayon_api.get_product_by_id( - project_name, current_version["parent"] + project_name, current_version["productId"] ) folder_entity = ayon_api.get_folder_by_id( project_name, product_entity["folderId"] ) - assert new_version is not None, "This is a bug" - + repre_name = current_representation["name"] new_representation = get_representation_by_name( - project_name, current_representation["name"], new_version["_id"] + project_name, repre_name, new_version["id"] ) - assert new_representation is not None, "Representation wasn't found" + if new_representation is None: + raise ValueError( + "Representation '{}' wasn't found on requested version".format( + repre_name + ) + ) path = get_representation_path(new_representation) - assert os.path.exists(path), "Path {} doesn't exist".format(path) + if not path or not os.path.exists(path): + raise ValueError("Path {} doesn't exist".format(path)) # Run update on the Loader for this container Loader = _get_container_loader(container) @@ -858,38 +842,40 @@ def filter_containers(containers, project_name): # Query version docs to get it's product ids # - also query hero version to be able identify if representation # belongs to existing version - version_docs = get_versions( + version_entities = ayon_api.get_versions( project_name, version_ids=repre_docs_by_version_id.keys(), hero=True, - fields=["_id", "parent", "type"] + fields={"id", "productId", "version"} ) verisons_by_id = {} versions_by_product_id = collections.defaultdict(list) hero_version_ids = set() - for version_doc in version_docs: - version_id = version_doc["_id"] + for version_entity in version_entities: + version_id = version_entity["id"] # Store versions by their ids - verisons_by_id[version_id] = version_doc + verisons_by_id[version_id] = version_entity # There's no need to query products for hero versions # - they are considered as latest? - if version_doc["type"] == "hero_version": + if version_entity["version"] < 0: hero_version_ids.add(version_id) continue - product_id = version_doc["parent"] - versions_by_product_id[product_id].append(version_doc) + product_id = version_entity["productId"] + versions_by_product_id[product_id].append(version_entity) - last_versions = get_last_versions( + last_versions = ayon_api.get_last_versions( project_name, - subset_ids=versions_by_product_id.keys(), - fields=["_id"] + versions_by_product_id.keys(), + fields={"id"} ) # Figure out which versions are outdated outdated_version_ids = set() - for product_id, last_version_doc in last_versions.items(): - for version_doc in versions_by_product_id[product_id]: - version_id = version_doc["_id"] - if version_id != last_version_doc["_id"]: + for product_id, last_version_entity in last_versions.items(): + for version_entity in versions_by_product_id[product_id]: + version_id = version_entity["id"] + if version_id in hero_version_ids: + continue + if version_id != last_version_entity["id"]: outdated_version_ids.add(version_id) # Based on all collected data figure out which containers are outdated diff --git a/client/ayon_core/pipeline/workfile/build_workfile.py b/client/ayon_core/pipeline/workfile/build_workfile.py index ec8681d09f..766b7baf04 100644 --- a/client/ayon_core/pipeline/workfile/build_workfile.py +++ b/client/ayon_core/pipeline/workfile/build_workfile.py @@ -15,10 +15,7 @@ import json import ayon_api -from ayon_core.client import ( - get_last_versions, - get_representations, -) +from ayon_core.client import get_representations from ayon_core.settings import get_project_settings from ayon_core.lib import ( filter_profiles, @@ -471,9 +468,9 @@ class BuildWorkfile: products_by_id[product_entity["id"]] = product_entity version_data = in_data["version"] - version_doc = version_data["version_doc"] - version_by_product_id[product_id] = version_doc - repres_by_version_id[version_doc["_id"]] = ( + version_entity = version_data["version_entity"] + version_by_product_id[product_id] = version_entity + repres_by_version_id[version_entity["id"]] = ( version_data["repres"] ) @@ -494,8 +491,8 @@ class BuildWorkfile: for product_id, profile in profiles_by_product_id.items(): profile_repre_names = profile["repre_names_lowered"] - version_doc = version_by_product_id[product_id] - version_id = version_doc["_id"] + version_entity = version_by_product_id[product_id] + version_id = version_entity["id"] repres = repres_by_version_id[version_id] for repre in repres: repre_name_low = repre["name"].lower() @@ -671,7 +668,7 @@ class BuildWorkfile: : { "product_entity": , "version": { - "version_doc": , + "version_entity": , "repres": [ , , ... ] @@ -706,22 +703,22 @@ class BuildWorkfile: for product_entity in product_entities } - last_version_by_product_id = get_last_versions( + last_version_by_product_id = ayon_api.get_last_versions( project_name, product_entities_by_id.keys() ) - last_version_docs_by_id = { - version["_id"]: version - for version in last_version_by_product_id.values() + last_version_entities_by_id = { + version_entity["id"]: version_entity + for version_entity in last_version_by_product_id.values() } repre_docs = get_representations( - project_name, version_ids=last_version_docs_by_id.keys() + project_name, version_ids=last_version_entities_by_id.keys() ) for repre_doc in repre_docs: version_id = repre_doc["parent"] - version_doc = last_version_docs_by_id[version_id] + version_entity = last_version_entities_by_id[version_id] - product_id = version_doc["parent"] + product_id = version_entity["productId"] product_entity = product_entities_by_id[product_id] folder_id = product_entity["folderId"] @@ -737,7 +734,7 @@ class BuildWorkfile: output[folder_id]["products"][product_id] = { "product_entity": product_entity, "version": { - "version_doc": version_doc, + "version_entity": version_entity, "repres": [] } } From 46e131d0e680144338a300e3375cbe7cfa8628a6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 6 Mar 2024 10:56:34 +0100 Subject: [PATCH 453/573] global plugins are using version entities --- .../plugins/load/delete_old_versions.py | 19 ++++++------- .../ayon_core/plugins/load/push_to_library.py | 6 ++--- .../publish/collect_anatomy_instance_data.py | 14 +++++----- .../plugins/publish/collect_audio.py | 15 +++++------ .../plugins/publish/collect_frames_fix.py | 20 +++++++------- .../plugins/publish/integrate_inputlinks.py | 7 +++-- .../plugins/publish/integrate_thumbnail.py | 27 +++++++++---------- .../publish/integrate_version_attrs.py | 2 +- 8 files changed, 51 insertions(+), 59 deletions(-) diff --git a/client/ayon_core/plugins/load/delete_old_versions.py b/client/ayon_core/plugins/load/delete_old_versions.py index 4c5bed899e..6b9fe3ba54 100644 --- a/client/ayon_core/plugins/load/delete_old_versions.py +++ b/client/ayon_core/plugins/load/delete_old_versions.py @@ -5,12 +5,13 @@ # import uuid # # import clique +# import ayon_api # from pymongo import UpdateOne # import qargparse # from qtpy import QtWidgets, QtCore # # from ayon_core import style -# from ayon_core.client import get_versions, get_representations +# from ayon_core.client import get_representations # from ayon_core.addon import AddonsManager # from ayon_core.lib import format_file_size # from ayon_core.pipeline import load, Anatomy @@ -201,16 +202,16 @@ # project_name = context["project"]["name"] # anatomy = Anatomy(project_name) # -# versions = list(get_versions( -# project_name, subset_ids=[product_entity["id"]] +# versions = list(ayon_api.get_versions( +# project_name, product_ids=[product_entity["id"]] # )) # # versions_by_parent = collections.defaultdict(list) # for ent in versions: -# versions_by_parent[ent["parent"]].append(ent) +# versions_by_parent[ent["productId"]].append(ent) # # def sort_func(ent): -# return int(ent["name"]) +# return int(ent["version"]) # # all_last_versions = [] # for _parent_id, _versions in versions_by_parent.items(): @@ -230,7 +231,7 @@ # # Update versions_by_parent without filtered versions # versions_by_parent = collections.defaultdict(list) # for ent in versions: -# versions_by_parent[ent["parent"]].append(ent) +# versions_by_parent[ent["productId"]].append(ent) # # # Filter already deleted versions # versions_to_pop = [] @@ -243,14 +244,14 @@ # msg = "Folder: \"{}\" | Product: \"{}\" | Version: \"{}\"".format( # folder_entity["path"], # product_entity["name"], -# version["name"] +# version["version"] # ) # self.log.debug(( # "Skipping version. Already tagged as `deleted`. < {} >" # ).format(msg)) # versions.remove(version) # -# version_ids = [ent["_id"] for ent in versions] +# version_ids = [ent["id"] for ent in versions] # # self.log.debug( # "Filtered versions to delete ({})".format(len(version_ids)) @@ -346,7 +347,7 @@ # if version_tags == orig_version_tags: # continue # -# update_query = {"_id": version["_id"]} +# update_query = {"_id": version["id"]} # update_data = {"$set": {"data.tags": version_tags}} # mongo_changes_bulk.append(UpdateOne(update_query, update_data)) # diff --git a/client/ayon_core/plugins/load/push_to_library.py b/client/ayon_core/plugins/load/push_to_library.py index 3fcba3ae7a..a191ee88f0 100644 --- a/client/ayon_core/plugins/load/push_to_library.py +++ b/client/ayon_core/plugins/load/push_to_library.py @@ -40,10 +40,8 @@ class PushToLibraryProject(load.ProductLoaderPlugin): "main.py" ) - project_entity = context["project"] - version_doc = context["version"] - project_name = project_entity["name"] - version_id = str(version_doc["_id"]) + project_name = context["project"]["name"] + version_id = context["version"]["id"] args = get_ayon_launcher_args( "run", diff --git a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py index 2f28f00ac5..48f674ae0f 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py @@ -33,9 +33,6 @@ import collections import pyblish.api import ayon_api -from ayon_core.client import ( - get_last_versions, -) from ayon_core.pipeline.version_start import get_versioning_start @@ -271,20 +268,21 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): for product_entity in product_entities } - last_version_docs_by_product_id = get_last_versions( - project_name, product_ids, fields=["name"] + last_versions_by_product_id = ayon_api.get_last_versions( + project_name, product_ids, fields={"version"} ) for product_entity in product_entities: product_id = product_entity["id"] - last_version_doc = last_version_docs_by_product_id.get(product_id) - if last_version_doc is None: + last_version_entity = last_versions_by_product_id.get(product_id) + if last_version_entity is None: continue + last_version = last_version_entity["version"] folder_id = product_entity["folderId"] product_name = product_entity["name"] _instances = hierarchy[folder_id][product_name] for _instance in _instances: - _instance.data["latestVersion"] = last_version_doc["name"] + _instance.data["latestVersion"] = last_version def fill_anatomy_data(self, context): self.log.debug("Storing anatomy data to instance data.") diff --git a/client/ayon_core/plugins/publish/collect_audio.py b/client/ayon_core/plugins/publish/collect_audio.py index 920cc950fb..f6946b1950 100644 --- a/client/ayon_core/plugins/publish/collect_audio.py +++ b/client/ayon_core/plugins/publish/collect_audio.py @@ -3,10 +3,7 @@ import collections import ayon_api import pyblish.api -from ayon_core.client import ( - get_last_versions, - get_representations, -) +from ayon_core.client import get_representations from ayon_core.pipeline.load import get_representation_path_with_anatomy @@ -148,12 +145,14 @@ class CollectAudio(pyblish.api.ContextPlugin): return output # Find all latest versions for the products - version_docs_by_product_id = get_last_versions( - project_name, subset_ids=product_ids, fields=["_id", "parent"] + last_versions_by_product_id = ayon_api.get_last_versions( + project_name, product_ids=product_ids, fields={"id", "productId"} ) version_id_by_product_id = { - product_id: version_doc["_id"] - for product_id, version_doc in version_docs_by_product_id.items() + product_id: version_entity["id"] + for product_id, version_entity in ( + last_versions_by_product_id.items() + ) } version_ids = set(version_id_by_product_id.values()) if not version_ids: diff --git a/client/ayon_core/plugins/publish/collect_frames_fix.py b/client/ayon_core/plugins/publish/collect_frames_fix.py index bd2379984c..7f6c003f5e 100644 --- a/client/ayon_core/plugins/publish/collect_frames_fix.py +++ b/client/ayon_core/plugins/publish/collect_frames_fix.py @@ -1,14 +1,12 @@ import pyblish.api +import ayon_api + from ayon_core.lib.attribute_definitions import ( TextDef, BoolDef ) - from ayon_core.pipeline.publish import AYONPyblishPluginMixin -from ayon_core.client.entities import ( - get_last_version_by_subset_name, - get_representations -) +from ayon_core.client.entities import get_representations class CollectFramesFixDef( @@ -41,24 +39,24 @@ class CollectFramesFixDef( instance.data["frames_to_fix"] = frames_to_fix product_name = instance.data["productName"] - folder_path = instance.data["folderPath"] + folder_entity = instance.data["folderEntity"] project_entity = instance.data["projectEntity"] project_name = project_entity["name"] - version = get_last_version_by_subset_name( + version_entity = ayon_api.get_last_version_by_product_name( project_name, product_name, - asset_name=folder_path + folder_entity["id"] ) - if not version: + if not version_entity: self.log.warning( "No last version found, re-render not possible" ) return representations = get_representations( - project_name, version_ids=[version["_id"]] + project_name, version_ids=[version_entity["id"]] ) published_files = [] for repre in representations: @@ -73,7 +71,7 @@ class CollectFramesFixDef( instance.data["last_version_published_files"])) if self.rewrite_version_enable and rewrite_version: - instance.data["version"] = version["name"] + instance.data["version"] = version_entity["version"] # limits triggering version validator instance.data.pop("latestVersion") diff --git a/client/ayon_core/plugins/publish/integrate_inputlinks.py b/client/ayon_core/plugins/publish/integrate_inputlinks.py index f7e802f410..1593e6ac7e 100644 --- a/client/ayon_core/plugins/publish/integrate_inputlinks.py +++ b/client/ayon_core/plugins/publish/integrate_inputlinks.py @@ -55,8 +55,7 @@ class IntegrateInputLinksAYON(pyblish.api.ContextPlugin): if not instance.data.get("publish", True): continue - version_doc = instance.data.get("versionEntity") - if not version_doc: + if not instance.data.get("versionEntity"): self.log.debug( "Instance {} doesn't have version.".format(instance)) continue @@ -88,14 +87,14 @@ class IntegrateInputLinksAYON(pyblish.api.ContextPlugin): self.log.warn("No workfile in this publish session.") return - workfile_version_id = workfile_instance.data["versionEntity"]["_id"] + workfile_version_id = workfile_instance.data["versionEntity"]["id"] # link workfile to all publishing versions for instance in other_instances: self.add_link( new_links_by_type, "generative", workfile_version_id, - instance.data["versionEntity"]["_id"], + instance.data["versionEntity"]["id"], ) loaded_versions = workfile_instance.context.get("loadedVersions") diff --git a/client/ayon_core/plugins/publish/integrate_thumbnail.py b/client/ayon_core/plugins/publish/integrate_thumbnail.py index 362c5686ab..6d470feb5f 100644 --- a/client/ayon_core/plugins/publish/integrate_thumbnail.py +++ b/client/ayon_core/plugins/publish/integrate_thumbnail.py @@ -26,8 +26,8 @@ import os import collections import pyblish.api +import ayon_api -from ayon_core.client import get_versions from ayon_core.client.operations import OperationsSession InstanceFilterResult = collections.namedtuple( @@ -59,20 +59,20 @@ class IntegrateThumbnailsAYON(pyblish.api.ContextPlugin): for instance_items in filtered_instance_items } # Query versions - version_docs = get_versions( + version_entities = ayon_api.get_versions( project_name, version_ids=version_ids, hero=True, - fields=["_id", "type", "name"] + fields={"id", "version"} ) # Store version by their id (converted to string) - version_docs_by_str_id = { - str(version_doc["_id"]): version_doc - for version_doc in version_docs + version_entities_by_id = { + version_entity["id"]: version_entity + for version_entity in version_entities } self._integrate_thumbnails( filtered_instance_items, - version_docs_by_str_id, + version_entities_by_id, project_name ) @@ -159,7 +159,7 @@ class IntegrateThumbnailsAYON(pyblish.api.ContextPlugin): def _integrate_thumbnails( self, filtered_instance_items, - version_docs_by_str_id, + version_entities_by_id, project_name ): from ayon_core.client.operations import create_thumbnail @@ -169,8 +169,8 @@ class IntegrateThumbnailsAYON(pyblish.api.ContextPlugin): for instance_item in filtered_instance_items: instance, thumbnail_path, version_id = instance_item instance_label = self._get_instance_label(instance) - version_doc = version_docs_by_str_id.get(version_id) - if not version_doc: + version_entity = version_entities_by_id.get(version_id) + if not version_entity: self.log.warning(( "Version entity for instance \"{}\" was not found." ).format(instance_label)) @@ -181,12 +181,11 @@ class IntegrateThumbnailsAYON(pyblish.api.ContextPlugin): # Set thumbnail id for version thumbnail_info_by_entity_id[version_id] = { "thumbnail_id": thumbnail_id, - "entity_type": version_doc["type"], + "entity_type": "version", } - if version_doc["type"] == "hero_version": + version_name = version_entity["version"] + if version_name < 0: version_name = "Hero" - else: - version_name = version_doc["name"] self.log.debug("Setting thumbnail for version \"{}\" <{}>".format( version_name, version_id )) diff --git a/client/ayon_core/plugins/publish/integrate_version_attrs.py b/client/ayon_core/plugins/publish/integrate_version_attrs.py index bc09af9db0..a5d6a17396 100644 --- a/client/ayon_core/plugins/publish/integrate_version_attrs.py +++ b/client/ayon_core/plugins/publish/integrate_version_attrs.py @@ -61,7 +61,7 @@ class IntegrateVersionAttributes(pyblish.api.ContextPlugin): op_session.update_entity( project_name, "version", - version_entity["_id"], + version_entity["id"], {"attrib": filtered_attributes} ) From 34327d7f754b037fd7f548410ea1f017a7043981 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 6 Mar 2024 10:59:15 +0100 Subject: [PATCH 454/573] loade plugins using version entity --- .../hosts/flame/plugins/load/load_clip.py | 17 +-- .../flame/plugins/load/load_clip_batch.py | 8 +- .../hosts/fusion/plugins/load/actions.py | 19 +-- .../fusion/plugins/load/load_sequence.py | 10 +- .../hosts/hiero/plugins/load/load_clip.py | 77 +++++----- .../hosts/hiero/plugins/load/load_effects.py | 11 +- .../hosts/maya/plugins/load/actions.py | 19 +-- .../maya/plugins/load/load_arnold_standin.py | 7 +- .../hosts/maya/plugins/load/load_look.py | 11 +- .../hosts/maya/plugins/load/load_vrayproxy.py | 15 +- .../hosts/nuke/plugins/load/actions.py | 27 ++-- .../hosts/nuke/plugins/load/load_backdrop.py | 53 +++---- .../nuke/plugins/load/load_camera_abc.py | 72 +++++---- .../hosts/nuke/plugins/load/load_clip.py | 143 +++++++++--------- .../hosts/nuke/plugins/load/load_effects.py | 82 +++++----- .../nuke/plugins/load/load_effects_ip.py | 77 +++++----- .../hosts/nuke/plugins/load/load_gizmo.py | 79 +++++----- .../hosts/nuke/plugins/load/load_gizmo_ip.py | 78 +++++----- .../hosts/nuke/plugins/load/load_image.py | 81 +++++----- .../hosts/nuke/plugins/load/load_model.py | 70 ++++----- .../hosts/nuke/plugins/load/load_ociolook.py | 27 ++-- .../nuke/plugins/load/load_script_precomp.py | 77 +++++----- client/ayon_core/hosts/resolve/api/plugin.py | 14 +- .../hosts/resolve/plugins/load/load_clip.py | 49 +++--- .../plugins/load/load_geometrycache_abc.py | 3 +- .../plugins/load/load_skeletalmesh_abc.py | 3 +- .../plugins/load/load_skeletalmesh_fbx.py | 3 +- .../plugins/load/load_staticmesh_fbx.py | 3 +- 28 files changed, 556 insertions(+), 579 deletions(-) diff --git a/client/ayon_core/hosts/flame/plugins/load/load_clip.py b/client/ayon_core/hosts/flame/plugins/load/load_clip.py index 72a6f2a585..80b2eb9ec7 100644 --- a/client/ayon_core/hosts/flame/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/flame/plugins/load/load_clip.py @@ -48,9 +48,9 @@ class LoadClip(opfapi.ClipLoader): self.fpd = fproject.current_workspace.desktop # load clip to timeline and get main variables - version = context['version'] - version_data = version.get("data", {}) - version_name = version.get("name", None) + version_entity = context["version"] + version_attributes = version_entity["attrib"] + version_name = version_entity["version"] colorspace = self.get_colorspace(context) # in case output is not in context replace key to representation @@ -112,11 +112,10 @@ class LoadClip(opfapi.ClipLoader): ] # move all version data keys to tag data - data_imprint = {} - for key in add_keys: - data_imprint.update({ - key: version_data.get(key, str(None)) - }) + data_imprint = { + key: version_attributes.get(key, str(None)) + for key in add_keys + } # add variables related to version context data_imprint.update({ @@ -198,7 +197,7 @@ class LoadClip(opfapi.ClipLoader): # }) # version_data = version.get("data", {}) # version_name = version.get("name", None) - # colorspace = version_data.get("colorspace", None) + # colorspace = version_data.get("colorSpace", None) # object_name = "{}_{}".format(name, namespace) # file = get_representation_path(repre_doc).replace("\\", "/") # clip = track_item.source() diff --git a/client/ayon_core/hosts/flame/plugins/load/load_clip_batch.py b/client/ayon_core/hosts/flame/plugins/load/load_clip_batch.py index 83ad47c855..7eaaa308df 100644 --- a/client/ayon_core/hosts/flame/plugins/load/load_clip_batch.py +++ b/client/ayon_core/hosts/flame/plugins/load/load_clip_batch.py @@ -45,9 +45,9 @@ class LoadClipBatch(opfapi.ClipLoader): self.batch = options.get("batch") or flame.batch # load clip to timeline and get main variables - version = context['version'] - version_data = version.get("data", {}) - version_name = version.get("name", None) + version_entity = context["version"] + version_attributes =version_entity["attrib"] + version_name = version_entity["version"] colorspace = self.get_colorspace(context) clip_name_template = self.clip_name_template @@ -129,7 +129,7 @@ class LoadClipBatch(opfapi.ClipLoader): # move all version data keys to tag data data_imprint = { - key: version_data.get(key, str(None)) + key: version_attributes.get(key, str(None)) for key in add_keys } # add variables related to version context diff --git a/client/ayon_core/hosts/fusion/plugins/load/actions.py b/client/ayon_core/hosts/fusion/plugins/load/actions.py index f67878bcff..2fac884b2e 100644 --- a/client/ayon_core/hosts/fusion/plugins/load/actions.py +++ b/client/ayon_core/hosts/fusion/plugins/load/actions.py @@ -27,11 +27,10 @@ class FusionSetFrameRangeLoader(load.LoaderPlugin): from ayon_core.hosts.fusion.api import lib - version = context['version'] - version_data = version.get("data", {}) + version_attributes = context["version"]["attrib"] - start = version_data.get("frameStart", None) - end = version_data.get("frameEnd", None) + start = version_attributes.get("frameStart", None) + end = version_attributes.get("frameEnd", None) if start is None or end is None: print("Skipping setting frame range because start or " @@ -62,11 +61,9 @@ class FusionSetFrameRangeWithHandlesLoader(load.LoaderPlugin): from ayon_core.hosts.fusion.api import lib - version = context['version'] - version_data = version.get("data", {}) - - start = version_data.get("frameStart", None) - end = version_data.get("frameEnd", None) + version_attributes = context["version"]["attrib"] + start = version_attributes.get("frameStart", None) + end = version_attributes.get("frameEnd", None) if start is None or end is None: print("Skipping setting frame range because start or " @@ -74,7 +71,7 @@ class FusionSetFrameRangeWithHandlesLoader(load.LoaderPlugin): return # Include handles - start -= version_data.get("handleStart", 0) - end += version_data.get("handleEnd", 0) + start -= version_attributes.get("handleStart", 0) + end += version_attributes.get("handleEnd", 0) lib.update_frame_range(start, end) diff --git a/client/ayon_core/hosts/fusion/plugins/load/load_sequence.py b/client/ayon_core/hosts/fusion/plugins/load/load_sequence.py index d42e0feda9..f53553ad1a 100644 --- a/client/ayon_core/hosts/fusion/plugins/load/load_sequence.py +++ b/client/ayon_core/hosts/fusion/plugins/load/load_sequence.py @@ -266,17 +266,17 @@ class FusionLoadSequence(load.LoaderPlugin): with comp_lock_and_undo_chunk(comp, "Remove Loader"): tool.Delete() - def _get_start(self, version_doc, tool): + def _get_start(self, version_entity, tool): """Return real start frame of published files (incl. handles)""" - data = version_doc["data"] + attributes = version_entity["attrib"] # Get start frame directly with handle if it's in data - start = data.get("frameStartHandle") + start = attributes.get("frameStartHandle") if start is not None: return start # Get frame start without handles - start = data.get("frameStart") + start = attributes.get("frameStart") if start is None: self.log.warning( "Missing start frame for version " @@ -286,7 +286,7 @@ class FusionLoadSequence(load.LoaderPlugin): return 0 # Use `handleStart` if the data is available - handle_start = data.get("handleStart") + handle_start = attributes.get("handleStart") if handle_start: start -= handle_start diff --git a/client/ayon_core/hosts/hiero/plugins/load/load_clip.py b/client/ayon_core/hosts/hiero/plugins/load/load_clip.py index e5ef977c42..b35c5b2f1f 100644 --- a/client/ayon_core/hosts/hiero/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/hiero/plugins/load/load_clip.py @@ -1,11 +1,6 @@ -from ayon_core.client import ( - get_version_by_id, - get_last_version_by_subset_id -) -from ayon_core.pipeline import ( - get_representation_path, - get_current_project_name, -) +import ayon_api + +from ayon_core.pipeline import get_representation_path from ayon_core.lib.transcoding import ( VIDEO_EXTENSIONS, IMAGE_EXTENSIONS @@ -101,10 +96,10 @@ class LoadClip(phiero.SequenceLoader): path = self.filepath_from_context(context) track_item = phiero.ClipLoader(self, context, path, **options).load() namespace = namespace or track_item.name() - version = context['version'] - version_data = version.get("data", {}) - version_name = version.get("name", None) - colorspace = version_data.get("colorspace", None) + version_entity = context["version"] + version_attributes = version_entity["attrib"] + version_name = version_entity["version"] + colorspace = version_attributes.get("colorSpace") object_name = self.clip_name_template.format( **context["representation"]["context"]) @@ -119,11 +114,11 @@ class LoadClip(phiero.SequenceLoader): ] # move all version data keys to tag data - data_imprint = {} - for key in add_keys: - data_imprint.update({ - key: version_data.get(key, str(None)) - }) + data_imprint = { + key: version_attributes.get(key, str(None)) + for key in add_keys + + } # add variables related to version context data_imprint.update({ @@ -133,7 +128,9 @@ class LoadClip(phiero.SequenceLoader): }) # update color of clip regarding the version order - self.set_item_color(track_item, version) + self.set_item_color( + context["project"]["name"], track_item, version_entity + ) # deal with multiselection self.multiselection(track_item) @@ -152,17 +149,18 @@ class LoadClip(phiero.SequenceLoader): def update(self, container, context): """ Updating previously loaded clips """ - version_doc = context["version"] + version_entity = context["version"] repre_doc = context["representation"] + # load clip to timeline and get main variables - name = container['name'] - namespace = container['namespace'] + name = container["name"] + namespace = container["namespace"] track_item = phiero.get_track_items( track_item_name=namespace).pop() - version_data = version_doc.get("data", {}) - version_name = version_doc.get("name", None) - colorspace = version_data.get("colorspace", None) + version_attributes = version_entity["attrib"] + version_name = version_entity["version"] + colorspace = version_attributes.get("colorSpace") object_name = "{}_{}".format(name, namespace) file = get_representation_path(repre_doc).replace("\\", "/") clip = track_item.source() @@ -174,17 +172,21 @@ class LoadClip(phiero.SequenceLoader): if colorspace: clip.setSourceMediaColourTransform(colorspace) - # add additional metadata from the version to imprint Avalon knob - add_keys = [ - "frameStart", "frameEnd", "source", "author", - "fps", "handleStart", "handleEnd" - ] + # add additional metadata from the version to imprint metadata knob # move all version data keys to tag data data_imprint = {} - for key in add_keys: + for key in [ + "frameStart", + "frameEnd", + "source", + "author", + "fps", + "handleStart", + "handleEnd", + ]: data_imprint.update({ - key: version_data.get(key, str(None)) + key: version_attributes.get(key, str(None)) }) # add variables related to version context @@ -196,7 +198,9 @@ class LoadClip(phiero.SequenceLoader): }) # update color of clip regarding the version order - self.set_item_color(track_item, version_doc) + self.set_item_color( + context["project"]["name"], track_item, version_entity + ) return phiero.update_container(track_item, data_imprint) @@ -219,14 +223,13 @@ class LoadClip(phiero.SequenceLoader): cls.sequence = cls.track.parent() @classmethod - def set_item_color(cls, track_item, version_doc): - project_name = get_current_project_name() - last_version_doc = get_last_version_by_subset_id( - project_name, version_doc["parent"], fields=["_id"] + def set_item_color(cls, project_name, track_item, version_entity): + last_version_entity = ayon_api.get_last_version_by_product_id( + project_name, version_entity["productId"], fields={"id"} ) clip = track_item.source() # set clip colour - if version_doc["_id"] == last_version_doc["_id"]: + if version_entity["id"] == last_version_entity["id"]: clip.binItem().setColor(cls.clip_color_last) else: clip.binItem().setColor(cls.clip_color) diff --git a/client/ayon_core/hosts/hiero/plugins/load/load_effects.py b/client/ayon_core/hosts/hiero/plugins/load/load_effects.py index c6ac20bb04..90666406fd 100644 --- a/client/ayon_core/hosts/hiero/plugins/load/load_effects.py +++ b/client/ayon_core/hosts/hiero/plugins/load/load_effects.py @@ -2,13 +2,10 @@ import json from collections import OrderedDict import six -from ayon_core.client import get_version_by_id - from ayon_core.pipeline import ( AVALON_CONTAINER_ID, load, get_representation_path, - get_current_project_name ) from ayon_core.hosts.hiero import api as phiero from ayon_core.lib import Logger @@ -158,7 +155,7 @@ class LoadEffects(load.LoaderPlugin): def update(self, container, context): """ Updating previously loaded effects """ - version_doc = context["version"] + version_entity = context["version"] repre_doc = context["representation"] active_track = container["_item"] file = get_representation_path(repre_doc).replace("\\", "/") @@ -168,9 +165,9 @@ class LoadEffects(load.LoaderPlugin): namespace = container['namespace'] # get timeline in out data - version_data = version_doc["data"] - clip_in = version_data["clipIn"] - clip_out = version_data["clipOut"] + version_attributes = version_entity["attrib"] + clip_in = version_attributes["clipIn"] + clip_out = version_attributes["clipOut"] object_name = "{}_{}".format(name, namespace) diff --git a/client/ayon_core/hosts/maya/plugins/load/actions.py b/client/ayon_core/hosts/maya/plugins/load/actions.py index f979623544..5f4095eeec 100644 --- a/client/ayon_core/hosts/maya/plugins/load/actions.py +++ b/client/ayon_core/hosts/maya/plugins/load/actions.py @@ -28,11 +28,9 @@ class SetFrameRangeLoader(load.LoaderPlugin): import maya.cmds as cmds - version = context['version'] - version_data = version.get("data", {}) - - start = version_data.get("frameStart", None) - end = version_data.get("frameEnd", None) + version_attributes = context["version"]["attrib"] + start = version_attributes.get("frameStart") + end = version_attributes.get("frameEnd") if start is None or end is None: print("Skipping setting frame range because start or " @@ -63,11 +61,10 @@ class SetFrameRangeWithHandlesLoader(load.LoaderPlugin): import maya.cmds as cmds - version = context['version'] - version_data = version.get("data", {}) + version_attributes = context["version"]["attrib"] - start = version_data.get("frameStart", None) - end = version_data.get("frameEnd", None) + start = version_attributes.get("frameStart") + end = version_attributes.get("frameEnd") if start is None or end is None: print("Skipping setting frame range because start or " @@ -75,8 +72,8 @@ class SetFrameRangeWithHandlesLoader(load.LoaderPlugin): return # Include handles - start -= version_data.get("handleStart", 0) - end += version_data.get("handleEnd", 0) + start -= version_attributes.get("handleStart", 0) + end += version_attributes.get("handleEnd", 0) cmds.playbackOptions(minTime=start, maxTime=end, diff --git a/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py b/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py index 6a3d488dc6..c7b5ed9d6c 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py @@ -49,10 +49,9 @@ class ArnoldStandinLoader(load.LoaderPlugin): import mtoa.ui.arnoldmenu - version = context['version'] - version_data = version.get("data", {}) + version_attributes = context["version"]["attrib"] - self.log.info("version_data: {}\n".format(version_data)) + self.log.info("version_attributes: {}\n".format(version_attributes)) folder_name = context["folder"]["name"] namespace = namespace or unique_namespace( @@ -95,7 +94,7 @@ class ArnoldStandinLoader(load.LoaderPlugin): sequence = is_sequence(os.listdir(os.path.dirname(repre_path))) cmds.setAttr(standin_shape + ".useFrameExtension", sequence) - fps = float(version["data"].get("fps")) or 25 + fps = float(version_attributes.get("fps")) or 25 cmds.setAttr(standin_shape + ".abcFPS", fps) nodes = [root, standin, standin_shape] diff --git a/client/ayon_core/hosts/maya/plugins/load/load_look.py b/client/ayon_core/hosts/maya/plugins/load/load_look.py index fb5be14aa1..186c6638b8 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_look.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_look.py @@ -6,10 +6,7 @@ from collections import defaultdict from qtpy import QtWidgets from ayon_core.client import get_representation_by_name -from ayon_core.pipeline import ( - get_current_project_name, - get_representation_path, -) +from ayon_core.pipeline import get_representation_path import ayon_core.hosts.maya.api.plugin from ayon_core.hosts.maya.api import lib from ayon_core.hosts.maya.api.lib import get_reference_node @@ -78,10 +75,10 @@ class LookLoader(ayon_core.hosts.maya.api.plugin.ReferenceLoader): shader_nodes = cmds.ls(members, type='shadingEngine') nodes = set(self._get_nodes_with_shader(shader_nodes)) - version_doc = context["version"] - project_name = get_current_project_name() + version_id = context["version"]["id"] + project_name = context["project"]["name"] json_representation = get_representation_by_name( - project_name, "json", version_doc["_id"] + project_name, "json", version_id ) # Load relationships diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py b/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py index 97f45bbaa3..3e77fd41fa 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py @@ -54,7 +54,9 @@ class VRayProxyLoader(load.LoaderPlugin): product_type = "vrayproxy" # get all representations for this version - filename = self._get_abc(context["version"]["_id"]) + filename = self._get_abc( + context["project"]["name"], context["version"]["id"] + ) if not filename: filename = self.filepath_from_context(context) @@ -108,10 +110,11 @@ class VRayProxyLoader(load.LoaderPlugin): # get all representations for this version repre_doc = context["representation"] - filename = ( - self._get_abc(repre_doc["parent"]) - or get_representation_path(repre_doc) + filename = self._get_abc( + context["project"]["name"], context["version"]["id"] ) + if not filename: + filename = get_representation_path(repre_doc) for vray_mesh in vraymeshes: cmds.setAttr("{}.fileName".format(vray_mesh), @@ -170,7 +173,7 @@ class VRayProxyLoader(load.LoaderPlugin): return [parent, proxy], parent - def _get_abc(self, version_id): + def _get_abc(self, project_name, version_id): # type: (str) -> str """Get abc representation file path if present. @@ -178,6 +181,7 @@ class VRayProxyLoader(load.LoaderPlugin): vray proxy, get is file path. Args: + project_name (str): Project name. version_id (str): Version hash id. Returns: @@ -187,7 +191,6 @@ class VRayProxyLoader(load.LoaderPlugin): """ self.log.debug( "Looking for abc in published representations of this version.") - project_name = get_current_project_name() abc_rep = get_representation_by_name(project_name, "abc", version_id) if abc_rep: self.log.debug("Found, we'll link alembic to vray proxy.") diff --git a/client/ayon_core/hosts/nuke/plugins/load/actions.py b/client/ayon_core/hosts/nuke/plugins/load/actions.py index de51321924..707a8a3c4d 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/actions.py +++ b/client/ayon_core/hosts/nuke/plugins/load/actions.py @@ -4,6 +4,7 @@ from ayon_core.lib import Logger from ayon_core.pipeline import load +from ayon_core.hosts.nuke.api import lib log = Logger.get_logger(__name__) @@ -25,14 +26,11 @@ class SetFrameRangeLoader(load.LoaderPlugin): color = "white" def load(self, context, name, namespace, data): + version_entity = context["version"] + version_attributes = version_entity["attrib"] - from ayon_core.hosts.nuke.api import lib - - version = context['version'] - version_data = version.get("data", {}) - - start = version_data.get("frameStart", None) - end = version_data.get("frameEnd", None) + start = version_attributes.get("frameStart") + end = version_attributes.get("frameEnd") log.info("start: {}, end: {}".format(start, end)) if start is None or end is None: @@ -59,14 +57,9 @@ class SetFrameRangeWithHandlesLoader(load.LoaderPlugin): color = "white" def load(self, context, name, namespace, data): - - from ayon_core.hosts.nuke.api import lib - - version = context['version'] - version_data = version.get("data", {}) - - start = version_data.get("frameStart", None) - end = version_data.get("frameEnd", None) + version_attributes = context["version"]["attrib"] + start = version_attributes.get("frameStart") + end = version_attributes.get("frameEnd") if start is None or end is None: print("Skipping setting frame range because start or " @@ -74,7 +67,7 @@ class SetFrameRangeWithHandlesLoader(load.LoaderPlugin): return # Include handles - start -= version_data.get("handleStart", 0) - end += version_data.get("handleEnd", 0) + start -= version_attributes.get("handleStart", 0) + end += version_attributes.get("handleEnd", 0) lib.update_frame_range(start, end) diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py b/client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py index b97943bd63..888e8ec62a 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py @@ -1,13 +1,9 @@ import nuke import nukescripts +import ayon_api -from ayon_core.client import ( - get_version_by_id, - get_last_version_by_subset_id, -) from ayon_core.pipeline import ( load, - get_current_project_name, get_representation_path, ) from ayon_core.hosts.nuke.api.lib import ( @@ -51,24 +47,23 @@ class LoadBackdropNodes(load.LoaderPlugin): """ # get main variables - version = context['version'] - version_data = version.get("data", {}) - vname = version.get("name", None) namespace = namespace or context["folder"]["name"] - colorspace = version_data.get("colorspace", None) + version_entity = context["version"] + + version_attributes = version_entity["attrib"] + colorspace = version_attributes.get("colorSpace") + object_name = "{}_{}".format(name, namespace) # prepare data for imprinting - # add additional metadata from the version to imprint to Avalon knob - add_keys = ["source", "author", "fps"] - data_imprint = { - "version": vname, + "version": version_entity["version"], "colorspaceInput": colorspace } - for k in add_keys: - data_imprint.update({k: version_data[k]}) + # add attributes from the version to imprint to metadata knob + for k in ["source", "author", "fps"]: + data_imprint[k] = version_attributes[k] # getting file path file = self.filepath_from_context(context).replace("\\", "/") @@ -190,7 +185,7 @@ class LoadBackdropNodes(load.LoaderPlugin): # get main variables # Get version from io project_name = context["project"]["name"] - version_doc = context["version"] + version_entity = context["version"] repre_doc = context["representation"] # get corresponding node @@ -198,23 +193,21 @@ class LoadBackdropNodes(load.LoaderPlugin): file = get_representation_path(repre_doc).replace("\\", "/") - name = container['name'] - version_data = version_doc.get("data", {}) - vname = version_doc.get("name", None) - namespace = container['namespace'] - colorspace = version_data.get("colorspace", None) + name = container["name"] + namespace = container["namespace"] object_name = "{}_{}".format(name, namespace) - add_keys = ["source", "author", "fps"] + version_attributes = version_entity["attrib"] + colorspace = version_attributes.get("colorSpace") data_imprint = { "representation": str(repre_doc["_id"]), - "version": vname, + "version": version_entity["version"], "colorspaceInput": colorspace, } - for k in add_keys: - data_imprint.update({k: version_data[k]}) + for k in ["source", "author", "fps"]: + data_imprint[k] = version_attributes[k] # adding nodes to node graph # just in case we are in group lets jump out of it @@ -234,18 +227,20 @@ class LoadBackdropNodes(load.LoaderPlugin): GN["name"].setValue(object_name) # get all versions in list - last_version_doc = get_last_version_by_subset_id( - project_name, version_doc["parent"], fields=["_id"] + last_version_entity = ayon_api.get_last_version_by_product_id( + project_name, version_entity["productId"], fields={"id"} ) # change color of node - if version_doc["_id"] == last_version_doc["_id"]: + if version_entity["id"] == last_version_entity["id"]: color_value = self.node_color else: color_value = "0xd88467ff" GN["tile_color"].setValue(int(color_value, 16)) - self.log.info("updated to version: {}".format(version_doc.get("name"))) + self.log.info( + "updated to version: {}".format(version_entity["version"]) + ) return update_container(GN, data_imprint) diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py b/client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py index 4dc68e187b..79d8a1d063 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py @@ -1,12 +1,8 @@ import nuke +import ayon_api -from ayon_core.client import ( - get_version_by_id, - get_last_version_by_subset_id -) from ayon_core.pipeline import ( load, - get_current_project_name, get_representation_path, ) from ayon_core.hosts.nuke.api import ( @@ -35,27 +31,25 @@ class AlembicCameraLoader(load.LoaderPlugin): def load(self, context, name, namespace, data): # get main variables - version = context['version'] - version_data = version.get("data", {}) - vname = version.get("name", None) - first = version_data.get("frameStart", None) - last = version_data.get("frameEnd", None) - fps = version_data.get("fps") or nuke.root()["fps"].getValue() + version_entity = context["version"] + + version_attributes = version_entity["attrib"] + first = version_attributes.get("frameStart") + last = version_attributes.get("frameEnd") + fps = version_attributes.get("fps") or nuke.root()["fps"].getValue() + namespace = namespace or context["folder"]["name"] object_name = "{}_{}".format(name, namespace) # prepare data for imprinting - # add additional metadata from the version to imprint to Avalon knob - add_keys = ["source", "author", "fps"] - + # add additional metadata from the version to imprint to metadata knob data_imprint = { "frameStart": first, "frameEnd": last, - "version": vname, + "version": version_entity["version"], } - - for k in add_keys: - data_imprint.update({k: version_data[k]}) + for k in ["source", "author", "fps"]: + data_imprint[k] = version_attributes[k] # getting file path file = self.filepath_from_context(context).replace("\\", "/") @@ -82,7 +76,9 @@ class AlembicCameraLoader(load.LoaderPlugin): camera_node.setXYpos(xpos, ypos) # color node by correct color by actual version - self.node_version_color(version, camera_node) + self.node_version_color( + context["project"]["name"], version_entity, camera_node + ) return containerise( node=camera_node, @@ -109,29 +105,26 @@ class AlembicCameraLoader(load.LoaderPlugin): None """ # Get version from io - version_doc = context["version"] + version_entity = context["version"] repre_doc = context["representation"] # get main variables - version_data = version_doc.get("data", {}) - vname = version_doc.get("name", None) - first = version_data.get("frameStart", None) - last = version_data.get("frameEnd", None) - fps = version_data.get("fps") or nuke.root()["fps"].getValue() + version_attributes = version_entity["attrib"] + first = version_attributes.get("frameStart") + last = version_attributes.get("frameEnd") + fps = version_attributes.get("fps") or nuke.root()["fps"].getValue() # prepare data for imprinting - # add additional metadata from the version to imprint to Avalon knob - add_keys = ["source", "author", "fps"] - data_imprint = { "representation": str(repre_doc["_id"]), "frameStart": first, "frameEnd": last, - "version": vname + "version": version_entity["version"] } - for k in add_keys: - data_imprint.update({k: version_data[k]}) + # add attributes from the version to imprint to metadata knob + for k in ["source", "author", "fps"]: + data_imprint[k] = version_attributes[k] # getting file path file = get_representation_path(repre_doc).replace("\\", "/") @@ -169,23 +162,26 @@ class AlembicCameraLoader(load.LoaderPlugin): d.setInput(index, camera_node) # color node by correct color by actual version - self.node_version_color(version_doc, camera_node) + self.node_version_color( + context["project"]["name"], version_entity, camera_node + ) - self.log.info("updated to version: {}".format(version_doc.get("name"))) + self.log.info( + "updated to version: {}".format(version_entity["version"]) + ) return update_container(camera_node, data_imprint) - def node_version_color(self, version_doc, node): + def node_version_color(self, project_name, version_entity, node): """ Coloring a node by correct color by actual version """ # get all versions in list - project_name = get_current_project_name() - last_version_doc = get_last_version_by_subset_id( - project_name, version_doc["parent"], fields=["_id"] + last_version_entity = ayon_api.get_last_version_by_product_id( + project_name, version_entity["productId"], fields={"id"} ) # change color of node - if version_doc["_id"] == last_version_doc["_id"]: + if version_entity["id"] == last_version_entity["id"]: color_value = self.node_color else: color_value = "0xd88467ff" diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py index f6c3387b15..c43c8853d8 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py @@ -1,14 +1,11 @@ +from copy import deepcopy + import nuke import qargparse -from pprint import pformat -from copy import deepcopy +import ayon_api + from ayon_core.lib import Logger -from ayon_core.client import ( - get_version_by_id, - get_last_version_by_subset_id, -) from ayon_core.pipeline import ( - get_current_project_name, get_representation_path, ) from ayon_core.hosts.nuke.api.lib import ( @@ -88,6 +85,10 @@ class LoadClip(plugin.NukeLoader): """Load asset via database """ representation = context["representation"] + version_entity = context["version"] + version_attributes = version_entity["attrib"] + version_data = version_entity["data"] + # reset container id so it is always unique for each instance self.reset_container_id() @@ -109,20 +110,16 @@ class LoadClip(plugin.NukeLoader): add_retime = options.get( "add_retime", self.options_defaults["add_retime"]) - version = context['version'] - version_data = version.get("data", {}) repre_id = representation["_id"] - self.log.debug("_ version_data: {}\n".format( - pformat(version_data))) self.log.debug( "Representation id `{}` ".format(repre_id)) - self.handle_start = version_data.get("handleStart", 0) - self.handle_end = version_data.get("handleEnd", 0) + self.handle_start = version_attributes.get("handleStart", 0) + self.handle_end = version_attributes.get("handleEnd", 0) - first = version_data.get("frameStart", None) - last = version_data.get("frameEnd", None) + first = version_attributes.get("frameStart") + last = version_attributes.get("frameEnd") first -= self.handle_start last += self.handle_end @@ -149,47 +146,50 @@ class LoadClip(plugin.NukeLoader): inpanel=False ) + # get colorspace + colorspace = ( + representation["data"].get("colorspace") + or version_attributes.get("colorSpace") + ) + # to avoid multiple undo steps for rest of process # we will switch off undo-ing with viewer_update_and_undo_stop(): read_node["file"].setValue(filepath) used_colorspace = self._set_colorspace( - read_node, version_data, representation["data"], filepath) + read_node, colorspace, filepath + ) self._set_range_to_node(read_node, first, last, start_at_workfile) - # add additional metadata from the version to imprint Avalon knob - add_keys = ["frameStart", "frameEnd", - "source", "colorspace", "author", "fps", "version", - "handleStart", "handleEnd"] + version_name = version_entity["version"] + if version_name < 0: + version_name = "hero" - data_imprint = {} - for key in add_keys: - if key == 'version': - version_doc = context["version"] - if version_doc["type"] == "hero_version": - version = "hero" - else: - version = version_doc.get("name") + data_imprint = { + "version": version_name, + "db_colorspace": colorspace + } + if used_colorspace: + data_imprint["used_colorspace"] = used_colorspace - if version: - data_imprint[key] = version + # add attributes from the version to imprint metadata knob + for key in [ + "frameStart", + "frameEnd", + "source", + "author", + "fps", + "handleStart", + "handleEnd", + ]: + value = version_attributes.get(key, str(None)) + if isinstance(value, str): + value = value.replace("\\", "/") + data_imprint[key] = value - elif key == 'colorspace': - colorspace = representation["data"].get(key) - colorspace = colorspace or version_data.get(key) - data_imprint["db_colorspace"] = colorspace - if used_colorspace: - data_imprint["used_colorspace"] = used_colorspace - else: - value_ = context["version"]['data'].get( - key, str(None)) - if isinstance(value_, (str)): - value_ = value_.replace("\\", "/") - data_imprint[key] = value_ - - if add_retime and version_data.get("retime", None): + if add_retime and version_data.get("retime"): data_imprint["addRetime"] = True read_node["tile_color"].setValue(int("0x4ecd25ff", 16)) @@ -202,7 +202,7 @@ class LoadClip(plugin.NukeLoader): loader=self.__class__.__name__, data=data_imprint) - if add_retime and version_data.get("retime", None): + if add_retime and version_data.get("retime"): self._make_retimes(read_node, version_data) self.set_as_member(read_node) @@ -250,8 +250,13 @@ class LoadClip(plugin.NukeLoader): """ + project_name = context["project"]["name"] + version_entity = context["version"] repre_doc = context["representation"] + version_attributes = version_entity["attrib"] + version_data = version_entity["data"] + is_sequence = len(repre_doc["files"]) > 1 read_node = container["node"] @@ -271,21 +276,19 @@ class LoadClip(plugin.NukeLoader): if "addRetime" in key ] - project_name = get_current_project_name() - version_doc = get_version_by_id(project_name, repre_doc["parent"]) - - version_data = version_doc.get("data", {}) repre_id = repre_doc["_id"] # colorspace profile - colorspace = repre_doc["data"].get("colorspace") - colorspace = colorspace or version_data.get("colorspace") + colorspace = ( + repre_doc["data"].get("colorspace") + or version_attributes.get("colorSpace") + ) - self.handle_start = version_data.get("handleStart", 0) - self.handle_end = version_data.get("handleEnd", 0) + self.handle_start = version_attributes.get("handleStart", 0) + self.handle_end = version_attributes.get("handleEnd", 0) - first = version_data.get("frameStart", None) - last = version_data.get("frameEnd", None) + first = version_attributes.get("frameStart") + last = version_attributes.get("frameEnd") first -= self.handle_start last += self.handle_end @@ -305,7 +308,7 @@ class LoadClip(plugin.NukeLoader): # we will switch off undo-ing with viewer_update_and_undo_stop(): used_colorspace = self._set_colorspace( - read_node, version_data, repre_doc["data"], filepath) + read_node, colorspace, filepath) self._set_range_to_node(read_node, first, last, start_at_workfile) @@ -313,39 +316,36 @@ class LoadClip(plugin.NukeLoader): "representation": str(repre_doc["_id"]), "frameStart": str(first), "frameEnd": str(last), - "version": str(version_doc.get("name")), + "version": str(version_entity["version"]), "db_colorspace": colorspace, - "source": version_data.get("source"), + "source": version_attributes.get("source"), "handleStart": str(self.handle_start), "handleEnd": str(self.handle_end), - "fps": str(version_data.get("fps")), - "author": version_data.get("author") + "fps": str(version_attributes.get("fps")), + "author": version_attributes.get("author") } # add used colorspace if found any if used_colorspace: updated_dict["used_colorspace"] = used_colorspace - last_version_doc = get_last_version_by_subset_id( - project_name, version_doc["parent"], fields=["_id"] + last_version_entity = ayon_api.get_last_version_by_product_id( + project_name, version_entity["productId"], fields={"id"} ) # change color of read_node - if version_doc["_id"] == last_version_doc["_id"]: + if version_entity["id"] == last_version_entity["id"]: color_value = "0x4ecd25ff" else: color_value = "0xd84f20ff" read_node["tile_color"].setValue(int(color_value, 16)) # Update the imprinted representation - update_container( - read_node, - updated_dict - ) + update_container(read_node, updated_dict) self.log.info( - "updated to version: {}".format(version_doc.get("name")) + "updated to version: {}".format(version_entity["version"]) ) - if add_retime and version_data.get("retime", None): + if add_retime and version_data.get("retime"): self._make_retimes(read_node, version_data) else: self.clear_members(read_node) @@ -462,12 +462,9 @@ class LoadClip(plugin.NukeLoader): return self.node_name_template.format(**name_data) - def _set_colorspace(self, node, version_data, repre_data, path): + def _set_colorspace(self, node, colorspace, path): output_color = None path = path.replace("\\", "/") - # get colorspace - colorspace = repre_data.get("colorspace") - colorspace = colorspace or version_data.get("colorspace") # colorspace from `project_settings/nuke/imageio/regex_inputs` iio_colorspace = get_imageio_input_colorspace(path) diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_effects.py b/client/ayon_core/hosts/nuke/plugins/load/load_effects.py index 514e64369c..dade7bfff0 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_effects.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_effects.py @@ -2,14 +2,10 @@ import json from collections import OrderedDict import nuke import six +import ayon_api -from ayon_core.client import ( - get_version_by_id, - get_last_version_by_subset_id, -) from ayon_core.pipeline import ( load, - get_current_project_name, get_representation_path, ) from ayon_core.hosts.nuke.api import ( @@ -47,30 +43,36 @@ class LoadEffects(load.LoaderPlugin): nuke node: containerised nuke node object """ # get main variables - version = context['version'] - version_data = version.get("data", {}) - vname = version.get("name", None) - first = version_data.get("frameStart", None) - last = version_data.get("frameEnd", None) + version_entity = context["version"] + + version_attributes = version_entity["attrib"] + first = version_attributes.get("frameStart") + last = version_attributes.get("frameEnd") + colorspace = version_attributes.get("colorSpace") + workfile_first_frame = int(nuke.root()["first_frame"].getValue()) namespace = namespace or context["folder"]["name"] - colorspace = version_data.get("colorspace", None) object_name = "{}_{}".format(name, namespace) # prepare data for imprinting - # add additional metadata from the version to imprint to Avalon knob - add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd", - "source", "author", "fps"] - data_imprint = { "frameStart": first, "frameEnd": last, - "version": vname, + "version": version_entity["version"], "colorspaceInput": colorspace, } - for k in add_keys: - data_imprint.update({k: version_data[k]}) + # add additional metadata from the version to imprint to Avalon knob + for k in [ + "frameStart", + "frameEnd", + "handleStart", + "handleEnd", + "source", + "author", + "fps" + ]: + data_imprint[k] = version_attributes[k] # getting file path file = self.filepath_from_context(context).replace("\\", "/") @@ -157,36 +159,40 @@ class LoadEffects(load.LoaderPlugin): # get main variables # Get version from io project_name = context["project"]["name"] - version_doc = context["version"] + version_entity = context["version"] repre_doc = context["representation"] # get corresponding node GN = container["node"] file = get_representation_path(repre_doc).replace("\\", "/") - name = container['name'] - version_data = version_doc.get("data", {}) - vname = version_doc.get("name", None) - first = version_data.get("frameStart", None) - last = version_data.get("frameEnd", None) - workfile_first_frame = int(nuke.root()["first_frame"].getValue()) - namespace = container['namespace'] - colorspace = version_data.get("colorspace", None) - object_name = "{}_{}".format(name, namespace) - add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd", - "source", "author", "fps"] + version_attributes = version_entity["attrib"] + first = version_attributes.get("frameStart") + last = version_attributes.get("frameEnd") + colorspace = version_attributes.get("colorSpace") + + workfile_first_frame = int(nuke.root()["first_frame"].getValue()) + namespace = container["namespace"] data_imprint = { "representation": str(repre_doc["_id"]), "frameStart": first, "frameEnd": last, - "version": vname, + "version": version_entity["version"], "colorspaceInput": colorspace } - for k in add_keys: - data_imprint.update({k: version_data[k]}) + for k in [ + "frameStart", + "frameEnd", + "handleStart", + "handleEnd", + "source", + "author", + "fps", + ]: + data_imprint[k] = version_attributes[k] # Update the imprinted representation update_container( @@ -252,19 +258,21 @@ class LoadEffects(load.LoaderPlugin): # try to find parent read node self.connect_read_node(GN, namespace, json_f["assignTo"]) - last_version_doc = get_last_version_by_subset_id( - project_name, version_doc["parent"], fields=["_id"] + last_version_entity = ayon_api.get_last_version_by_product_id( + project_name, version_entity["productId"], fields={"id"} ) # change color of node - if version_doc["_id"] == last_version_doc["_id"]: + if version_entity["id"] == last_version_entity["id"]: color_value = "0x3469ffff" else: color_value = "0xd84f20ff" GN["tile_color"].setValue(int(color_value, 16)) - self.log.info("updated to version: {}".format(version_doc.get("name"))) + self.log.info( + "updated to version: {}".format(version_entity["version"]) + ) def connect_read_node(self, group_node, namespace, product_name): """ diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py b/client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py index d391cc6c9c..47b84abc6a 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py @@ -2,14 +2,10 @@ import json from collections import OrderedDict import six import nuke +import ayon_api -from ayon_core.client import ( - get_version_by_id, - get_last_version_by_subset_id, -) from ayon_core.pipeline import ( load, - get_current_project_name, get_representation_path, ) from ayon_core.hosts.nuke.api import lib @@ -48,30 +44,35 @@ class LoadEffectsInputProcess(load.LoaderPlugin): """ # get main variables - version = context['version'] - version_data = version.get("data", {}) - vname = version.get("name", None) - first = version_data.get("frameStart", None) - last = version_data.get("frameEnd", None) + version_entity = context["version"] + + version_attributes = version_entity["attrib"] + first = version_attributes.get("frameStart") + last = version_attributes.get("frameEnd") + colorspace = version_attributes.get("colorSpace") + workfile_first_frame = int(nuke.root()["first_frame"].getValue()) namespace = namespace or context["folder"]["name"] - colorspace = version_data.get("colorspace", None) object_name = "{}_{}".format(name, namespace) # prepare data for imprinting - # add additional metadata from the version to imprint to Avalon knob - add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd", - "source", "author", "fps"] - data_imprint = { "frameStart": first, "frameEnd": last, - "version": vname, + "version": version_entity["version"], "colorspaceInput": colorspace, } - - for k in add_keys: - data_imprint.update({k: version_data[k]}) + # add additional metadata from the version to imprint to Avalon knob + for k in [ + "frameStart", + "frameEnd", + "handleStart", + "handleEnd", + "source", + "author", + "fps" + ]: + data_imprint[k] = version_attributes[k] # getting file path file = self.filepath_from_context(context).replace("\\", "/") @@ -162,33 +163,39 @@ class LoadEffectsInputProcess(load.LoaderPlugin): # get main variables # Get version from io project_name = context["project"]["name"] - version_doc = context["version"] + version_entity = context["version"] repre_doc = context["representation"] # get corresponding node GN = container["node"] file = get_representation_path(repre_doc).replace("\\", "/") - version_data = version_doc.get("data", {}) - vname = version_doc.get("name", None) - first = version_data.get("frameStart", None) - last = version_data.get("frameEnd", None) - workfile_first_frame = int(nuke.root()["first_frame"].getValue()) - colorspace = version_data.get("colorspace", None) - add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd", - "source", "author", "fps"] + version_attributes = version_entity["attrib"] + first = version_attributes.get("frameStart") + last = version_attributes.get("frameEnd") + colorspace = version_attributes.get("colorSpace") + + workfile_first_frame = int(nuke.root()["first_frame"].getValue()) data_imprint = { "representation": str(repre_doc["_id"]), "frameStart": first, "frameEnd": last, - "version": vname, + "version": version_entity["version"], "colorspaceInput": colorspace, } - for k in add_keys: - data_imprint.update({k: version_data[k]}) + for k in [ + "frameStart", + "frameEnd", + "handleStart", + "handleEnd", + "source", + "author", + "fps" + ]: + data_imprint[k] = version_attributes[k] # Update the imprinted representation update_container( @@ -252,18 +259,18 @@ class LoadEffectsInputProcess(load.LoaderPlugin): output.setInput(0, pre_node) # get all versions in list - last_version_doc = get_last_version_by_subset_id( - project_name, version_doc["parent"], fields=["_id"] + last_version_entity = ayon_api.get_last_version_by_product_id( + project_name, version_entity["productId"], fields={"id"} ) # change color of node - if version_doc["_id"] == last_version_doc["_id"]: + if version_entity["id"] == last_version_entity["id"]: color_value = "0x3469ffff" else: color_value = "0xd84f20ff" GN["tile_color"].setValue(int(color_value, 16)) - self.log.info("updated to version: {}".format(version_doc.get("name"))) + self.log.info("updated to version: {}".format(version_entity["name"])) def connect_active_viewer(self, group_node): """ diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py b/client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py index 39c52293e2..dcba0e6720 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py @@ -1,12 +1,8 @@ import nuke +import ayon_api -from ayon_core.client import ( - get_version_by_id, - get_last_version_by_subset_id, -) from ayon_core.pipeline import ( load, - get_current_project_name, get_representation_path, ) from ayon_core.hosts.nuke.api.lib import ( @@ -50,29 +46,35 @@ class LoadGizmo(load.LoaderPlugin): """ # get main variables - version = context['version'] - version_data = version.get("data", {}) - vname = version.get("name", None) - first = version_data.get("frameStart", None) - last = version_data.get("frameEnd", None) + version_entity = context["version"] + version_attributes = version_entity["attrib"] + + first = version_attributes.get("frameStart") + last = version_attributes.get("frameEnd") + colorspace = version_attributes.get("colorSpace") + namespace = namespace or context["folder"]["name"] - colorspace = version_data.get("colorspace", None) object_name = "{}_{}".format(name, namespace) # prepare data for imprinting - # add additional metadata from the version to imprint to Avalon knob - add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd", - "source", "author", "fps"] - data_imprint = { "frameStart": first, "frameEnd": last, - "version": vname, + "version": version_entity["version"], "colorspaceInput": colorspace } - for k in add_keys: - data_imprint.update({k: version_data[k]}) + # add attributes from the version to imprint to metadata knob + for k in [ + "frameStart", + "frameEnd", + "handleStart", + "handleEnd", + "source", + "author", + "fps" + ]: + data_imprint[k] = version_attributes[k] # getting file path file = self.filepath_from_context(context).replace("\\", "/") @@ -109,35 +111,38 @@ class LoadGizmo(load.LoaderPlugin): # get main variables # Get version from io project_name = context["project"]["name"] - version_doc = context["version"] + version_entity = context["version"] repre_doc = context["representation"] + version_attributes = version_entity["attrib"] + # get corresponding node group_node = container["node"] file = get_representation_path(repre_doc).replace("\\", "/") - name = container['name'] - version_data = version_doc.get("data", {}) - vname = version_doc.get("name", None) - first = version_data.get("frameStart", None) - last = version_data.get("frameEnd", None) - namespace = container['namespace'] - colorspace = version_data.get("colorspace", None) - object_name = "{}_{}".format(name, namespace) - add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd", - "source", "author", "fps"] + first = version_attributes.get("frameStart") + last = version_attributes.get("frameEnd") + colorspace = version_attributes.get("colorSpace") data_imprint = { "representation": str(repre_doc["_id"]), "frameStart": first, "frameEnd": last, - "version": vname, + "version": version_entity["version"], "colorspaceInput": colorspace } - for k in add_keys: - data_imprint.update({k: version_data[k]}) + for k in [ + "frameStart", + "frameEnd", + "handleStart", + "handleEnd", + "source", + "author", + "fps" + ]: + data_imprint[k] = version_attributes[k] # capture pipeline metadata avalon_data = get_avalon_knob_data(group_node) @@ -158,19 +163,21 @@ class LoadGizmo(load.LoaderPlugin): # set updated pipeline metadata set_avalon_knob_data(new_group_node, avalon_data) - last_version_doc = get_last_version_by_subset_id( - project_name, version_doc["parent"], fields=["_id"] + last_version_entity = ayon_api.get_last_version_by_product_id( + project_name, version_entity["productId"], fields={"id"} ) # change color of node - if version_doc["_id"] == last_version_doc["_id"]: + if version_entity["id"] == last_version_entity["id"]: color_value = self.node_color else: color_value = "0xd88467ff" new_group_node["tile_color"].setValue(int(color_value, 16)) - self.log.info("updated to version: {}".format(version_doc.get("name"))) + self.log.info( + "updated to version: {}".format(version_entity["name"]) + ) return update_container(new_group_node, data_imprint) diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py b/client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py index 061cb9e4f9..6c5f4f402b 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py @@ -1,13 +1,9 @@ import nuke import six +import ayon_api -from ayon_core.client import ( - get_version_by_id, - get_last_version_by_subset_id, -) from ayon_core.pipeline import ( load, - get_current_project_name, get_representation_path, ) from ayon_core.hosts.nuke.api.lib import ( @@ -52,29 +48,35 @@ class LoadGizmoInputProcess(load.LoaderPlugin): """ # get main variables - version = context['version'] - version_data = version.get("data", {}) - vname = version.get("name", None) - first = version_data.get("frameStart", None) - last = version_data.get("frameEnd", None) + version_entity = context["version"] + + version_attributes = version_entity["attrib"] + first = version_attributes.get("frameStart") + last = version_attributes.get("frameEnd") + colorspace = version_attributes.get("colorSpace") + namespace = namespace or context["folder"]["name"] - colorspace = version_data.get("colorspace", None) object_name = "{}_{}".format(name, namespace) # prepare data for imprinting - # add additional metadata from the version to imprint to Avalon knob - add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd", - "source", "author", "fps"] - + # add additional metadata from the version to imprint to metadata knob data_imprint = { "frameStart": first, "frameEnd": last, - "version": vname, + "version": version_entity["version"], "colorspaceInput": colorspace } - for k in add_keys: - data_imprint.update({k: version_data[k]}) + for k in [ + "frameStart", + "frameEnd", + "handleStart", + "handleEnd", + "source", + "author", + "fps" + ]: + data_imprint[k] = version_attributes[k] # getting file path file = self.filepath_from_context(context).replace("\\", "/") @@ -116,35 +118,37 @@ class LoadGizmoInputProcess(load.LoaderPlugin): # get main variables # Get version from io project_name = context["project"]["name"] - version_doc = context["version"] + version_entity = context["version"] repre_doc = context["representation"] # get corresponding node group_node = container["node"] file = get_representation_path(repre_doc).replace("\\", "/") - name = container['name'] - version_data = version_doc.get("data", {}) - vname = version_doc.get("name", None) - first = version_data.get("frameStart", None) - last = version_data.get("frameEnd", None) - namespace = container['namespace'] - colorspace = version_data.get("colorspace", None) - object_name = "{}_{}".format(name, namespace) - add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd", - "source", "author", "fps"] + version_attributes = version_entity["attrib"] + first = version_attributes.get("frameStart") + last = version_attributes.get("frameEnd") + colorspace = version_attributes.get("colorSpace") data_imprint = { "representation": str(repre_doc["_id"]), "frameStart": first, "frameEnd": last, - "version": vname, + "version": version_entity["version"], "colorspaceInput": colorspace } - for k in add_keys: - data_imprint.update({k: version_data[k]}) + for k in [ + "frameStart", + "frameEnd", + "handleStart", + "handleEnd", + "source", + "author", + "fps" + ]: + data_imprint[k] = version_attributes[k] # capture pipeline metadata avalon_data = get_avalon_knob_data(group_node) @@ -165,18 +169,20 @@ class LoadGizmoInputProcess(load.LoaderPlugin): # set updated pipeline metadata set_avalon_knob_data(new_group_node, avalon_data) - last_version_doc = get_last_version_by_subset_id( - project_name, version_doc["parent"], fields=["_id"] + last_version_entity = ayon_api.get_last_version_by_product_id( + project_name, version_entity["productId"], fields={"id"} ) # change color of node - if version_doc["_id"] == last_version_doc["_id"]: + if version_entity["id"] == last_version_entity["id"]: color_value = self.node_color else: color_value = "0xd88467ff" new_group_node["tile_color"].setValue(int(color_value, 16)) - self.log.info("updated to version: {}".format(version_doc.get("name"))) + self.log.info( + "updated to version: {}".format(version_entity["version"]) + ) return update_container(new_group_node, data_imprint) diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_image.py b/client/ayon_core/hosts/nuke/plugins/load/load_image.py index b2d6098a71..0e5096fda6 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_image.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_image.py @@ -1,14 +1,10 @@ import nuke import qargparse +import ayon_api -from ayon_core.client import ( - get_version_by_id, - get_last_version_by_subset_id, -) from ayon_core.pipeline import ( load, - get_current_project_name, get_representation_path, ) from ayon_core.hosts.nuke.api.lib import ( @@ -72,13 +68,13 @@ class LoadImage(load.LoaderPlugin): "frame_number", int(nuke.root()["first_frame"].getValue()) ) - version = context['version'] - version_data = version.get("data", {}) - repr_id = context["representation"]["_id"] + version_entity = context["version"] + version_attributes = version_entity["attrib"] + repre_doc = context["representation"] + repre_id = repre_doc["_id"] - self.log.info("version_data: {}\n".format(version_data)) self.log.debug( - "Representation id `{}` ".format(repr_id)) + "Representation id `{}` ".format(repre_id)) last = first = int(frame_number) @@ -89,16 +85,13 @@ class LoadImage(load.LoaderPlugin): file = self.filepath_from_context(context) if not file: - repr_id = context["representation"]["_id"] self.log.warning( - "Representation id `{}` is failing to load".format(repr_id)) + "Representation id `{}` is failing to load".format(repre_id)) return file = file.replace("\\", "/") - representation = context["representation"] - repr_cont = representation["context"] - frame = repr_cont.get("frame") + frame = repre_doc["context"].get("frame") if frame: padding = len(frame) file = file.replace( @@ -118,7 +111,7 @@ class LoadImage(load.LoaderPlugin): r["file"].setValue(file) # Set colorspace defined in version data - colorspace = context["version"]["data"].get("colorspace") + colorspace = version_entity["attrib"].get("colorSpace") if colorspace: r["colorspace"].setValue(str(colorspace)) @@ -132,19 +125,16 @@ class LoadImage(load.LoaderPlugin): r["origlast"].setValue(last) r["last"].setValue(last) - # add additional metadata from the version to imprint Avalon knob - add_keys = ["source", "colorspace", "author", "fps", "version"] - + # add attributes from the version to imprint metadata knob + colorspace = version_attributes["colorSpace"] data_imprint = { "frameStart": first, - "frameEnd": last + "frameEnd": last, + "version": version_entity["version"], + "colorspace": colorspace, } - for k in add_keys: - if k == 'version': - data_imprint.update({k: context["version"]['name']}) - else: - data_imprint.update( - {k: context["version"]['data'].get(k, str(None))}) + for k in ["source", "author", "fps"]: + data_imprint[k] = version_attributes.get(k, str(None)) r["tile_color"].setValue(int("0x4ecd25ff", 16)) @@ -172,7 +162,7 @@ class LoadImage(load.LoaderPlugin): assert node.Class() == "Read", "Must be Read" project_name = context["project"]["name"] - version_doc = context["version"] + version_entity = context["version"] repre_doc = context["representation"] repr_cont = repre_doc["context"] @@ -180,9 +170,9 @@ class LoadImage(load.LoaderPlugin): file = get_representation_path(repre_doc) if not file: - repr_id = repre_doc["_id"] + repre_id = repre_doc["_id"] self.log.warning( - "Representation id `{}` is failing to load".format(repr_id)) + "Representation id `{}` is failing to load".format(repre_id)) return file = file.replace("\\", "/") @@ -195,12 +185,10 @@ class LoadImage(load.LoaderPlugin): format(frame_number, "0{}".format(padding))) # Get start frame from version data - last_version_doc = get_last_version_by_subset_id( - project_name, version_doc["parent"], fields=["_id"] + last_version_entity = ayon_api.get_last_version_by_product_id( + project_name, version_entity["productId"], fields={"id"} ) - version_data = version_doc.get("data", {}) - last = first = int(frame_number) # Set the global in to the start frame of the sequence @@ -210,31 +198,30 @@ class LoadImage(load.LoaderPlugin): node["origlast"].setValue(last) node["last"].setValue(last) - updated_dict = {} - updated_dict.update({ + version_attributes = version_entity["attrib"] + updated_dict = { "representation": str(repre_doc["_id"]), "frameStart": str(first), "frameEnd": str(last), - "version": str(version_doc.get("name")), - "colorspace": version_data.get("colorspace"), - "source": version_data.get("source"), - "fps": str(version_data.get("fps")), - "author": version_data.get("author") - }) + "version": str(version_entity["version"]), + "colorspace": version_attributes.get("colorSpace"), + "source": version_attributes.get("source"), + "fps": str(version_attributes.get("fps")), + "author": version_attributes.get("author") + } # change color of node - if version_doc["_id"] == last_version_doc["_id"]: + if version_entity["id"] == last_version_entity["id"]: color_value = "0x4ecd25ff" else: color_value = "0xd84f20ff" node["tile_color"].setValue(int(color_value, 16)) # Update the imprinted representation - update_container( - node, - updated_dict - ) - self.log.info("updated to version: {}".format(version_doc.get("name"))) + update_container(node, updated_dict) + self.log.info("updated to version: {}".format( + version_entity["version"] + )) def remove(self, container): node = container["node"] diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_model.py b/client/ayon_core/hosts/nuke/plugins/load/load_model.py index f68591ac50..b9032f7c28 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_model.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_model.py @@ -1,12 +1,8 @@ import nuke +import ayon_api -from ayon_core.client import ( - get_version_by_id, - get_last_version_by_subset_id, -) from ayon_core.pipeline import ( load, - get_current_project_name, get_representation_path, ) from ayon_core.hosts.nuke.api.lib import maintained_selection @@ -33,27 +29,26 @@ class AlembicModelLoader(load.LoaderPlugin): def load(self, context, name, namespace, data): # get main variables - version = context['version'] - version_data = version.get("data", {}) - vname = version.get("name", None) - first = version_data.get("frameStart", None) - last = version_data.get("frameEnd", None) - fps = version_data.get("fps") or nuke.root()["fps"].getValue() + project_name = context["project"]["name"] + version_entity = context["version"] + + version_attributes = version_entity["attrib"] + first = version_attributes.get("frameStart") + last = version_attributes.get("frameEnd") + fps = version_attributes.get("fps") or nuke.root()["fps"].getValue() + namespace = namespace or context["folder"]["name"] object_name = "{}_{}".format(name, namespace) # prepare data for imprinting - # add additional metadata from the version to imprint to Avalon knob - add_keys = ["source", "author", "fps"] - data_imprint = { "frameStart": first, "frameEnd": last, - "version": vname + "version": version_entity["version"] } - - for k in add_keys: - data_imprint.update({k: version_data[k]}) + # add attributes from the version to imprint to metadata knob + for k in ["source", "author", "fps"]: + data_imprint[k] = version_attributes[k] # getting file path file = self.filepath_from_context(context).replace("\\", "/") @@ -86,7 +81,7 @@ class AlembicModelLoader(load.LoaderPlugin): model_node.setXYpos(xpos, ypos) # color node by correct color by actual version - self.node_version_color(version, model_node) + self.node_version_color(project_name, version_entity, model_node) return containerise( node=model_node, @@ -113,32 +108,30 @@ class AlembicModelLoader(load.LoaderPlugin): None """ # Get version from io - version_doc = context["version"] + project_name = context["project"]["name"] + version_entity = context["version"] repre_doc = context["representation"] # get corresponding node model_node = container["node"] # get main variables - version_data = version_doc.get("data", {}) - vname = version_doc.get("name", None) - first = version_data.get("frameStart", None) - last = version_data.get("frameEnd", None) - fps = version_data.get("fps") or nuke.root()["fps"].getValue() + version_attributes = version_entity["attrib"] + first = version_attributes.get("frameStart") + last = version_attributes.get("frameEnd") + fps = version_attributes.get("fps") or nuke.root()["fps"].getValue() # prepare data for imprinting - # add additional metadata from the version to imprint to Avalon knob - add_keys = ["source", "author", "fps"] - data_imprint = { "representation": str(repre_doc["_id"]), "frameStart": first, "frameEnd": last, - "version": vname + "version": version_entity["version"] } - for k in add_keys: - data_imprint.update({k: version_data[k]}) + # add additional metadata from the version to imprint to Avalon knob + for k in ["source", "author", "fps"]: + data_imprint[k] = version_attributes[k] # getting file path file = get_representation_path(repre_doc).replace("\\", "/") @@ -181,22 +174,23 @@ class AlembicModelLoader(load.LoaderPlugin): d.setInput(index, model_node) # color node by correct color by actual version - self.node_version_color(version_doc, model_node) + self.node_version_color(project_name, version_entity, model_node) - self.log.info("updated to version: {}".format(version_doc.get("name"))) + self.log.info( + "updated to version: {}".format(version_entity["version"]) + ) return update_container(model_node, data_imprint) - def node_version_color(self, version, node): + def node_version_color(self, project_name, version_entity, node): """ Coloring a node by correct color by actual version""" - project_name = get_current_project_name() - last_version_doc = get_last_version_by_subset_id( - project_name, version["parent"], fields=["_id"] + last_version_entity = ayon_api.get_last_version_by_product_id( + project_name, version_entity["productId"], fields={"id"} ) # change color of node - if version["_id"] == last_version_doc["_id"]: + if version_entity["id"] == last_version_entity["id"]: color_value = self.node_color else: color_value = "0xd88467ff" diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_ociolook.py b/client/ayon_core/hosts/nuke/plugins/load/load_ociolook.py index f3151bd389..e09e715db5 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_ociolook.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_ociolook.py @@ -1,16 +1,13 @@ import os import json import secrets + import nuke import six +import ayon_api -from ayon_core.client import ( - get_version_by_id, - get_last_version_by_subset_id -) from ayon_core.pipeline import ( load, - get_current_project_name, get_representation_path, ) from ayon_core.hosts.nuke.api import ( @@ -68,7 +65,11 @@ class LoadOcioLookNodes(load.LoaderPlugin): # renaming group node group_node["name"].setValue(node_name) - self._node_version_color(context["version"], group_node) + self._node_version_color( + context["project"]["name"], + context["version"], + group_node + ) self.log.info( "Loaded lut setup: `{}`".format(group_node["name"].value())) @@ -220,7 +221,6 @@ class LoadOcioLookNodes(load.LoaderPlugin): return group_node def update(self, container, context): - version_doc = context["version"] repre_doc = context["representation"] group_node = container["node"] @@ -235,7 +235,9 @@ class LoadOcioLookNodes(load.LoaderPlugin): group_node ) - self._node_version_color(version_doc, group_node) + self._node_version_color( + context["project"]["name"], context["version"], group_node + ) self.log.info("Updated lut setup: `{}`".format( group_node["name"].value())) @@ -287,16 +289,15 @@ class LoadOcioLookNodes(load.LoaderPlugin): with viewer_update_and_undo_stop(): nuke.delete(node) - def _node_version_color(self, version, node): + def _node_version_color(self, project_name, version_entity, node): """ Coloring a node by correct color by actual version""" - project_name = get_current_project_name() - last_version_doc = get_last_version_by_subset_id( - project_name, version["parent"], fields=["_id"] + last_version_entity = ayon_api.get_last_version_by_product_id( + project_name, version_entity["productId"], fields={"id"} ) # change color of node - if version["_id"] == last_version_doc["_id"]: + if version_entity["id"] == last_version_entity["id"]: color_value = self.current_node_color else: color_value = self.old_node_color diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_script_precomp.py b/client/ayon_core/hosts/nuke/plugins/load/load_script_precomp.py index 96a4e65d49..6dc74a517e 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_script_precomp.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_script_precomp.py @@ -1,11 +1,7 @@ import nuke +import ayon_api -from ayon_core.client import ( - get_version_by_id, - get_last_version_by_subset_id, -) from ayon_core.pipeline import ( - get_current_project_name, load, get_representation_path, ) @@ -32,12 +28,12 @@ class LinkAsGroup(load.LoaderPlugin): def load(self, context, name, namespace, data): # for k, v in context.items(): # log.info("key: `{}`, value: {}\n".format(k, v)) - version = context['version'] - version_data = version.get("data", {}) + version_entity = context["version"] - vname = version.get("name", None) - first = version_data.get("frameStart", None) - last = version_data.get("frameEnd", None) + version_attributes = version_entity["attrib"] + first = version_attributes.get("frameStart") + last = version_attributes.get("frameEnd") + colorspace = version_attributes.get("colorSpace") # Fallback to folder name when namespace is None if namespace is None: @@ -46,20 +42,23 @@ class LinkAsGroup(load.LoaderPlugin): file = self.filepath_from_context(context).replace("\\", "/") self.log.info("file: {}\n".format(file)) - self.log.info("versionData: {}\n".format(context["version"]["data"])) - - # add additional metadata from the version to imprint to Avalon knob - add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd", - "source", "author", "fps"] - data_imprint = { - "startingFrame": first, - "frameStart": first, - "frameEnd": last, - "version": vname + "startingFrame": first, + "frameStart": first, + "frameEnd": last, + "version": version_entity["version"] } - for k in add_keys: - data_imprint.update({k: context["version"]['data'][k]}) + # add additional metadata from the version to imprint to Avalon knob + for k in [ + "frameStart", + "frameEnd", + "handleStart", + "handleEnd", + "source", + "author", + "fps" + ]: + data_imprint[k] = version_attributes[k] # group context is set to precomp, so back up one level. nuke.endGroup() @@ -72,7 +71,6 @@ class LinkAsGroup(load.LoaderPlugin): ) # Set colorspace defined in version data - colorspace = context["version"]["data"].get("colorspace", None) self.log.info("colorspace: {}\n".format(colorspace)) P["name"].setValue("{}_{}".format(name, namespace)) @@ -118,27 +116,23 @@ class LinkAsGroup(load.LoaderPlugin): node = container["node"] project_name = context["project"]["name"] - version_doc = context["version"] + version_entity = context["version"] repre_doc = context["representation"] root = get_representation_path(repre_doc).replace("\\", "/") # Get start frame from version data - last_version_doc = get_last_version_by_subset_id( - project_name, version_doc["parent"], fields=["_id"] - ) - updated_dict = {} - version_data = version_doc["data"] - updated_dict.update({ + version_attributes = version_entity["attrib"] + updated_dict = { "representation": str(repre_doc["_id"]), - "frameEnd": version_data.get("frameEnd"), - "version": version_doc.get("name"), - "colorspace": version_data.get("colorspace"), - "source": version_data.get("source"), - "fps": version_data.get("fps"), - "author": version_data.get("author") - }) + "frameEnd": version_attributes.get("frameEnd"), + "version": version_entity["version"], + "colorspace": version_attributes.get("colorSpace"), + "source": version_attributes.get("source"), + "fps": version_attributes.get("fps"), + "author": version_attributes.get("author") + } # Update the imprinted representation update_container( @@ -148,14 +142,19 @@ class LinkAsGroup(load.LoaderPlugin): node["file"].setValue(root) + last_version_entity = ayon_api.get_last_version_by_product_id( + project_name, version_entity["productId"], fields={"id"} + ) # change color of node - if version_doc["_id"] == last_version_doc["_id"]: + if version_entity["id"] == last_version_entity["id"]: color_value = "0xff0ff0ff" else: color_value = "0xd84f20ff" node["tile_color"].setValue(int(color_value, 16)) - self.log.info("updated to version: {}".format(version_doc.get("name"))) + self.log.info( + "updated to version: {}".format(version_entity["version"]) + ) def remove(self, container): node = container["node"] diff --git a/client/ayon_core/hosts/resolve/api/plugin.py b/client/ayon_core/hosts/resolve/api/plugin.py index e4340dc64a..7c4490a897 100644 --- a/client/ayon_core/hosts/resolve/api/plugin.py +++ b/client/ayon_core/hosts/resolve/api/plugin.py @@ -368,7 +368,7 @@ class ClipLoader: product_name, representation_name ]) - self.data["versionData"] = self.context["version"]["data"] + self.data["versionAttributes"] = self.context["version"]["attrib"] self.data["timeline_basename"] = "timeline_{}_{}".format( product_name, representation_name) @@ -415,13 +415,11 @@ class ClipLoader: # Load file without the handles of the source media # We remove the handles from the source in and source out # so that the handles are excluded in the timeline - handle_start = 0 - handle_end = 0 # get version data frame data from db - version_data = self.data["versionData"] - frame_start = version_data.get("frameStart") - frame_end = version_data.get("frameEnd") + version_attributes = self.data["versionAttributes"] + frame_start = version_attributes.get("frameStart") + frame_end = version_attributes.get("frameEnd") # The version data usually stored the frame range + handles of the # media however certain representations may be shorter because they @@ -433,8 +431,8 @@ class ClipLoader: # from source and out if frame_start is not None and frame_end is not None: # Version has frame range data, so we can compare media length - handle_start = version_data.get("handleStart", 0) - handle_end = version_data.get("handleEnd", 0) + handle_start = version_attributes.get("handleStart", 0) + handle_end = version_attributes.get("handleEnd", 0) frame_start_handle = frame_start - handle_start frame_end_handle = frame_start + handle_end database_frame_duration = int( diff --git a/client/ayon_core/hosts/resolve/plugins/load/load_clip.py b/client/ayon_core/hosts/resolve/plugins/load/load_clip.py index 04b2aaaf15..6715135377 100644 --- a/client/ayon_core/hosts/resolve/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/resolve/plugins/load/load_clip.py @@ -1,8 +1,5 @@ -from ayon_core.client import get_last_version_by_subset_id -from ayon_core.pipeline import ( - get_representation_context, - get_current_project_name -) +import ayon_api + from ayon_core.hosts.resolve.api import lib, plugin from ayon_core.hosts.resolve.api.pipeline import ( containerise, @@ -50,7 +47,11 @@ class LoadClip(plugin.TimelineItemLoader): namespace = namespace or timeline_item.GetName() # update color of clip regarding the version order - self.set_item_color(timeline_item, version=context["version"]) + self.set_item_color( + context["project"]["name"], + timeline_item, + context["version"] + ) data_imprint = self.get_tag_data(context, name, namespace) return containerise( @@ -79,7 +80,11 @@ class LoadClip(plugin.TimelineItemLoader): timeline_item = loader.update(timeline_item, files) # update color of clip regarding the version order - self.set_item_color(timeline_item, version=context["version"]) + self.set_item_color( + context["project"]["name"], + timeline_item, + context["version"] + ) # if original media pool item has no remaining usages left # remove it from the media pool @@ -93,10 +98,9 @@ class LoadClip(plugin.TimelineItemLoader): """Return data to be imprinted on the timeline item marker""" repre_doc = context["representation"] - version_doc = context["version"] - version_data = version_doc.get("data", {}) - version_name = version_doc.get("name", None) - colorspace = version_data.get("colorspace", None) + version_entity = context["version"] + version_attributes = version_entity["attrib"] + colorspace = version_attributes.get("colorSpace", None) object_name = "{}_{}".format(name, namespace) # add additional metadata from the version to imprint Avalon knob @@ -106,37 +110,34 @@ class LoadClip(plugin.TimelineItemLoader): "fps", "handleStart", "handleEnd" ] data = { - key: version_data.get(key, "None") for key in add_version_data_keys + key: version_attributes.get(key, "None") + for key in add_version_data_keys } # add variables related to version context data.update({ "representation": str(repre_doc["_id"]), - "version": version_name, + "version": version_entity["version"], "colorspace": colorspace, "objectName": object_name }) return data @classmethod - def set_item_color(cls, timeline_item, version): + def set_item_color(cls, project_name, timeline_item, version_entity): """Color timeline item based on whether it is outdated or latest""" - # define version name - version_name = version.get("name", None) # get all versions in list - project_name = get_current_project_name() - last_version_doc = get_last_version_by_subset_id( + last_version_entity = ayon_api.get_last_version_by_product_id( project_name, - version["parent"], + version_entity["productId"], fields=["name"] ) - if last_version_doc: - last_version = last_version_doc["name"] - else: - last_version = None + last_version_id = None + if last_version_entity: + last_version_id = last_version_entity["id"] # set clip colour - if version_name == last_version: + if version_entity["id"] == last_version_id: timeline_item.SetClipColor(cls.clip_color_last) else: timeline_item.SetClipColor(cls.clip_color) diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py b/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py index 7f6dd147fe..c2f183480c 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py @@ -167,7 +167,7 @@ class PointCacheAlembicLoader(plugin.Loader): # Create directory for folder and Ayon container folder_name = context["folder"]["name"] product_name = context["product"]["name"] - version_doc = context["version"] + version = context["version"]["version"] repre_doc = context["representation"] suffix = "_CON" @@ -176,7 +176,6 @@ class PointCacheAlembicLoader(plugin.Loader): asset_name = f"{folder_name}_{product_name}" # Check if version is hero version and use different name - version = version_doc.get("name", -1) if version < 0: name_version = f"{product_name}_hero" else: diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py index 4408e5a090..9879b0b967 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py @@ -147,7 +147,7 @@ class SkeletalMeshAlembicLoader(plugin.Loader): def update(self, container, context): folder_name = context["folder"]["name"] product_name = context["product"]["name"] - version_doc = context["version"] + version = context["version"]["version"] repre_doc = context["representation"] # Create directory for folder and Ayon container @@ -156,7 +156,6 @@ class SkeletalMeshAlembicLoader(plugin.Loader): if folder_name: asset_name = f"{folder_name}_{product_name}" # Check if version is hero version and use different name - version = version_doc.get("name", -1) if version < 0: name_version = f"{product_name}_hero" else: diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py index 43b2b4c4f1..f643e027d2 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py @@ -149,7 +149,7 @@ class SkeletalMeshFBXLoader(plugin.Loader): def update(self, container, context): folder_name = context["folder"]["name"] product_name = context["product"]["name"] - version_doc = context["version"] + version = context["version"]["version"] repre_doc = context["representation"] # Create directory for asset and Ayon container @@ -158,7 +158,6 @@ class SkeletalMeshFBXLoader(plugin.Loader): if folder_name: asset_name = f"{folder_name}_{product_name}" # Check if version is hero version and use different name - version = version_doc.get("name", -1) if version < 0: name_version = f"{product_name}_hero" else: diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py b/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py index 373fc988ea..a23290997e 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py @@ -137,7 +137,7 @@ class StaticMeshFBXLoader(plugin.Loader): def update(self, container, context): folder_name = context["folder"]["name"] product_name = context["product"]["name"] - version_doc = context["version"] + version = context["version"]["version"] repre_doc = context["representation"] # Create directory for asset and Ayon container @@ -146,7 +146,6 @@ class StaticMeshFBXLoader(plugin.Loader): if folder_name: asset_name = f"{folder_name}_{product_name}" # Check if version is hero version and use different name - version = version_doc.get("name", -1) if version < 0: name_version = f"{product_name}_hero" else: From 5b3a7de12108d69c9a49ccf94ab24e811903c747 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 6 Mar 2024 10:59:37 +0100 Subject: [PATCH 455/573] remove conversions for representation parents --- client/ayon_core/client/entities.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/client/entities.py b/client/ayon_core/client/entities.py index 89fdd01bbc..d0acf535e8 100644 --- a/client/ayon_core/client/entities.py +++ b/client/ayon_core/client/entities.py @@ -558,8 +558,8 @@ def get_representations_parents(project_name, representations): for repre_id, parents in parents_by_repre_id.items(): version, subset, folder, project = parents new_parents[repre_id] = ( - convert_v4_version_to_v3(version), - convert_v4_subset_to_v3(subset), + version, + subset, folder, project ) From e5c78edd6f14dd8b63770df3d745fa9b577db20e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 6 Mar 2024 11:00:20 +0100 Subject: [PATCH 456/573] hosts are using version entities --- client/ayon_core/hosts/flame/api/plugin.py | 8 ++--- client/ayon_core/hosts/hiero/api/plugin.py | 7 ++-- .../plugins/publish/extract_usd_layered.py | 13 +++----- client/ayon_core/hosts/maya/api/lib.py | 24 ++++++-------- client/ayon_core/hosts/maya/api/setdress.py | 15 ++++----- .../hosts/maya/tools/mayalookassigner/app.py | 10 +++--- .../tools/mayalookassigner/arnold_standin.py | 18 ++++++----- .../tools/mayalookassigner/vray_proxies.py | 23 ++++++------- client/ayon_core/hosts/nuke/api/lib.py | 32 +++++++++---------- .../publish/collect_published_version.py | 8 ++--- .../hosts/traypublisher/api/plugin.py | 7 ++-- .../publish/submit_publish_cache_job.py | 22 ++++++------- .../plugins/publish/submit_publish_job.py | 22 ++++++------- 13 files changed, 101 insertions(+), 108 deletions(-) diff --git a/client/ayon_core/hosts/flame/api/plugin.py b/client/ayon_core/hosts/flame/api/plugin.py index cf28a3cef3..b00ff636ed 100644 --- a/client/ayon_core/hosts/flame/api/plugin.py +++ b/client/ayon_core/hosts/flame/api/plugin.py @@ -748,11 +748,9 @@ class ClipLoader(LoaderPlugin): Returns: str: colorspace name or None """ - version = context['version'] - version_data = version.get("data", {}) - colorspace = version_data.get( - "colorspace", None - ) + version_entity = context["version"] + version_attributes = version_entity["attrib"] + colorspace = version_attributes.get("colorSpace") if ( not colorspace diff --git a/client/ayon_core/hosts/hiero/api/plugin.py b/client/ayon_core/hosts/hiero/api/plugin.py index 667dc2af6c..cf3b4dadaf 100644 --- a/client/ayon_core/hosts/hiero/api/plugin.py +++ b/client/ayon_core/hosts/hiero/api/plugin.py @@ -454,7 +454,7 @@ class ClipLoader: representation = repr["name"] self.data["clip_name"] = self.clip_name_template.format(**repr_cntx) self.data["track_name"] = "_".join([product_name, representation]) - self.data["versionData"] = self.context["version"]["data"] + self.data["versionAttributes"] = self.context["version"]["attrib"] # gets file path file = get_representation_path_from_context(self.context) if not file: @@ -518,8 +518,9 @@ class ClipLoader: self.media_duration = int(self.media.duration()) # get handles - self.handle_start = self.data["versionData"].get("handleStart") - self.handle_end = self.data["versionData"].get("handleEnd") + version_attributes = self.data["versionAttributes"] + self.handle_start = version_attributes.get("handleStart") + self.handle_end = version_attributes.get("handleEnd") if self.handle_start is None: self.handle_start = self.data["folderAttributes"]["handleStart"] if self.handle_end is None: diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py index fc9feadd40..33ab7ef43c 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py @@ -7,10 +7,7 @@ from collections import deque import ayon_api import pyblish.api -from ayon_core.client import ( - get_last_version_by_subset_id, - get_representation_by_name, -) +from ayon_core.client import get_representation_by_name from ayon_core.pipeline import ( get_representation_path, publish, @@ -297,15 +294,15 @@ class ExtractUSDLayered(publish.Extractor): self.log.debug("No existing product..") return False - version_doc = get_last_version_by_subset_id( - project_name, product_entity["id"], fields=["_id"] + version_entity = ayon_api.get_last_version_by_product_id( + project_name, product_entity["id"], fields={"id"} ) - if not version_doc: + if not version_entity: self.log.debug("No existing version..") return False representation = get_representation_by_name( - project_name, ext.lstrip("."), version_doc["_id"] + project_name, ext.lstrip("."), version_entity["id"] ) if not representation: self.log.debug("No existing representation..") diff --git a/client/ayon_core/hosts/maya/api/lib.py b/client/ayon_core/hosts/maya/api/lib.py index dac4c6a672..d3b3fd4d26 100644 --- a/client/ayon_core/hosts/maya/api/lib.py +++ b/client/ayon_core/hosts/maya/api/lib.py @@ -21,10 +21,7 @@ from maya.api import OpenMaya import ayon_api -from ayon_core.client import ( - get_last_versions, - get_representation_by_name, -) +from ayon_core.client import get_representation_by_name from ayon_core.settings import get_project_settings from ayon_core.pipeline import ( get_current_project_name, @@ -1985,14 +1982,13 @@ def assign_look(nodes, product_name="lookDefault"): product_entity["id"] for product_entity in product_entities_by_folder_id.values() } - last_version_docs = get_last_versions( + last_version_entities = ayon_api.get_last_versions( project_name, - subset_ids=product_ids, - fields=["_id", "name", "data.families"] + product_ids ) - last_version_docs_by_product_id = { - last_version_doc["parent"]: last_version_doc - for last_version_doc in last_version_docs + last_version_entities_by_product_id = { + last_version_entity["productId"]: last_version_entity + for last_version_entity in last_version_entities } for folder_id, asset_nodes in grouped.items(): @@ -2004,14 +2000,14 @@ def assign_look(nodes, product_name="lookDefault"): continue product_id = product_entity["id"] - last_version = last_version_docs_by_product_id.get(product_id) + last_version = last_version_entities_by_product_id.get(product_id) if not last_version: log.warning(( "Not found last version for product '{}' on folder with id {}" ).format(product_name, folder_id)) continue - families = last_version.get("data", {}).get("families") or [] + families = last_version.get("attrib", {}).get("families") or [] if "look" not in families: log.warning(( "Last version for product '{}' on folder with id {}" @@ -2020,9 +2016,9 @@ def assign_look(nodes, product_name="lookDefault"): continue log.debug("Assigning look '{}' ".format( - product_name, last_version["name"])) + product_name, last_version["version"])) - assign_look_by_version(asset_nodes, last_version["_id"]) + assign_look_by_version(asset_nodes, last_version["id"]) def apply_shaders(relationships, shadernodes, nodes): diff --git a/client/ayon_core/hosts/maya/api/setdress.py b/client/ayon_core/hosts/maya/api/setdress.py index e070130e15..a69bf8d39b 100644 --- a/client/ayon_core/hosts/maya/api/setdress.py +++ b/client/ayon_core/hosts/maya/api/setdress.py @@ -11,8 +11,6 @@ import ayon_api from maya import cmds from ayon_core.client import ( - get_version_by_name, - get_last_version_by_subset_id, get_representation_by_id, get_representation_by_name, get_representation_parents, @@ -298,26 +296,27 @@ def update_package_version(container, version): assert current_representation is not None, "This is a bug" ( - version_doc, + version_entity, product_entity, folder_entity, project_entity ) = get_representation_parents(project_name, current_representation) if version == -1: - new_version = get_last_version_by_subset_id( + new_version = ayon_api.get_last_version_by_product_id( project_name, product_entity["id"] ) else: - new_version = get_version_by_name( + new_version = ayon_api.get_version_by_name( project_name, version, product_entity["id"] ) - assert new_version is not None, "This is a bug" + if new_version is None: + raise ValueError("Version not found: {}".format(version)) # Get the new representation (new file) new_representation = get_representation_by_name( - project_name, current_representation["name"], new_version["_id"] + project_name, current_representation["name"], new_version["id"] ) # TODO there is 'get_representation_context' to get the context which # could be possible to use here @@ -325,7 +324,7 @@ def update_package_version(container, version): "project": project_entity, "folder": folder_entity, "product": product_entity, - "version": version_doc, + "version": version_entity, "representation": new_representation, } update_package(container, new_context) diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/app.py b/client/ayon_core/hosts/maya/tools/mayalookassigner/app.py index 5158542a8f..44d8dfda21 100644 --- a/client/ayon_core/hosts/maya/tools/mayalookassigner/app.py +++ b/client/ayon_core/hosts/maya/tools/mayalookassigner/app.py @@ -2,10 +2,10 @@ import sys import time import logging +import ayon_api from qtpy import QtWidgets, QtCore from ayon_core import style -from ayon_core.client import get_last_version_by_subset_id from ayon_core.pipeline import get_current_project_name from ayon_core.tools.utils.lib import qt_app_context from ayon_core.hosts.maya.api.lib import ( @@ -240,8 +240,8 @@ class MayaLookAssignerWindow(QtWidgets.QWidget): continue # Get the latest version of this asset's look product - version = get_last_version_by_subset_id( - project_name, assign_look["_id"], fields=["_id"] + version_entity = ayon_api.get_last_version_by_product_id( + project_name, assign_look["id"], fields={"id"} ) product_name = assign_look["name"] @@ -283,7 +283,9 @@ class MayaLookAssignerWindow(QtWidgets.QWidget): # Assign look if nodes: - assign_look_by_version(nodes, version_id=version["_id"]) + assign_look_by_version( + nodes, version_id=version_entity["id"] + ) end = time.time() diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/arnold_standin.py b/client/ayon_core/hosts/maya/tools/mayalookassigner/arnold_standin.py index bd2f0baddf..a20880dffc 100644 --- a/client/ayon_core/hosts/maya/tools/mayalookassigner/arnold_standin.py +++ b/client/ayon_core/hosts/maya/tools/mayalookassigner/arnold_standin.py @@ -4,10 +4,11 @@ from collections import defaultdict import logging from maya import cmds +import ayon_api from ayon_core.pipeline import get_current_project_name -from ayon_core.client import get_last_version_by_subset_name from ayon_core.hosts.maya import api + from . import lib from .alembic import get_alembic_ids_cache from .usd import is_usd_lib_supported, get_usd_ids_cache @@ -146,20 +147,21 @@ def assign_look(standin, product_name): for folder_id, node_ids in node_ids_by_folder_id.items(): # Get latest look version - version = get_last_version_by_subset_name( + version_entity = ayon_api.get_last_version_by_product_name( project_name, - subset_name=product_name, - asset_id=folder_id, - fields=["_id"] + product_name, + folder_id, + fields={"id"} ) - if not version: + if not version_entity: log.info("Didn't find last version for product name {}".format( product_name )) continue + version_id = version_entity["id"] - relationships = lib.get_look_relationships(version["_id"]) - shader_nodes, container_node = lib.load_look(version["_id"]) + relationships = lib.get_look_relationships(version_id) + shader_nodes, container_node = lib.load_look(version_id) namespace = shader_nodes[0].split(":")[0] # Get only the node ids and paths related to this folder diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/vray_proxies.py b/client/ayon_core/hosts/maya/tools/mayalookassigner/vray_proxies.py index df74dcd217..74cdbeb7d4 100644 --- a/client/ayon_core/hosts/maya/tools/mayalookassigner/vray_proxies.py +++ b/client/ayon_core/hosts/maya/tools/mayalookassigner/vray_proxies.py @@ -4,8 +4,8 @@ from collections import defaultdict import logging from maya import cmds +import ayon_api -from ayon_core.client import get_last_version_by_subset_name from ayon_core.pipeline import get_current_project_name import ayon_core.hosts.maya.lib as maya_lib from . import lib @@ -73,27 +73,28 @@ def vrayproxy_assign_look(vrayproxy, product_name="lookDefault"): # Group by asset id so we run over the look per asset node_ids_by_asset_id = defaultdict(set) for node_id in nodes_by_id: - asset_id = node_id.split(":", 1)[0] - node_ids_by_asset_id[asset_id].add(node_id) + folder_id = node_id.split(":", 1)[0] + node_ids_by_asset_id[folder_id].add(node_id) project_name = get_current_project_name() - for asset_id, node_ids in node_ids_by_asset_id.items(): + for folder_id, node_ids in node_ids_by_asset_id.items(): # Get latest look version - version = get_last_version_by_subset_name( + version_entity = ayon_api.get_last_version_by_product_name( project_name, - subset_name=product_name, - asset_id=asset_id, - fields=["_id"] + product_name, + folder_id, + fields={"id"} ) - if not version: + if not version_entity: print("Didn't find last version for product name {}".format( product_name )) continue + version_id = version_entity["id"] - relationships = lib.get_look_relationships(version["_id"]) - shadernodes, _ = lib.load_look(version["_id"]) + relationships = lib.get_look_relationships(version_id) + shadernodes, _ = lib.load_look(version_id) # Get only the node ids and paths related to this asset # And get the shader edits the look supplies diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index 3f2621fa6f..88e3583410 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -14,11 +14,7 @@ import nuke from qtpy import QtCore, QtWidgets import ayon_api -from ayon_core.client import ( - get_versions, - get_last_versions, - get_representations, -) +from ayon_core.client import get_representations from ayon_core.host import HostDirmap from ayon_core.tools.utils import host_tools @@ -864,19 +860,21 @@ def check_inventory_versions(): repre_docs_by_id[repre_id] = repre_doc version_ids.add(repre_doc["parent"]) - version_docs = get_versions( - project_name, version_ids, fields=["_id", "name", "parent"] + version_entities = ayon_api.get_versions( + project_name, + version_ids=version_ids, + fields={"id", "version", "productId"}, ) # Store versions by id and collect product ids - version_docs_by_id = {} + version_entities_by_id = {} product_ids = set() - for version_doc in version_docs: - version_docs_by_id[version_doc["_id"]] = version_doc - product_ids.add(version_doc["parent"]) + for version_entity in version_entities: + version_entities_by_id[version_entity["id"]] = version_entity + product_ids.add(version_entity["productId"]) # Query last versions based on product ids - last_versions_by_product_id = get_last_versions( - project_name, subset_ids=product_ids, fields=["_id", "parent"] + last_versions_by_product_id = ayon_api.get_last_versions( + project_name, product_ids=product_ids, fields={"id", "productId"} ) # Loop through collected container nodes and their representation ids @@ -892,18 +890,18 @@ def check_inventory_versions(): continue version_id = repre_doc["parent"] - version_doc = version_docs_by_id.get(version_id) - if not version_doc: + version_entity = version_entities_by_id.get(version_id) + if not version_entity: log.warning(( "Could not find the version on node \"{}\"" ).format(node.name())) continue # Get last version based on product id - product_id = version_doc["parent"] + product_id = version_entity["productId"] last_version = last_versions_by_product_id[product_id] # Check if last version is same as current version - if last_version["_id"] == version_doc["_id"]: + if last_version["id"] == version_entity["id"]: color_value = "0x4ecd25ff" else: color_value = "0xd84f20ff" diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_published_version.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_published_version.py index 5ea8276dea..84c9fa3e62 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_published_version.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_published_version.py @@ -16,8 +16,8 @@ Provides: """ import pyblish.api +import ayon_api -from ayon_core.client import get_last_version_by_subset_name from ayon_core.pipeline.version_start import get_versioning_start @@ -44,12 +44,12 @@ class CollectPublishedVersion(pyblish.api.ContextPlugin): project_name = context.data["projectName"] folder_id = context.data["folderEntity"]["id"] - version_doc = get_last_version_by_subset_name( + version_entity = ayon_api.get_last_version_by_product_name( project_name, workfile_product_name, folder_id ) - if version_doc: - version_int = int(version_doc["name"]) + 1 + if version_entity: + version_int = int(version_entity["version"]) + 1 else: version_int = get_versioning_start( project_name, diff --git a/client/ayon_core/hosts/traypublisher/api/plugin.py b/client/ayon_core/hosts/traypublisher/api/plugin.py index 1039b570ee..257d01eb50 100644 --- a/client/ayon_core/hosts/traypublisher/api/plugin.py +++ b/client/ayon_core/hosts/traypublisher/api/plugin.py @@ -1,6 +1,5 @@ import ayon_api -from ayon_core.client import get_last_versions from ayon_core.lib.attribute_definitions import ( FileDef, BoolDef, @@ -181,10 +180,10 @@ class SettingsCreator(TrayPublishCreator): )) product_ids = {p["id"] for p in product_entities} - last_versions = get_last_versions( + last_versions = ayon_api.get_last_versions( self.project_name, product_ids, - fields=["name", "parent"]) + fields={"version", "productId"}) for product_entity in product_entities: product_id = product_entity["id"] @@ -194,7 +193,7 @@ class SettingsCreator(TrayPublishCreator): last_version = last_versions.get(product_id) version = 0 if last_version is not None: - version = last_version["name"] + version = last_version["version"] product_entities_by_folder_path[folder_path][product_name] += ( version ) diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py index 1bf23f84a5..0561e0f65c 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py @@ -4,11 +4,11 @@ import os import json import re from copy import deepcopy -import requests +import requests +import ayon_api import pyblish.api -from ayon_core.client import get_last_version_by_subset_name from ayon_core.pipeline import publish from ayon_core.lib import EnumDef, is_in_tests from ayon_core.pipeline.version_start import get_versioning_start @@ -411,16 +411,16 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin, project_name = context.data["projectName"] host_name = context.data["hostName"] if not version: - folder_id = None + version_entity = None if folder_entity: - folder_id = folder_entity["id"] - version = get_last_version_by_subset_name( - project_name, - product_name, - asset_id=folder_id - ) - if version: - version = int(version["name"]) + 1 + version_entity = ayon_api.get_last_version_by_product_name( + project_name, + product_name, + folder_entity["id"] + ) + + if version_entity: + version = int(version_entity["version"]) + 1 else: version = get_versioning_start( project_name, diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py index 3c78a95f1e..7a6abd5507 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py @@ -4,12 +4,12 @@ import os import json import re from copy import deepcopy + import requests import clique - +import ayon_api import pyblish.api -from ayon_core.client import get_last_version_by_subset_name from ayon_core.pipeline import publish from ayon_core.lib import EnumDef, is_in_tests from ayon_core.pipeline.version_start import get_versioning_start @@ -533,16 +533,16 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, project_name = context.data["projectName"] host_name = context.data["hostName"] if not version: - folder_id = None + version_entity = None if folder_entity: - folder_id = folder_entity["id"] - version = get_last_version_by_subset_name( - project_name, - product_name, - asset_id=folder_id - ) - if version: - version = int(version["name"]) + 1 + version_entity = ayon_api.get_last_version_by_product_name( + project_name, + product_name, + folder_entity["id"] + ) + + if version_entity: + version = int(version_entity["version"]) + 1 else: version = get_versioning_start( project_name, From 41b0c51171288de702b3f76971ea321e1d99aa11 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 6 Mar 2024 11:00:38 +0100 Subject: [PATCH 457/573] use 'get_folders' for folder regex filtering --- .../workfile/workfile_template_builder.py | 61 +++---------------- 1 file changed, 9 insertions(+), 52 deletions(-) diff --git a/client/ayon_core/pipeline/workfile/workfile_template_builder.py b/client/ayon_core/pipeline/workfile/workfile_template_builder.py index 07287b45c6..5e588862a9 100644 --- a/client/ayon_core/pipeline/workfile/workfile_template_builder.py +++ b/client/ayon_core/pipeline/workfile/workfile_template_builder.py @@ -26,12 +26,8 @@ from ayon_api import ( get_products, get_last_versions, ) -from ayon_api.graphql_queries import folders_graphql_query -from ayon_core.client import ( - get_representations, - get_ayon_server_api_connection, -) +from ayon_core.client import get_representations from ayon_core.settings import get_project_settings from ayon_core.host import IWorkfileHost, HostBase from ayon_core.lib import ( @@ -1437,50 +1433,6 @@ class PlaceholderLoadMixin(object): return {} - def _query_by_folder_regex(self, project_name, folder_regex): - """Query folders by folder path regex. - - WARNING: - This method will be removed once the same functionality is - available in ayon-python-api. - - Args: - project_name (str): Project name. - folder_regex (str): Regex for folder path. - - Returns: - list[str]: List of folder paths. - """ - - query = folders_graphql_query({"id"}) - - folders_field = None - for child in query._children: - if child.path != "project": - continue - - for project_child in child._children: - if project_child.path == "project/folders": - folders_field = project_child - break - if folders_field: - break - - if "folderPathRegex" not in query._variables: - folder_path_regex_var = query.add_variable( - "folderPathRegex", "String!" - ) - folders_field.set_filter("pathEx", folder_path_regex_var) - - query.set_variable_value("projectName", project_name) - if folder_regex: - query.set_variable_value("folderPathRegex", folder_regex) - - api = get_ayon_server_api_connection() - for parsed_data in query.continuous_query(api): - for folder in parsed_data["project"]["folders"]: - yield folder["id"] - def _get_representations(self, placeholder): """Prepared query of representations based on load options. @@ -1524,9 +1476,14 @@ class PlaceholderLoadMixin(object): folder_ids = [current_folder_entity["_id"]] elif builder_type == "all_folders": - folder_ids = list(self._query_by_folder_regex( - project_name, folder_path_regex - )) + folder_ids = { + folder_entity["id"] + for folder_entity in get_folders( + project_name, + folder_path_regex=folder_path_regex, + fields={"id"} + ) + } if not folder_ids: return [] From e812e8e9630ff423e2cfd4c116a34724315567b8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 6 Mar 2024 11:01:26 +0100 Subject: [PATCH 458/573] tools use version entity --- .../ayon_core/tools/loader/models/actions.py | 143 +++++++----------- .../tools/loader/models/site_sync.py | 6 +- .../tools/loader/ui/products_delegates.py | 6 +- .../tools/push_to_project/control.py | 29 ++-- .../tools/push_to_project/models/integrate.py | 131 +++++++++------- client/ayon_core/tools/utils/lib.py | 10 +- 6 files changed, 161 insertions(+), 164 deletions(-) diff --git a/client/ayon_core/tools/loader/models/actions.py b/client/ayon_core/tools/loader/models/actions.py index d5da2ca80e..0a1c5b6080 100644 --- a/client/ayon_core/tools/loader/models/actions.py +++ b/client/ayon_core/tools/loader/models/actions.py @@ -7,10 +7,7 @@ import uuid import ayon_api -from ayon_core.client import ( - get_versions, - get_representations, -) +from ayon_core.client import get_representations from ayon_core.pipeline.load import ( discover_loader_plugins, ProductLoaderPlugin, @@ -34,12 +31,12 @@ class LoaderActionsModel: This is probably only part of models that requires to use codebase from 'ayon_core.client' because of backwards compatibility with loaders logic - which are expecting mongo documents. + which are expecting entities. TODOs: Deprecate 'qargparse' usage in loaders and implement conversion of 'ActionItem' to data (and 'from_data'). - Use controller to get entities (documents) -> possible only when + Use controller to get entities -> possible only when loaders are able to handle AYON vs. OpenPype logic. Add missing site sync logic, and if possible remove it from loaders. Implement loader actions to replace load plugins. @@ -367,39 +364,6 @@ class LoaderActionsModel: return action_item.order, action_item.label - def _get_version_docs(self, project_name, version_ids): - """Get version documents for given version ids. - - This function also handles hero versions and copies data from - source version to it. - - Todos: - Remove this function when this is completely rewritten to - use AYON calls. - """ - - version_docs = list(get_versions( - project_name, version_ids=version_ids, hero=True - )) - hero_versions_by_src_id = collections.defaultdict(list) - src_hero_version = set() - for version_doc in version_docs: - if version_doc["type"] != "hero": - continue - version_id = "" - src_hero_version.add(version_id) - hero_versions_by_src_id[version_id].append(version_doc) - - src_versions = [] - if src_hero_version: - src_versions = get_versions(project_name, version_ids=version_ids) - for src_version in src_versions: - src_version_id = src_version["_id"] - for hero_version in hero_versions_by_src_id[src_version_id]: - hero_version["data"] = copy.deepcopy(src_version["data"]) - - return version_docs - def _contexts_for_versions(self, project_name, version_ids): """Get contexts for given version ids. @@ -408,7 +372,7 @@ class LoaderActionsModel: given versions. This method is very similar to '_contexts_for_representations' but the - queries of documents are called in a different order. + queries of entities are called in a different order. Args: project_name (str): Project name. @@ -425,16 +389,18 @@ class LoaderActionsModel: if not project_name and not version_ids: return version_context_by_id, repre_context_by_id - version_docs = self._get_version_docs(project_name, version_ids) - version_docs_by_id = {} - version_docs_by_product_id = collections.defaultdict(list) - for version_doc in version_docs: - version_id = version_doc["_id"] - product_id = version_doc["parent"] - version_docs_by_id[version_id] = version_doc - version_docs_by_product_id[product_id].append(version_doc) + version_entities = ayon_api.get_versions( + project_name, version_ids=version_ids + ) + version_entities_by_id = {} + version_entities_by_product_id = collections.defaultdict(list) + for version_entity in version_entities: + version_id = version_entity["id"] + product_id = version_entity["productId"] + version_entities_by_id[version_id] = version_entity + version_entities_by_product_id[product_id].append(version_entity) - _product_ids = set(version_docs_by_product_id.keys()) + _product_ids = set(version_entities_by_product_id.keys()) _product_entities = ayon_api.get_products( project_name, product_ids=_product_ids ) @@ -448,9 +414,9 @@ class LoaderActionsModel: project_entity = ayon_api.get_project(project_name) - for version_doc in version_docs: - version_id = version_doc["_id"] - product_id = version_doc["parent"] + for version_entity in version_entities_by_id: + version_id = version_entity["id"] + product_id = version_entity["productId"] product_entity = product_entities_by_id[product_id] folder_id = product_entity["folderId"] folder_entity = folder_entities_by_id[folder_id] @@ -458,15 +424,15 @@ class LoaderActionsModel: "project": project_entity, "folder": folder_entity, "product": product_entity, - "version": version_doc, + "version": version_entity, } repre_docs = get_representations( project_name, version_ids=version_ids) for repre_doc in repre_docs: version_id = repre_doc["parent"] - version_doc = version_docs_by_id[version_id] - product_id = version_doc["parent"] + version_entity = version_entities_by_id[version_id] + product_id = version_entity["productId"] product_entity = product_entities_by_id[product_id] folder_id = product_entity["folderId"] folder_entity = folder_entities_by_id[folder_id] @@ -475,7 +441,7 @@ class LoaderActionsModel: "project": project_entity, "folder": folder_entity, "product": product_entity, - "version": version_doc, + "version": version_entity, "representation": repre_doc, } @@ -489,7 +455,7 @@ class LoaderActionsModel: given versions. This method is very similar to '_contexts_for_versions' but the - queries of documents are called in a different order. + queries of entities are called in a different order. Args: project_name (str): Project name. @@ -509,12 +475,14 @@ class LoaderActionsModel: project_name, representation_ids=repre_ids )) version_ids = {r["parent"] for r in repre_docs} - version_docs = self._get_version_docs(project_name, version_ids) - version_docs_by_id = { - v["_id"]: v for v in version_docs + version_entities = ayon_api.get_versions( + project_name, version_ids=version_ids + ) + version_entities_by_id = { + v["id"]: v for v in version_entities } - product_ids = {v["parent"] for v in version_docs_by_id.values()} + product_ids = {v["productId"] for v in version_entities_by_id.values()} product_entities = ayon_api.get_products( project_name, product_ids=product_ids ) @@ -543,8 +511,8 @@ class LoaderActionsModel: for repre_doc in repre_docs: version_id = repre_doc["parent"] - version_doc = version_docs_by_id[version_id] - product_id = version_doc["parent"] + version_entity = version_entities_by_id[version_id] + product_id = version_entity["productId"] product_entity = product_entities_by_id[product_id] folder_id = product_entity["folderId"] folder_entity = folder_entities_by_id[folder_id] @@ -553,7 +521,7 @@ class LoaderActionsModel: "project": project_entity, "folder": folder_entity, "product": product_entity, - "version": version_doc, + "version": version_entity, "representation": repre_doc, } return product_context_by_id, repre_context_by_id @@ -667,8 +635,10 @@ class LoaderActionsModel: project_entity = ayon_api.get_project(project_name) - version_docs = self._get_version_docs(project_name, version_ids) - product_ids = {v["parent"] for v in version_docs} + version_entities = list(ayon_api.get_versions( + project_name, version_ids=version_ids + )) + product_ids = {v["productId"] for v in version_entities} product_entities = ayon_api.get_products( project_name, product_ids=product_ids ) @@ -679,8 +649,8 @@ class LoaderActionsModel: ) folder_entities_by_id = {f["id"]: f for f in folder_entities} product_contexts = [] - for version_doc in version_docs: - product_id = version_doc["parent"] + for version_entity in version_entities: + product_id = version_entity["productId"] product_entity = product_entities_by_id[product_id] folder_id = product_entity["folderId"] folder_entity = folder_entities_by_id[folder_id] @@ -688,7 +658,7 @@ class LoaderActionsModel: "project": project_entity, "folder": folder_entity, "product": product_entity, - "version": version_doc, + "version": version_entity, }) return self._load_products_by_loader( @@ -706,7 +676,7 @@ class LoaderActionsModel: This triggers 'load' method of 'LoaderPlugin' for given representation ids. For that are prepared contexts for each representation, with - all parent documents. + all parent entities. Args: loader (LoaderPlugin): Loader plugin to use. @@ -720,9 +690,13 @@ class LoaderActionsModel: project_name, representation_ids=representation_ids )) version_ids = {r["parent"] for r in repre_docs} - version_docs = self._get_version_docs(project_name, version_ids) - version_docs_by_id = {v["_id"]: v for v in version_docs} - product_ids = {v["parent"] for v in version_docs_by_id.values()} + version_entities = ayon_api.get_versions( + project_name, version_ids=version_ids + ) + version_entities_by_id = {v["id"]: v for v in version_entities} + product_ids = { + v["productId"] for v in version_entities_by_id.values() + } product_entities = ayon_api.get_products( project_name, product_ids=product_ids ) @@ -735,8 +709,8 @@ class LoaderActionsModel: repre_contexts = [] for repre_doc in repre_docs: version_id = repre_doc["parent"] - version_doc = version_docs_by_id[version_id] - product_id = version_doc["parent"] + version_entity = version_entities_by_id[version_id] + product_id = version_entity["productId"] product_entity = product_entities_by_id[product_id] folder_id = product_entity["folderId"] folder_entity = folder_entities_by_id[folder_id] @@ -744,7 +718,7 @@ class LoaderActionsModel: "project": project_entity, "folder": folder_entity, "product": product_entity, - "version": version_doc, + "version": version_entity, "representation": repre_doc, }) @@ -758,18 +732,17 @@ class LoaderActionsModel: Args: loader (LoaderPlugin): Loader plugin to use. repre_contexts (list[dict]): Full info about selected - representations, containing repre, version, product, folder and - project documents. + representations, containing repre, version, product, folder + and project entities. options (dict): Data from options. """ error_info = [] for repre_context in repre_contexts: - version_doc = repre_context["version"] - if version_doc["type"] == "hero_version": - version_name = "Hero" - else: - version_name = version_doc.get("name") + version_entity = repre_context["version"] + version = version_entity["version"] + if version < 0: + version = "Hero" try: load_with_repre_context( loader, @@ -784,7 +757,7 @@ class LoaderActionsModel: None, repre_context["representation"]["name"], repre_context["product"]["name"], - version_name + version )) except Exception as exc: @@ -800,7 +773,7 @@ class LoaderActionsModel: formatted_traceback, repre_context["representation"]["name"], repre_context["product"]["name"], - version_name + version )) return error_info diff --git a/client/ayon_core/tools/loader/models/site_sync.py b/client/ayon_core/tools/loader/models/site_sync.py index 2a6f1558ad..ff30a3e6fa 100644 --- a/client/ayon_core/tools/loader/models/site_sync.py +++ b/client/ayon_core/tools/loader/models/site_sync.py @@ -1,8 +1,10 @@ import collections from ayon_core.lib import Logger -from ayon_core.client.entities import get_representations -from ayon_core.client import get_linked_representation_id +from ayon_core.client import ( + get_representations, + get_linked_representation_id, +) from ayon_core.addon import AddonsManager from ayon_core.tools.ayon_utils.models import NestedCacheItem from ayon_core.tools.loader.abstract import ActionItem diff --git a/client/ayon_core/tools/loader/ui/products_delegates.py b/client/ayon_core/tools/loader/ui/products_delegates.py index 53d35c2bb7..12ed1165ae 100644 --- a/client/ayon_core/tools/loader/ui/products_delegates.py +++ b/client/ayon_core/tools/loader/ui/products_delegates.py @@ -50,9 +50,7 @@ class VersionComboBox(QtWidgets.QComboBox): item = self._items_by_id.get(version_id) if item is None: - label = format_version( - abs(version_item.version), version_item.is_hero - ) + label = format_version(version_item.version) item = QtGui.QStandardItem(label) item.setData(version_id, QtCore.Qt.UserRole) self._items_by_id[version_id] = item @@ -85,7 +83,7 @@ class VersionDelegate(QtWidgets.QStyledItemDelegate): def displayText(self, value, locale): if not isinstance(value, numbers.Integral): return "N/A" - return format_version(abs(value), value < 0) + return format_version(value) def paint(self, painter, option, index): fg_color = index.data(QtCore.Qt.ForegroundRole) diff --git a/client/ayon_core/tools/push_to_project/control.py b/client/ayon_core/tools/push_to_project/control.py index 1bc03d2285..e74bc8c210 100644 --- a/client/ayon_core/tools/push_to_project/control.py +++ b/client/ayon_core/tools/push_to_project/control.py @@ -2,10 +2,7 @@ import threading import ayon_api -from ayon_core.client import ( - get_version_by_id, - get_representations, -) +from ayon_core.client import get_representations from ayon_core.settings import get_project_settings from ayon_core.lib import prepare_template_data from ayon_core.lib.events import QueuedEventSystem @@ -35,7 +32,7 @@ class PushToContextController: self._src_folder_entity = None self._src_folder_task_entities = {} self._src_product_entity = None - self._src_version_doc = None + self._src_version_entity = None self._src_label = None self._submission_enabled = False @@ -75,13 +72,15 @@ class PushToContextController: folder_entity = None task_entities = {} product_entity = None - version_doc = None + version_entity = None if project_name and version_id: - version_doc = get_version_by_id(project_name, version_id) + version_entity = ayon_api.get_version_by_id( + project_name, version_id + ) - if version_doc: + if version_entity: product_entity = ayon_api.get_product_by_id( - project_name, version_doc["parent"] + project_name, version_entity["productId"] ) if product_entity: @@ -100,14 +99,14 @@ class PushToContextController: self._src_folder_entity = folder_entity self._src_folder_task_entities = task_entities self._src_product_entity = product_entity - self._src_version_doc = version_doc + self._src_version_entity = version_entity if folder_entity: self._user_values.set_new_folder_name(folder_entity["name"]) variant = self._get_src_variant() if variant: self._user_values.set_variant(variant) - comment = version_doc["data"].get("comment") + comment = version_entity["attrib"].get("comment") if comment: self._user_values.set_comment(comment) @@ -218,12 +217,12 @@ class PushToContextController: folder_path = folder_entity["path"] product_entity = self._src_product_entity - version_doc = self._src_version_doc + version_entity = self._src_version_entity return "Source: {}{}/{}/v{:0>3}".format( self._src_project_name, folder_path, product_entity["name"], - version_doc["name"] + version_entity["version"] ) def _get_task_info_from_repre_docs(self, task_entities, repre_docs): @@ -256,10 +255,10 @@ class PushToContextController: def _get_src_variant(self): project_name = self._src_project_name - version_doc = self._src_version_doc + version_entity = self._src_version_entity task_entities = self._src_folder_task_entities repre_docs = get_representations( - project_name, version_ids=[version_doc["_id"]] + project_name, version_ids=[version_entity["id"]] ) task_name, task_type = self._get_task_info_from_repre_docs( task_entities, repre_docs diff --git a/client/ayon_core/tools/push_to_project/models/integrate.py b/client/ayon_core/tools/push_to_project/models/integrate.py index 3fe5fa5ea1..91b9a65a8c 100644 --- a/client/ayon_core/tools/push_to_project/models/integrate.py +++ b/client/ayon_core/tools/push_to_project/models/integrate.py @@ -1,7 +1,6 @@ import os import re import copy -import socket import itertools import datetime import sys @@ -10,24 +9,15 @@ import uuid import ayon_api -from ayon_core.client import ( - get_version_by_id, - get_last_version_by_subset_id, - get_version_by_name, - get_representations, -) +from ayon_core.client import get_representations from ayon_core.client.operations import ( OperationsSession, - new_version_doc, new_representation_doc, - prepare_version_update_data, prepare_representation_update_data, ) from ayon_core.addon import AddonsManager from ayon_core.lib import ( StringTemplate, - get_ayon_username, - get_formatted_current_time, source_hash, ) @@ -445,7 +435,7 @@ class ProjectPushItemProcess: self._src_folder_entity = None self._src_product_entity = None - self._src_version_doc = None + self._src_version_entity = None self._src_repre_items = None self._project_entity = None @@ -453,7 +443,7 @@ class ProjectPushItemProcess: self._folder_entity = None self._task_info = None self._product_entity = None - self._version_doc = None + self._version_entity = None self._product_type = None self._product_name = None @@ -570,16 +560,20 @@ class ProjectPushItemProcess: self._log_debug(f"Project '{src_project_name}' found") - version_doc = get_version_by_id(src_project_name, src_version_id) - if not version_doc: + version_entity = ayon_api.get_version_by_id( + src_project_name, src_version_id + ) + if not version_entity: self._status.set_failed(( f"Source version with id \"{src_version_id}\"" f" was not found in project \"{src_project_name}\"" )) raise PushToProjectError(self._status.fail_reason) - product_id = version_doc["parent"] - product_entity = ayon_api.get_product_by_id(src_project_name, product_id) + product_id = version_entity["productId"] + product_entity = ayon_api.get_product_by_id( + src_project_name, product_id + ) if not product_entity: self._status.set_failed(( f"Could find product with id \"{product_id}\"" @@ -621,7 +615,7 @@ class ProjectPushItemProcess: self._src_folder_entity = folder_entity self._src_product_entity = product_entity - self._src_version_doc = version_doc + self._src_version_entity = version_entity self._src_repre_items = repre_items def _fill_destination_project(self): @@ -882,27 +876,42 @@ class ProjectPushItemProcess: project_name = self._item.dst_project_name version = self._item.dst_version - src_version_doc = self._src_version_doc + src_version_entity = self._src_version_entity product_entity = self._product_entity product_id = product_entity["id"] - src_data = src_version_doc["data"] product_type = product_entity["productType"] + src_attrib = src_version_entity["attrib"] + + dst_attrib = {} + for key in { + "productType", + "productTypes", + "families", + "fps", + "pixelAspect", + "clipIn", + "clipOut", + "frameStart", + "frameEnd", + "handleStart", + "handleEnd", + "resolutionWidth", + "resolutionHeight", + "colorSpace", + "source", + "comment", + "description", + "intent", + }: + if key in src_attrib: + dst_attrib[key] = src_attrib[key] - version_data = { - "families": [product_type], - "fps": src_data.get("fps"), - "source": src_data.get("source"), - "machine": socket.gethostname(), - "comment": self._item.comment or "", - "author": get_ayon_username(), - "time": get_formatted_current_time(), - } if version is None: - last_version_doc = get_last_version_by_subset_id( + last_version_entity = ayon_api.get_last_version_by_product_id( project_name, product_id ) - if last_version_doc: - version = int(last_version_doc["name"]) + 1 + if last_version_entity: + version = int(last_version_entity["version"]) + 1 else: version = get_versioning_start( project_name, @@ -913,34 +922,50 @@ class ProjectPushItemProcess: product_name=product_entity["name"] ) - existing_version_doc = get_version_by_name( + existing_version_entity = ayon_api.get_version_by_name( project_name, version, product_id ) # Update existing version - if existing_version_doc: - version_doc = new_version_doc( - version, product_id, version_data, existing_version_doc["_id"] + if existing_version_entity: + ayon_api.patch( + "projects/{}/versions/{}".format( + project_name, existing_version_entity["id"] + ), + **{"attrib": dst_attrib} ) - update_data = prepare_version_update_data( - existing_version_doc, version_doc + version_entity = ayon_api.get_version_by_id( + project_name, existing_version_entity["id"] ) - if update_data: - self._operations.update_entity( - project_name, - "version", - existing_version_doc["_id"], - update_data - ) - self._version_doc = version_doc + # TODO use operations + # self._operations.update_entity( + # project_name, + # "version", + # existing_version_entity["id"], + # update_data + # ) + self._version_entity = version_entity return - version_doc = new_version_doc( - version, product_id, version_data + response = ayon_api.post( + "projects/{}/versions/{}".format( + project_name, existing_version_entity["id"] + ), + **{ + "version": version, + "productId": product_id, + "attrib": dst_attrib, + } ) - self._operations.create_entity(project_name, "version", version_doc) + version_entity = ayon_api.get_version_by_id( + project_name, response.data["id"] + ) + # TODO use operations + # self._operations.create_entity( + # project_name, "version", version_entity + # ) - self._version_doc = version_doc + self._version_entity = version_entity def _integrate_representations(self): try: @@ -951,8 +976,8 @@ class ProjectPushItemProcess: raise def _real_integrate_representations(self): - version_doc = self._version_doc - version_id = version_doc["_id"] + version_entity = self._version_entity + version_id = version_entity["id"] existing_repres = get_representations( self._item.dst_project_name, version_ids=[version_id] @@ -976,7 +1001,7 @@ class ProjectPushItemProcess: "name": self._product_name, "type": self._product_type, }, - "version": version_doc["name"] + "version": version_entity["version"] }) path_template = anatomy.templates[template_name]["path"].replace( diff --git a/client/ayon_core/tools/utils/lib.py b/client/ayon_core/tools/utils/lib.py index 2e70f27da5..741fc1f335 100644 --- a/client/ayon_core/tools/utils/lib.py +++ b/client/ayon_core/tools/utils/lib.py @@ -120,12 +120,12 @@ def paint_image_with_color(image, color): return pixmap -def format_version(value, hero_version=False): +def format_version(value): """Formats integer to displayable version name""" - label = "v{0:03d}".format(value) - if not hero_version: - return label - return "[{}]".format(label) + label = "v{0:03d}".format(abs(value)) + if value < 0: + return "[{}]".format(label) + return label @contextlib.contextmanager From 2caf32c5b05f0153a8f1c25ad80cb08301d6d6e3 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 6 Mar 2024 23:08:40 +0800 Subject: [PATCH 459/573] supports the check on only one keyframe in timeline --- .../plugins/publish/validate_no_animation.py | 19 ++++++++++++++++++- server_addon/max/server/version.py | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_no_animation.py b/client/ayon_core/hosts/max/plugins/publish/validate_no_animation.py index 0b7a296cd9..4b2a18d606 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_no_animation.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_no_animation.py @@ -8,6 +8,23 @@ from ayon_core.pipeline import ( from ayon_core.hosts.max.api.action import SelectInvalidAction +def get_invalid_keys(obj): + """function to check on whether there is keyframe in + + Args: + obj (str): object needed to check if there is a keyframe + + Returns: + bool: whether invalid object(s) exist + """ + for transform in ["Position", "Rotation", "Scale"]: + num_of_key = rt.NumKeys(rt.getPropertyController( + obj.controller, transform)) + if num_of_key > 0: + return True + return False + + class ValidateNoAnimation(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): """Validates No Animation @@ -45,6 +62,6 @@ class ValidateNoAnimation(pyblish.api.InstancePlugin, list: list of invalid objects """ invalid = [invalid for invalid in instance.data["members"] - if invalid.isAnimated] + if invalid.isAnimated or get_invalid_keys(invalid)] return invalid diff --git a/server_addon/max/server/version.py b/server_addon/max/server/version.py index 0a8da88258..f1380eede2 100644 --- a/server_addon/max/server/version.py +++ b/server_addon/max/server/version.py @@ -1 +1 @@ -__version__ = "0.1.6" +__version__ = "0.1.7" From fd8bf2cc67d03d9e7b80c903e1d6ccb40d6c0df0 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 6 Mar 2024 16:29:12 +0100 Subject: [PATCH 460/573] Adjust clip start if slate is present Trim the clip's start time by 1 frame and update its duration accordingly when a "slate" is detected in the version data families. Also, corrected calculation for `frame_end_handle` to use `frame_end`. --- client/ayon_core/hosts/resolve/api/plugin.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/resolve/api/plugin.py b/client/ayon_core/hosts/resolve/api/plugin.py index 157b8de363..dfce3ea37a 100644 --- a/client/ayon_core/hosts/resolve/api/plugin.py +++ b/client/ayon_core/hosts/resolve/api/plugin.py @@ -410,6 +410,11 @@ class ClipLoader: source_out = int(_clip_property("End")) source_duration = int(_clip_property("Frames")) + # Trim clip start if slate is present + if "slate" in self.data["versionData"]["families"]: + source_in += 1 + source_duration = source_out - source_in + 1 + if not self.with_handles: # Load file without the handles of the source media # We remove the handles from the source in and source out @@ -435,7 +440,7 @@ class ClipLoader: handle_start = version_data.get("handleStart", 0) handle_end = version_data.get("handleEnd", 0) frame_start_handle = frame_start - handle_start - frame_end_handle = frame_start + handle_end + frame_end_handle = frame_end + handle_end database_frame_duration = int( frame_end_handle - frame_start_handle + 1 ) From b5873d47a8c8b9f4d812421bee0221f4ad7b7030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Wed, 6 Mar 2024 16:44:18 +0100 Subject: [PATCH 461/573] Update bug_report.yml --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 5a0ed1ae1d..e6badf936a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,6 +1,6 @@ name: Bug Report description: File a bug report -title: '' +title: 'Your issue title here' labels: - 'type: bug' body: From 14db89cc359c2240727a24cc832e1733dbb67414 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Wed, 6 Mar 2024 16:45:43 +0100 Subject: [PATCH 462/573] Update enhancement_request.yml --- .github/ISSUE_TEMPLATE/enhancement_request.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/enhancement_request.yml b/.github/ISSUE_TEMPLATE/enhancement_request.yml index da4d0d9319..31b2eb2edd 100644 --- a/.github/ISSUE_TEMPLATE/enhancement_request.yml +++ b/.github/ISSUE_TEMPLATE/enhancement_request.yml @@ -1,6 +1,6 @@ name: Enhancement Request description: Create a report to help us enhance a particular feature -title: "" +title: "Your issue title here" labels: - "type: enhancement" body: @@ -49,4 +49,4 @@ body: label: "Additional context:" description: Add any other context or screenshots about the enhancement request here. validations: - required: false \ No newline at end of file + required: false From 091a706518e229fbb2c019351ef8dc73fa6b8f8b Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 7 Mar 2024 11:49:20 +0000 Subject: [PATCH 463/573] Dont brute force colorspaces list --- client/ayon_core/hosts/nuke/api/utils.py | 37 ++++++++++-------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/utils.py b/client/ayon_core/hosts/nuke/api/utils.py index 0a05077a68..80d719664a 100644 --- a/client/ayon_core/hosts/nuke/api/utils.py +++ b/client/ayon_core/hosts/nuke/api/utils.py @@ -1,4 +1,6 @@ import os +import re + import nuke from ayon_core import resources @@ -110,35 +112,28 @@ def colorspace_exists_on_node(node, colorspace_name): def get_colorspace_list(colorspace_knob): """Get available colorspace profile names - Because the values returned from colorspace_knob.values() do not correspond - to the value returned from colorspace_knob.value(), and the extracted - colorspace comes from using colorspace_knob.value(), we need to iterate - through all values to get the correct value. - - A code example of the above would be: - - for count, value in enumerate(colorspace_knob.values()): - colorspace_knob.setValue(count) - print(colorspace_knob.value() in colorspace_knob.values()) - Args: colorspace_knob (nuke.Knob): nuke knob object Returns: list: list of strings names of profiles """ - original_value = colorspace_knob.value() + results = [] - colorspaces = [] + # This pattern is to match with roles which uses an indentation and + # parentheses with original colorspace. The value returned from the + # colorspace is the string before the indentation, so we'll need to + # convert the values to match with value returned from the knob, + # ei. knob.value(). + pattern = r".*\t.* \(.*\)" + for colorspace in nuke.getColorspaceList(colorspace_knob): + match = re.search(pattern, colorspace) + if match: + results.append(colorspace.split("\t")[0]) + else: + results.append(colorspace) - try: - for count, _ in enumerate(colorspace_knob.values()): - colorspace_knob.setValue(count) - colorspaces.append(colorspace_knob.value()) - finally: - colorspace_knob.setValue(original_value) - - return colorspaces + return results def is_headless(): From 5120df1157b0b865430f36ebd213d3887731edb0 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Thu, 7 Mar 2024 12:14:44 +0000 Subject: [PATCH 464/573] Update client/ayon_core/hosts/nuke/api/utils.py Co-authored-by: Roy Nieterau --- client/ayon_core/hosts/nuke/api/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/nuke/api/utils.py b/client/ayon_core/hosts/nuke/api/utils.py index 80d719664a..1bfc1919fa 100644 --- a/client/ayon_core/hosts/nuke/api/utils.py +++ b/client/ayon_core/hosts/nuke/api/utils.py @@ -129,7 +129,7 @@ def get_colorspace_list(colorspace_knob): for colorspace in nuke.getColorspaceList(colorspace_knob): match = re.search(pattern, colorspace) if match: - results.append(colorspace.split("\t")[0]) + results.append(colorspace.split("\t", 1)[0]) else: results.append(colorspace) From 2fe09689e8d317838ea218332ecd022f862f60d9 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 7 Mar 2024 21:57:24 +0100 Subject: [PATCH 465/573] missing import --- client/ayon_core/hosts/nuke/plugins/load/load_clip.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py index a75703b718..98c3303eb8 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py @@ -11,6 +11,9 @@ from ayon_core.pipeline import ( get_current_project_name, get_representation_path, ) +from ayon_core.pipeline.colorspace import ( + get_imageio_file_rules_colorspace_from_filepath +) from ayon_core.hosts.nuke.api.lib import ( get_imageio_input_colorspace, maintained_selection From 1a0b23650883bdafd2585f6c5ecc6bbbb7c0c176 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 7 Mar 2024 19:10:40 +0000 Subject: [PATCH 466/573] Remove representation unnessecary frameStart --- .../ayon_core/hosts/nuke/plugins/publish/collect_writes.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_writes.py b/client/ayon_core/hosts/nuke/plugins/publish/collect_writes.py index 58afb2cd1f..745351dc49 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/collect_writes.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/collect_writes.py @@ -194,7 +194,6 @@ class CollectNukeWrites(pyblish.api.InstancePlugin, "frameEndHandle": last_frame, }) - # TODO temporarily set stagingDir as persistent for backward # compatibility. This is mainly focused on `renders`folders which # were previously not cleaned up (and could be used in read notes) @@ -269,10 +268,6 @@ class CollectNukeWrites(pyblish.api.InstancePlugin, "tags": [] } - frame_start_str = self._get_frame_start_str(first_frame, last_frame) - - representation['frameStart'] = frame_start_str - # set slate frame collected_frames = self._add_slate_frame_to_collected_frames( instance, From 8b4085b3c3ad34125df14bbfd7b81ff2856caa61 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 7 Mar 2024 19:10:58 +0000 Subject: [PATCH 467/573] Exclude instances nodes from slate collection. --- .../ayon_core/hosts/nuke/plugins/publish/collect_slate_node.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_slate_node.py b/client/ayon_core/hosts/nuke/plugins/publish/collect_slate_node.py index 3baa0cd9b5..ac30bd6051 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/collect_slate_node.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/collect_slate_node.py @@ -17,7 +17,8 @@ class CollectSlate(pyblish.api.InstancePlugin): ( n_ for n_ in nuke.allNodes() if "slate" in n_.name().lower() - if not n_["disable"].getValue() + if not n_["disable"].getValue() and + "publish_instance" not in n_.knobs() # Exclude instance nodes. ), None ) From 4feaf4841f657e13e0468ed80a8ceca8ba208674 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 11:59:57 +0100 Subject: [PATCH 468/573] modified load utils to expect representation entity --- client/ayon_core/pipeline/load/__init__.py | 6 +- client/ayon_core/pipeline/load/plugins.py | 16 +- client/ayon_core/pipeline/load/utils.py | 298 ++++++++++++--------- 3 files changed, 178 insertions(+), 142 deletions(-) diff --git a/client/ayon_core/pipeline/load/__init__.py b/client/ayon_core/pipeline/load/__init__.py index 0e2768dbe1..bdc5ece620 100644 --- a/client/ayon_core/pipeline/load/__init__.py +++ b/client/ayon_core/pipeline/load/__init__.py @@ -8,9 +8,10 @@ from .utils import ( LoaderNotFoundError, get_repres_contexts, - get_contexts_for_repre_docs, get_product_contexts, get_representation_context, + get_representation_contexts, + get_representation_contexts_by_ids, load_with_repre_context, load_with_product_context, @@ -62,9 +63,10 @@ __all__ = ( "LoaderNotFoundError", "get_repres_contexts", - "get_contexts_for_repre_docs", "get_product_contexts", "get_representation_context", + "get_representation_contexts", + "get_representation_contexts_by_ids", "load_with_repre_context", "load_with_product_context", diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 797737966d..b5d228380a 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -76,11 +76,11 @@ class LoaderPlugin(list): setattr(cls, option, value) @classmethod - def has_valid_extension(cls, repre_doc): + def has_valid_extension(cls, repre_entity): """Has representation document valid extension for loader. Args: - repre_doc (dict[str, Any]): Representation document. + repre_entity (dict[str, Any]): Representation entity. Returns: bool: Representation has valid extension @@ -90,11 +90,11 @@ class LoaderPlugin(list): return True # Get representation main file extension from 'context' - repre_context = repre_doc.get("context") or {} + repre_context = repre_entity.get("context") or {} ext = repre_context.get("ext") if not ext: # Legacy way how to get extensions - path = repre_doc.get("data", {}).get("path") + path = repre_entity.get("attrib", {}).get("path") if not path: cls.log.info( "Representation doesn't have known source of extension" @@ -138,18 +138,18 @@ class LoaderPlugin(list): ): return False - repre_doc = context.get("representation") - if not repre_doc: + repre_entity = context.get("representation") + if not repre_entity: return False plugin_repre_names = set(plugin_repre_names) if ( "*" not in plugin_repre_names - and repre_doc["name"] not in plugin_repre_names + and repre_entity["name"] not in plugin_repre_names ): return False - if not cls.has_valid_extension(repre_doc): + if not cls.has_valid_extension(repre_entity): return False plugin_product_types = set(plugin_product_types) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index ed0a7c4848..28bb3b8955 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -8,12 +8,6 @@ import numbers import ayon_api from ayon_core.host import ILoadHost -from ayon_core.client import ( - get_representations, - get_representation_by_id, - get_representation_by_name, - get_representation_parents -) from ayon_core.lib import ( StringTemplate, TemplateUnsolved, @@ -99,64 +93,11 @@ def get_repres_contexts(representation_ids, project_name=None): if not project_name: project_name = get_current_project_name() - repre_docs = get_representations(project_name, representation_ids) - - return get_contexts_for_repre_docs(project_name, repre_docs) - - -def get_contexts_for_repre_docs(project_name, repre_docs): - contexts = {} - if not repre_docs: - return contexts - - 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_entities = ayon_api.get_versions( - project_name, version_ids=version_ids + repre_entities = ayon_api.get_representations( + project_name, representation_ids ) - version_entities_by_id = {} - product_ids = set() - for version_entity in version_entities: - version_entities_by_id[version_entity["id"]] = version_entity - product_ids.add(version_entity["productId"]) - - product_entities = ayon_api.get_products( - project_name, product_ids=product_ids - ) - product_entities_by_id = {} - folder_ids = set() - for product_entity in product_entities: - product_entities_by_id[product_entity["id"]] = product_entity - folder_ids.add(product_entity["folderId"]) - - folder_entities_by_id = { - folder_entity["id"]: folder_entity - for folder_entity in ayon_api.get_folders( - project_name, folder_ids=folder_ids - ) - } - - project_entity = ayon_api.get_project(project_name) - - for repre_id, repre_doc in repre_docs_by_id.items(): - version_entity = version_entities_by_id[repre_doc["parent"]] - product_entity = product_entities_by_id[version_entity["productId"]] - folder_entity = folder_entities_by_id[product_entity["folderId"]] - context = { - "project": project_entity, - "folder": folder_entity, - "product": product_entity, - "version": version_entity, - "representation": repre_doc, - } - contexts[repre_id] = context - - return contexts + return get_representation_contexts(project_name, repre_entities) def get_product_contexts(product_ids, project_name=None): @@ -208,51 +149,138 @@ def get_product_contexts(product_ids, project_name=None): return contexts -def get_representation_context(representation): +def get_representation_contexts(project_name, representation_entities): + """Parenthood context for representations. + + Function fills ``None`` if any entity was not found or could + not be queried. + + Args: + project_name (str): Project name. + representation_entities (Iterable[dict[str, Any]]): Representation + entities. + + Returns: + dict[str, dict[str, Any]]: The full representation context by + representation id. + + """ + repre_entities_by_id = { + repre_entity["id"]: repre_entity + for repre_entity in representation_entities + } + + if not repre_entities_by_id: + return {} + + repre_ids = set(repre_entities_by_id) + + parents_by_repre_id = ayon_api.get_representations_parents( + project_name, repre_ids + ) + output = {} + for repre_id in repre_ids: + repre_entity = repre_entities_by_id[repre_id] + ( + version_entity, + product_entity, + folder_entity, + project_entity + ) = parents_by_repre_id[repre_id] + output[repre_id] = { + "project": project_entity, + "folder": folder_entity, + "product": product_entity, + "version": version_entity, + "representation": repre_entity, + } + return output + + +def get_representation_contexts_by_ids(project_name, representation_ids): + """Parenthood context for representations found by ids. + + Function fills ``None`` if any entity was not found or could + not be queried. + + Args: + project_name (str): Project name. + representation_ids (Iterable[str]): Representation ids. + + Returns: + dict[str, dict[str, Any]]: The full representation context by + representation id. + + """ + repre_ids = set(representation_ids) + if not repre_ids: + return {} + + # Query representation entities by id + repre_entities_by_id = { + repre_entity["id"]: repre_entity + for repre_entity in ayon_api.get_representations( + project_name, repre_ids + ) + } + output = get_representation_contexts( + project_name, repre_entities_by_id.values() + ) + for repre_id in repre_ids: + if repre_id not in output: + output[repre_id] = { + "project": None, + "folder": None, + "product": None, + "version": None, + "representation": None, + } + return output + + +def get_representation_context(project_name, representation): """Return parenthood context for representation. Args: - representation (str or ObjectId or dict): The representation id - or full representation as returned by the database. + project_name (str): Project name. + representation (Union[dict[str, Any], str]): Representation entity + or representation id. Returns: - dict: The full representation context. + dict[str, dict[str, Any]]: The full representation context. + + Raises: + ValueError: When representation is invalid or parents were not found. + """ - from ayon_core.pipeline import get_current_project_name - - assert representation is not None, "This is a bug" - - project_name = get_current_project_name() - if not isinstance(representation, dict): - representation = get_representation_by_id( - project_name, representation + if not representation: + raise ValueError( + "Invalid argument value {}".format(str(representation)) ) - if not representation: - raise AssertionError("Representation was not found in database") + if isinstance(representation, dict): + repre_entity = representation + repre_id = repre_entity["id"] + context = get_representation_contexts( + project_name, [repre_entity] + )[repre_id] + else: + repre_id = representation + context = get_representation_contexts_by_ids( + project_name, {repre_id} + )[repre_id] - ( - version_entity, - product_entity, - folder_entity, - project_entity - ) = get_representation_parents(project_name, representation) - if not version_entity: - raise AssertionError("Version was not found in database") - if not product_entity: - raise AssertionError("Product was not found in database") - if not folder_entity: - raise AssertionError("Folder was not found in database") - if not project_entity: - raise AssertionError("Project was not found in database") + missing_entities = [] + for key, value in context.items(): + if value is None: + missing_entities.append(key) - context = { - "project": project_entity, - "folder": folder_entity, - "product": product_entity, - "version": version_entity, - "representation": representation, - } + if missing_entities: + raise ValueError( + "Not able to receive representation parent types: {}".format( + ", ".join(missing_entities) + ) + ) return context @@ -352,7 +380,7 @@ def load_container( Args: Loader (Loader): The loader class to trigger. - representation (str or ObjectId or dict): The representation id + representation (str 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 product name. @@ -366,8 +394,11 @@ def load_container( the representation. """ + from ayon_core.pipeline import get_current_project_name - context = get_representation_context(representation) + context = get_representation_context( + get_current_project_name(), representation + ) return load_with_repre_context( Loader, context, @@ -433,13 +464,13 @@ def update_container(container, version=-1): # Compute the different version from 'representation' project_name = get_current_project_name() - current_representation = get_representation_by_id( + current_representation = ayon_api.get_representation_by_id( project_name, container["representation"] ) assert current_representation is not None, "This is a bug" - current_version_id = current_representation["parent"] + current_version_id = current_representation["versionId"] current_version = ayon_api.get_version_by_id( project_name, current_version_id, fields={"productId"} ) @@ -468,7 +499,7 @@ def update_container(container, version=-1): ) repre_name = current_representation["name"] - new_representation = get_representation_by_name( + new_representation = ayon_api.get_representation_by_name( project_name, repre_name, new_version["id"] ) if new_representation is None: @@ -506,7 +537,7 @@ def switch_container(container, representation, loader_plugin=None): Args: container (dict): container information - representation (dict): representation data from document + representation (dict): representation entity Returns: function call @@ -533,21 +564,20 @@ def switch_container(container, representation, loader_plugin=None): # Get the new representation to switch to project_name = get_current_project_name() - new_representation = get_representation_by_id( - project_name, representation["_id"] - ) - new_context = get_representation_context(new_representation) - if not is_compatible_loader(loader_plugin, new_context): + context = get_representation_context( + project_name, representation["id"] + ) + if not is_compatible_loader(loader_plugin, context): raise IncompatibleLoaderError( "Loader {} is incompatible with {}".format( - loader_plugin.__name__, new_context["product"]["name"] + loader_plugin.__name__, context["product"]["name"] ) ) - loader = loader_plugin(new_context) + loader = loader_plugin(context) - return loader.switch(container, new_context) + return loader.switch(container, context) def get_representation_path_from_context(context): @@ -567,7 +597,7 @@ def get_representation_path_from_context(context): return get_representation_path(representation, root) -def get_representation_path_with_anatomy(repre_doc, anatomy): +def get_representation_path_with_anatomy(repre_entity, anatomy): """Receive representation path using representation document and anatomy. Anatomy is used to replace 'root' key in representation file. Ideally @@ -579,7 +609,7 @@ def get_representation_path_with_anatomy(repre_doc, anatomy): imagine the result should also contain paths to possible resources. Args: - repre_doc (Dict[str, Any]): Representation document. + repre_entity (Dict[str, Any]): Representation entity. anatomy (Anatomy): Project anatomy object. Returns: @@ -591,7 +621,7 @@ def get_representation_path_with_anatomy(repre_doc, anatomy): """ try: - template = repre_doc["data"]["template"] + template = repre_entity["attrib"]["template"] except KeyError: raise InvalidRepresentationContext(( @@ -600,7 +630,7 @@ def get_representation_path_with_anatomy(repre_doc, anatomy): )) try: - context = repre_doc["context"] + context = repre_entity["context"] context["root"] = anatomy.roots path = StringTemplate.format_strict_template(template, context) @@ -638,7 +668,7 @@ def get_representation_path(representation, root=None): def path_from_representation(): try: - template = representation["data"]["template"] + template = representation["attrib"]["template"] except KeyError: return None @@ -665,10 +695,10 @@ def get_representation_path(representation, root=None): return path def path_from_data(): - if "path" not in representation["data"]: + if "path" not in representation["attrib"]: return None - path = representation["data"]["path"] + path = representation["attrib"]["path"] # Force replacing backslashes with forward slashed if not on # windows if platform.system().lower() != "windows": @@ -745,8 +775,12 @@ def filter_repre_contexts_by_loader(repre_contexts, loader): def loaders_from_representation(loaders, representation): """Return all compatible loaders for a representation.""" + from ayon_core.pipeline import get_current_project_name - context = get_representation_context(representation) + project_name = get_current_project_name() + context = get_representation_context( + project_name, representation + ) return loaders_from_repre_context(loaders, context) @@ -825,26 +859,26 @@ def filter_containers(containers, project_name): invalid_containers.extend(containers) return output - repre_docs = get_representations( + repre_entities = ayon_api.get_representations( project_name, representation_ids=repre_ids, - fields=["_id", "parent"] + fields={"id", "versionId"} ) # Store representations by stringified representation id - repre_docs_by_str_id = {} - repre_docs_by_version_id = collections.defaultdict(list) - for repre_doc in repre_docs: - repre_id = str(repre_doc["_id"]) - version_id = repre_doc["parent"] - repre_docs_by_str_id[repre_id] = repre_doc - repre_docs_by_version_id[version_id].append(repre_doc) + repre_entities_by_id = {} + repre_entities_by_version_id = collections.defaultdict(list) + for repre_entity in repre_entities: + repre_id = repre_entity["id"] + version_id = repre_entity["versionId"] + repre_entities_by_id[repre_id] = repre_entity + repre_entities_by_version_id[version_id].append(repre_entity) # Query version docs to get it's product ids # - also query hero version to be able identify if representation # belongs to existing version version_entities = ayon_api.get_versions( project_name, - version_ids=repre_docs_by_version_id.keys(), + version_ids=repre_entities_by_version_id.keys(), hero=True, fields={"id", "productId", "version"} ) @@ -887,8 +921,8 @@ def filter_containers(containers, project_name): invalid_containers.append(container) continue - repre_doc = repre_docs_by_str_id.get(repre_id) - if not repre_doc: + repre_entity = repre_entities_by_id.get(repre_id) + if not repre_entity: log.debug(( "Container '{}' has an invalid representation." " It is missing in the database." @@ -896,7 +930,7 @@ def filter_containers(containers, project_name): not_found_containers.append(container) continue - version_id = repre_doc["parent"] + version_id = repre_entity["versionId"] if version_id in outdated_version_ids: outdated_containers.append(container) From 1313b61fe0698c0580310cf6b723f80072b9e227 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 12:00:36 +0100 Subject: [PATCH 469/573] modified rest of pipeline codebase to expect representation entity --- client/ayon_core/pipeline/context_tools.py | 4 ++- client/ayon_core/pipeline/delivery.py | 4 +-- .../pipeline/farm/pyblish_functions.py | 12 ++++---- .../pipeline/workfile/build_workfile.py | 22 ++++++++------ .../workfile/workfile_template_builder.py | 30 +++++++++---------- 5 files changed, 39 insertions(+), 33 deletions(-) diff --git a/client/ayon_core/pipeline/context_tools.py b/client/ayon_core/pipeline/context_tools.py index 5c41996aab..b314e02998 100644 --- a/client/ayon_core/pipeline/context_tools.py +++ b/client/ayon_core/pipeline/context_tools.py @@ -457,7 +457,9 @@ def is_representation_from_latest(representation): """ project_name = get_current_project_name() - return ayon_api.version_is_latest(project_name, representation["parent"]) + return ayon_api.version_is_latest( + project_name, representation["versionId"] + ) def get_template_data_from_session(session=None, settings=None): diff --git a/client/ayon_core/pipeline/delivery.py b/client/ayon_core/pipeline/delivery.py index cb90e67090..d2b78422e3 100644 --- a/client/ayon_core/pipeline/delivery.py +++ b/client/ayon_core/pipeline/delivery.py @@ -143,7 +143,7 @@ def deliver_single_file( src_path = os.path.normpath(src_path.replace("\\", "/")) if not os.path.exists(src_path): - msg = "{} doesn't exist for {}".format(src_path, repre["_id"]) + msg = "{} doesn't exist for {}".format(src_path, repre["id"]) report_items["Source file was not found"].append(msg) return report_items, 0 @@ -216,7 +216,7 @@ def deliver_sequence( if not hash_path_exist(src_path): msg = "{} doesn't exist for {}".format( - src_path, repre["_id"]) + src_path, repre["id"]) report_items["Source file was not found"].append(msg) return report_items, 0 diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 23db1e37be..dadf2cbe1a 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -14,7 +14,6 @@ from ayon_core.pipeline import ( get_representation_path, Anatomy, ) -from ayon_core.client import get_representations from ayon_core.lib import Logger from ayon_core.pipeline.publish import KnownPublishError from ayon_core.pipeline.farm.patterning import match_aov_pattern @@ -729,13 +728,14 @@ def get_resources(project_name, version_entity, extension=None): # there is a `context_filter` argument that won't probably work in # final release of AYON. SO we'll rather not use it - repre_docs = list(get_representations( - project_name, version_ids=[version_entity["id"]])) + repre_entities = list(ayon_api.get_representations( + project_name, version_ids={version_entity["id"]} + )) filtered = [] - for doc in repre_docs: - if doc["context"]["ext"] in extensions: - filtered.append(doc) + for repre_entity in repre_entities: + if repre_entity["context"]["ext"] in extensions: + filtered.append(repre_entity) representation = filtered[0] directory = get_representation_path(representation) diff --git a/client/ayon_core/pipeline/workfile/build_workfile.py b/client/ayon_core/pipeline/workfile/build_workfile.py index 766b7baf04..6d7ea2c7ad 100644 --- a/client/ayon_core/pipeline/workfile/build_workfile.py +++ b/client/ayon_core/pipeline/workfile/build_workfile.py @@ -15,7 +15,6 @@ import json import ayon_api -from ayon_core.client import get_representations from ayon_core.settings import get_project_settings from ayon_core.lib import ( filter_profiles, @@ -172,7 +171,7 @@ class BuildWorkfile: if current_context_profiles: # Add current folder entity if preset has current context set folder_entities.append(current_folder_entity) - current_folder_id = current_folder_entity["_id"] + current_folder_id = current_folder_entity["id"] if link_context_profiles: # Find and append linked folders if preset has set linked mapping @@ -617,7 +616,7 @@ class BuildWorkfile: try: container = load_container( loader, - repre["_id"], + repre["id"], name=product_name ) loaded_containers.append(container) @@ -710,12 +709,12 @@ class BuildWorkfile: version_entity["id"]: version_entity for version_entity in last_version_by_product_id.values() } - repre_docs = get_representations( + repre_entities = ayon_api.get_representations( project_name, version_ids=last_version_entities_by_id.keys() ) - for repre_doc in repre_docs: - version_id = repre_doc["parent"] + for repre_entity in repre_entities: + version_id = repre_entity["versionId"] version_entity = last_version_entities_by_id[version_id] product_id = version_entity["productId"] @@ -739,8 +738,13 @@ class BuildWorkfile: } } - output[folder_id]["products"][product_id]["version"]["repres"].append( - repre_doc - ) + ( + output + [folder_id] + ["products"] + [product_id] + ["version"] + ["repres"] + ).append(repre_entity) return output diff --git a/client/ayon_core/pipeline/workfile/workfile_template_builder.py b/client/ayon_core/pipeline/workfile/workfile_template_builder.py index 5e588862a9..e8b5268a6d 100644 --- a/client/ayon_core/pipeline/workfile/workfile_template_builder.py +++ b/client/ayon_core/pipeline/workfile/workfile_template_builder.py @@ -25,9 +25,9 @@ from ayon_api import ( get_task_by_name, get_products, get_last_versions, + get_representations, ) -from ayon_core.client import get_representations from ayon_core.settings import get_project_settings from ayon_core.host import IWorkfileHost, HostBase from ayon_core.lib import ( @@ -40,7 +40,7 @@ from ayon_core.lib.attribute_definitions import get_attributes_keys from ayon_core.pipeline import Anatomy from ayon_core.pipeline.load import ( get_loaders_by_name, - get_contexts_for_repre_docs, + get_representation_contexts, load_with_repre_context, ) @@ -1442,7 +1442,7 @@ class PlaceholderLoadMixin(object): Note: This returns all representation documents from all versions of matching product. To filter for last version use - '_reduce_last_version_repre_docs'. + '_reduce_last_version_repre_entities'. Args: placeholder (PlaceholderItem): Item which should be populated. @@ -1473,7 +1473,7 @@ class PlaceholderLoadMixin(object): builder_type = placeholder.data["builder_type"] folder_ids = [] if builder_type == "context_folder": - folder_ids = [current_folder_entity["_id"]] + folder_ids = [current_folder_entity["id"]] elif builder_type == "all_folders": folder_ids = { @@ -1497,8 +1497,8 @@ class PlaceholderLoadMixin(object): filtered_product_ids = set() for product in products: if ( - product_name_regex is None - or product_name_regex.match(product["name"]) + product_name_regex is None + or product_name_regex.match(product["name"]) ): filtered_product_ids.add(product["id"]) @@ -1513,7 +1513,7 @@ class PlaceholderLoadMixin(object): ) return list(get_representations( project_name, - representation_names=[representation_name], + representation_names={representation_name}, version_ids=version_ids )) @@ -1529,15 +1529,15 @@ class PlaceholderLoadMixin(object): pass - def _reduce_last_version_repre_docs(self, representations): + def _reduce_last_version_repre_entities(self, representations): """Reduce representations to last verison.""" mapping = {} # TODO use representation context with entities - # - using 'asset', 'subset' and 'version' from context on + # - using 'folder', 'subset' and 'version' from context on # representation is danger - for repre_doc in representations: - repre_context = repre_doc["context"] + for repre_entity in representations: + repre_context = repre_entity["context"] folder_name = repre_context["asset"] product_name = repre_context["subset"] @@ -1551,7 +1551,7 @@ class PlaceholderLoadMixin(object): product_mapping[product_name] = collections.defaultdict(list) version_mapping = product_mapping[product_name] - version_mapping[version].append(repre_doc) + version_mapping[version].append(repre_entity) output = [] for product_mapping in mapping.values(): @@ -1588,10 +1588,10 @@ class PlaceholderLoadMixin(object): placeholder_representations = self._get_representations(placeholder) filtered_representations = [] - for representation in self._reduce_last_version_repre_docs( + for representation in self._reduce_last_version_repre_entities( placeholder_representations ): - repre_id = str(representation["_id"]) + repre_id = representation["id"] if repre_id not in ignore_repre_ids: filtered_representations.append(representation) @@ -1601,7 +1601,7 @@ class PlaceholderLoadMixin(object): ).format(placeholder.scene_identifier)) return - repre_load_contexts = get_contexts_for_repre_docs( + repre_load_contexts = get_representation_contexts( self.project_name, filtered_representations ) loaders_by_name = self.builder.get_loaders_by_name() From 70306a1229c54cffce0a2513fbd784a47d1bce81 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 12:28:24 +0100 Subject: [PATCH 470/573] use representation entity in global plugins --- .../plugins/publish/collect_audio.py | 25 +++++++++---------- .../plugins/publish/collect_frames_fix.py | 13 +++++++--- ...llect_input_representations_to_versions.py | 13 +++++----- .../publish/collect_scene_loaded_versions.py | 18 ++++++------- .../plugins/publish/integrate_thumbnail.py | 2 +- 5 files changed, 37 insertions(+), 34 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_audio.py b/client/ayon_core/plugins/publish/collect_audio.py index f6946b1950..05d9149c32 100644 --- a/client/ayon_core/plugins/publish/collect_audio.py +++ b/client/ayon_core/plugins/publish/collect_audio.py @@ -3,7 +3,6 @@ import collections import ayon_api import pyblish.api -from ayon_core.client import get_representations from ayon_core.pipeline.load import get_representation_path_with_anatomy @@ -79,17 +78,17 @@ class CollectAudio(pyblish.api.ContextPlugin): # Query all required documents project_name = context.data["projectName"] anatomy = context.data["anatomy"] - repre_docs_by_folder_paths = self.query_representations( + repre_entities_by_folder_paths = self.query_representations( project_name, folder_paths) for folder_path, instances in instances_by_folder_path.items(): - repre_docs = repre_docs_by_folder_paths[folder_path] - if not repre_docs: + repre_entities = repre_entities_by_folder_paths[folder_path] + if not repre_entities: continue - repre_doc = repre_docs[0] + repre_entity = repre_entities[0] repre_path = get_representation_path_with_anatomy( - repre_doc, anatomy + repre_entity, anatomy ) for instance in instances: instance.data["audio"] = [{ @@ -159,20 +158,20 @@ class CollectAudio(pyblish.api.ContextPlugin): return output # Find representations under latest versions of audio products - repre_docs = get_representations( + repre_entities = ayon_api.get_representations( project_name, version_ids=version_ids ) - repre_docs_by_version_id = collections.defaultdict(list) - for repre_doc in repre_docs: - version_id = repre_doc["parent"] - repre_docs_by_version_id[version_id].append(repre_doc) + repre_entities_by_version_id = collections.defaultdict(list) + for repre_entity in repre_entities: + version_id = repre_entity["versionId"] + repre_entities_by_version_id[version_id].append(repre_entity) - if not repre_docs_by_version_id: + if not repre_entities_by_version_id: return output for folder_path in folder_paths: folder_id = folder_id_by_path.get(folder_path) product_id = product_id_by_folder_id.get(folder_id) version_id = version_id_by_product_id.get(product_id) - output[folder_path] = repre_docs_by_version_id[version_id] + output[folder_path] = repre_entities_by_version_id[version_id] return output diff --git a/client/ayon_core/plugins/publish/collect_frames_fix.py b/client/ayon_core/plugins/publish/collect_frames_fix.py index 7f6c003f5e..0f7d5b692a 100644 --- a/client/ayon_core/plugins/publish/collect_frames_fix.py +++ b/client/ayon_core/plugins/publish/collect_frames_fix.py @@ -6,7 +6,6 @@ from ayon_core.lib.attribute_definitions import ( BoolDef ) from ayon_core.pipeline.publish import AYONPyblishPluginMixin -from ayon_core.client.entities import get_representations class CollectFramesFixDef( @@ -55,12 +54,18 @@ class CollectFramesFixDef( ) return - representations = get_representations( - project_name, version_ids=[version_entity["id"]] + representations = ayon_api.get_representations( + project_name, version_ids={version_entity["id"]} ) published_files = [] for repre in representations: - if repre["context"]["family"] not in self.families: + # TODO get product type from product entity instead of + # representation 'context' data. + repre_context = repre["context"] + product_type = repre_context.get("product", {}).get("type") + if not product_type: + product_type = repre_context.get("family") + if product_type not in self.families: continue for file_info in repre.get("files"): diff --git a/client/ayon_core/plugins/publish/collect_input_representations_to_versions.py b/client/ayon_core/plugins/publish/collect_input_representations_to_versions.py index 6caee1be6a..770f3470c6 100644 --- a/client/ayon_core/plugins/publish/collect_input_representations_to_versions.py +++ b/client/ayon_core/plugins/publish/collect_input_representations_to_versions.py @@ -1,7 +1,6 @@ +import ayon_api import pyblish.api -from ayon_core.client import get_representations - class CollectInputRepresentationsToVersions(pyblish.api.ContextPlugin): """Converts collected input representations to input versions. @@ -24,14 +23,14 @@ class CollectInputRepresentationsToVersions(pyblish.api.ContextPlugin): if inst_repre: representations.update(inst_repre) - representations_docs = get_representations( - project_name=context.data["projectEntity"]["name"], + repre_entities = ayon_api.get_representations( + project_name=context.data["projectName"], representation_ids=representations, - fields=["_id", "parent"]) + fields={"id", "versionId"}) representation_id_to_version_id = { - str(repre["_id"]): repre["parent"] - for repre in representations_docs + repre["id"]: repre["versionId"] + for repre in repre_entities } for instance in context: diff --git a/client/ayon_core/plugins/publish/collect_scene_loaded_versions.py b/client/ayon_core/plugins/publish/collect_scene_loaded_versions.py index c1326f164d..7cbdd9c4ba 100644 --- a/client/ayon_core/plugins/publish/collect_scene_loaded_versions.py +++ b/client/ayon_core/plugins/publish/collect_scene_loaded_versions.py @@ -1,6 +1,6 @@ +import ayon_api import pyblish.api -from ayon_core.client import get_representations from ayon_core.pipeline import registered_host @@ -42,21 +42,21 @@ class CollectSceneLoadedVersions(pyblish.api.ContextPlugin): } project_name = context.data["projectName"] - repre_docs = get_representations( + repre_entities = ayon_api.get_representations( project_name, representation_ids=repre_ids, - fields=["_id", "parent"] + fields={"id", "versionId"} ) - repre_doc_by_str_id = { - str(doc["_id"]): doc - for doc in repre_docs + repre_entities_by_id = { + repre_entity["id"]: repre_entity + for repre_entity in repre_entities } # QUESTION should we add same representation id when loaded multiple # times? for con in containers: repre_id = con["representation"] - repre_doc = repre_doc_by_str_id.get(repre_id) + repre_entity = repre_entities_by_id.get(repre_id) if repre_doc is None: self.log.warning(( "Skipping container," @@ -68,8 +68,8 @@ class CollectSceneLoadedVersions(pyblish.api.ContextPlugin): # may have more then one representation that are same version version = { "container_name": con["name"], - "representation_id": repre_doc["_id"], - "version_id": repre_doc["parent"], + "representation_id": repre_entity["id"], + "version_id": repre_entity["versionId"], } loaded_versions.append(version) diff --git a/client/ayon_core/plugins/publish/integrate_thumbnail.py b/client/ayon_core/plugins/publish/integrate_thumbnail.py index 6d470feb5f..2770dd04cf 100644 --- a/client/ayon_core/plugins/publish/integrate_thumbnail.py +++ b/client/ayon_core/plugins/publish/integrate_thumbnail.py @@ -132,7 +132,7 @@ class IntegrateThumbnailsAYON(pyblish.api.ContextPlugin): def _get_version_id(self, published_representations): for repre_info in published_representations.values(): - return repre_info["representation"]["parent"] + return repre_info["representation"]["versionId"] def _get_instance_thumbnail_path(self, published_representations): thumb_repre_doc = None From c4f74a6aedc12fe7d74adedbb6403eb2dca51eda Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 12:28:37 +0100 Subject: [PATCH 471/573] minor fixes in global plugins --- .../ayon_core/plugins/publish/collect_anatomy_instance_data.py | 2 +- client/ayon_core/plugins/publish/integrate_inputlinks.py | 2 +- client/ayon_core/plugins/publish/integrate_version_attrs.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py index 48f674ae0f..80ea42dd6b 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py @@ -379,7 +379,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): def _fill_folder_data(self, instance, project_entity, anatomy_data): # QUESTION should we make sure that all folder data are poped if # folder data cannot be found? - # - 'asset', 'hierarchy', 'parent', 'folder' + # - 'folder', 'hierarchy', 'parent', 'folder' folder_entity = instance.data.get("folderEntity") if folder_entity: folder_name = folder_entity["name"] diff --git a/client/ayon_core/plugins/publish/integrate_inputlinks.py b/client/ayon_core/plugins/publish/integrate_inputlinks.py index 1593e6ac7e..16aef09a39 100644 --- a/client/ayon_core/plugins/publish/integrate_inputlinks.py +++ b/client/ayon_core/plugins/publish/integrate_inputlinks.py @@ -122,7 +122,7 @@ class IntegrateInputLinksAYON(pyblish.api.ContextPlugin): new_links_by_type, "generative", input_version, - version_entity["_id"], + version_entity["id"], ) def _get_existing_links(self, project_name, link_type, entity_ids): diff --git a/client/ayon_core/plugins/publish/integrate_version_attrs.py b/client/ayon_core/plugins/publish/integrate_version_attrs.py index a5d6a17396..0380d33137 100644 --- a/client/ayon_core/plugins/publish/integrate_version_attrs.py +++ b/client/ayon_core/plugins/publish/integrate_version_attrs.py @@ -56,7 +56,7 @@ class IntegrateVersionAttributes(pyblish.api.ContextPlugin): continue self.log.debug("Updating attributes on version {} to {}".format( - version_entity["_id"], str(filtered_attributes) + version_entity["id"], str(filtered_attributes) )) op_session.update_entity( project_name, From 6045fc5baad8c2f369cae5900a67ab5abdfc8bf4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 12:34:05 +0100 Subject: [PATCH 472/573] use representation entity in global load and inventory actions --- .../plugins/inventory/remove_and_load.py | 26 ++++++++++++------- .../plugins/load/delete_old_versions.py | 9 ++++--- client/ayon_core/plugins/load/delivery.py | 10 +++---- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/client/ayon_core/plugins/inventory/remove_and_load.py b/client/ayon_core/plugins/inventory/remove_and_load.py index 5529090b42..6553f9a7b3 100644 --- a/client/ayon_core/plugins/inventory/remove_and_load.py +++ b/client/ayon_core/plugins/inventory/remove_and_load.py @@ -1,12 +1,12 @@ -from ayon_core.pipeline import InventoryAction -from ayon_core.pipeline import get_current_project_name +import ayon_api + +from ayon_core.pipeline import get_current_project_name, InventoryAction from ayon_core.pipeline.load.plugins import discover_loader_plugins from ayon_core.pipeline.load.utils import ( get_loader_identifier, remove_container, load_container, ) -from ayon_core.client import get_representation_by_id class RemoveAndLoad(InventoryAction): @@ -21,6 +21,7 @@ class RemoveAndLoad(InventoryAction): get_loader_identifier(plugin): plugin for plugin in discover_loader_plugins(project_name=project_name) } + repre_ids = set() for container in containers: # Get loader loader_name = container["loader"] @@ -30,16 +31,23 @@ class RemoveAndLoad(InventoryAction): "Failed to get loader '{}', can't remove " "and load container".format(loader_name) ) + repre_ids.add(container["representation"]) - # Get representation - representation = get_representation_by_id( - project_name, container["representation"] + repre_entities_by_id = { + repre_entity["id"]: repre_entity + for repre_entity in ayon_api.get_representations( + project_name, representation_ids=repre_ids ) - if not representation: + } + for container in containers: + # Get representation + repre_id = container["representation"] + repre_entity = repre_entities_by_id.get(repre_id) + if not repre_entity: self.log.warning( "Skipping remove and load because representation id is not" " found in database: '{}'".format( - container["representation"] + repre_id ) ) continue @@ -48,4 +56,4 @@ class RemoveAndLoad(InventoryAction): remove_container(container) # Load container - load_container(loader, representation) + load_container(loader, repre_entity) diff --git a/client/ayon_core/plugins/load/delete_old_versions.py b/client/ayon_core/plugins/load/delete_old_versions.py index 6b9fe3ba54..8fa0c2edb6 100644 --- a/client/ayon_core/plugins/load/delete_old_versions.py +++ b/client/ayon_core/plugins/load/delete_old_versions.py @@ -11,7 +11,6 @@ # from qtpy import QtWidgets, QtCore # # from ayon_core import style -# from ayon_core.client import get_representations # from ayon_core.addon import AddonsManager # from ayon_core.lib import format_file_size # from ayon_core.pipeline import load, Anatomy @@ -265,7 +264,7 @@ # print(msg) # return # -# repres = list(get_representations( +# repres = list(ayon_api.get_representations( # project_name, version_ids=version_ids # )) # @@ -276,7 +275,9 @@ # dir_paths = {} # file_paths_by_dir = collections.defaultdict(list) # for repre in repres: -# file_path, seq_path = self.path_from_representation(repre, anatomy) +# file_path, seq_path = self.path_from_representation( +# repre, anatomy +# ) # if file_path is None: # self.log.debug(( # "Could not format path for represenation \"{}\"" @@ -347,7 +348,7 @@ # if version_tags == orig_version_tags: # continue # -# update_query = {"_id": version["id"]} +# update_query = {"id": version["id"]} # update_data = {"$set": {"data.tags": version_tags}} # mongo_changes_bulk.append(UpdateOne(update_query, update_data)) # diff --git a/client/ayon_core/plugins/load/delivery.py b/client/ayon_core/plugins/load/delivery.py index 1de822bb6c..453bdfb87a 100644 --- a/client/ayon_core/plugins/load/delivery.py +++ b/client/ayon_core/plugins/load/delivery.py @@ -2,9 +2,9 @@ import copy import platform from collections import defaultdict +import ayon_api from qtpy import QtWidgets, QtCore, QtGui -from ayon_core.client import get_representations from ayon_core.pipeline import load, Anatomy from ayon_core import resources, style @@ -202,7 +202,7 @@ class DeliveryOptionsDialog(QtWidgets.QDialog): ) anatomy_data = copy.deepcopy(repre["context"]) - new_report_items = check_destination_path(str(repre["_id"]), + new_report_items = check_destination_path(repre["id"], self.anatomy, anatomy_data, datetime_data, @@ -260,7 +260,7 @@ class DeliveryOptionsDialog(QtWidgets.QDialog): report_items.update(new_report_items) self._update_progress(uploaded) else: # fallback for Pype2 and representations without files - frame = repre['context'].get('frame') + frame = repre["context"].get("frame") if frame: repre["context"]["frame"] = len(str(frame)) * "#" @@ -290,9 +290,9 @@ class DeliveryOptionsDialog(QtWidgets.QDialog): return templates def _set_representations(self, project_name, contexts): - version_ids = [context["version"]["_id"] for context in contexts] + version_ids = {context["version"]["id"] for context in contexts} - repres = list(get_representations( + repres = list(ayon_api.get_representations( project_name, version_ids=version_ids )) From 3a5becbc98747e05f270349e9def376f4b5be63f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 12:36:12 +0100 Subject: [PATCH 473/573] modified loader tools to use representation entitiy --- .../ayon_core/tools/loader/models/actions.py | 38 +++--- .../tools/loader/models/site_sync.py | 122 +++++++++++++++--- 2 files changed, 123 insertions(+), 37 deletions(-) diff --git a/client/ayon_core/tools/loader/models/actions.py b/client/ayon_core/tools/loader/models/actions.py index 0a1c5b6080..d8ae30b49f 100644 --- a/client/ayon_core/tools/loader/models/actions.py +++ b/client/ayon_core/tools/loader/models/actions.py @@ -1,13 +1,11 @@ import sys import traceback import inspect -import copy import collections import uuid import ayon_api -from ayon_core.client import get_representations from ayon_core.pipeline.load import ( discover_loader_plugins, ProductLoaderPlugin, @@ -427,22 +425,22 @@ class LoaderActionsModel: "version": version_entity, } - repre_docs = get_representations( + repre_entities = ayon_api.get_representations( project_name, version_ids=version_ids) - for repre_doc in repre_docs: - version_id = repre_doc["parent"] + for repre_entity in repre_entities: + version_id = repre_entity["versionId"] version_entity = version_entities_by_id[version_id] product_id = version_entity["productId"] product_entity = product_entities_by_id[product_id] folder_id = product_entity["folderId"] folder_entity = folder_entities_by_id[folder_id] - repre_context_by_id[repre_doc["_id"]] = { + repre_context_by_id[repre_entity["id"]] = { "project": project_entity, "folder": folder_entity, "product": product_entity, "version": version_entity, - "representation": repre_doc, + "representation": repre_entity, } return version_context_by_id, repre_context_by_id @@ -471,10 +469,10 @@ class LoaderActionsModel: if not project_name and not repre_ids: return product_context_by_id, repre_context_by_id - repre_docs = list(get_representations( + repre_entities = list(ayon_api.get_representations( project_name, representation_ids=repre_ids )) - version_ids = {r["parent"] for r in repre_docs} + version_ids = {r["versionId"] for r in repre_entities} version_entities = ayon_api.get_versions( project_name, version_ids=version_ids ) @@ -509,20 +507,20 @@ class LoaderActionsModel: "product": product_entity, } - for repre_doc in repre_docs: - version_id = repre_doc["parent"] + for repre_entity in repre_entities: + version_id = repre_entity["versionId"] version_entity = version_entities_by_id[version_id] product_id = version_entity["productId"] product_entity = product_entities_by_id[product_id] folder_id = product_entity["folderId"] folder_entity = folder_entities_by_id[folder_id] - repre_context_by_id[repre_doc["_id"]] = { + repre_context_by_id[repre_entity["id"]] = { "project": project_entity, "folder": folder_entity, "product": product_entity, "version": version_entity, - "representation": repre_doc, + "representation": repre_entity, } return product_context_by_id, repre_context_by_id @@ -567,9 +565,9 @@ class LoaderActionsModel: repre_product_ids = set() repre_folder_ids = set() for repre_context in filtered_repre_contexts: - repre_ids.add(repre_context["representation"]["_id"]) + repre_ids.add(repre_context["representation"]["id"]) repre_product_ids.add(repre_context["product"]["id"]) - repre_version_ids.add(repre_context["version"]["_id"]) + repre_version_ids.add(repre_context["version"]["id"]) repre_folder_ids.add(repre_context["folder"]["id"]) item = self._create_loader_action_item( @@ -686,10 +684,10 @@ class LoaderActionsModel: """ project_entity = ayon_api.get_project(project_name) - repre_docs = list(get_representations( + repre_entities = list(ayon_api.get_representations( project_name, representation_ids=representation_ids )) - version_ids = {r["parent"] for r in repre_docs} + version_ids = {r["versionId"] for r in repre_entities} version_entities = ayon_api.get_versions( project_name, version_ids=version_ids ) @@ -707,8 +705,8 @@ class LoaderActionsModel: ) folder_entities_by_id = {f["id"]: f for f in folder_entities} repre_contexts = [] - for repre_doc in repre_docs: - version_id = repre_doc["parent"] + for repre_entity in repre_entities: + version_id = repre_entity["versionId"] version_entity = version_entities_by_id[version_id] product_id = version_entity["productId"] product_entity = product_entities_by_id[product_id] @@ -719,7 +717,7 @@ class LoaderActionsModel: "folder": folder_entity, "product": product_entity, "version": version_entity, - "representation": repre_doc, + "representation": repre_entity, }) return self._load_representations_by_loader( diff --git a/client/ayon_core/tools/loader/models/site_sync.py b/client/ayon_core/tools/loader/models/site_sync.py index ff30a3e6fa..daa9f7ba50 100644 --- a/client/ayon_core/tools/loader/models/site_sync.py +++ b/client/ayon_core/tools/loader/models/site_sync.py @@ -1,10 +1,8 @@ import collections +from ayon_api import get_representations, get_versions_links + from ayon_core.lib import Logger -from ayon_core.client import ( - get_representations, - get_linked_representation_id, -) from ayon_core.addon import AddonsManager from ayon_core.tools.ayon_utils.models import NestedCacheItem from ayon_core.tools.loader.abstract import ActionItem @@ -324,24 +322,34 @@ class SiteSyncModel: active_site = self.get_active_site(project_name) remote_site = self.get_remote_site(project_name) - repre_docs = list(get_representations( - project_name, representation_ids=representation_ids - )) - product_type_by_repre_id = { - item["_id"]: item["context"]["family"] - for item in repre_docs + repre_entities_by_id = { + repre_entity["id"]: repre_entity + for repre_entity in get_representations( + project_name, representation_ids=representation_ids + ) } + # TODO get product type from product entity instead of 'context' + # on representation + product_type_by_repre_id = {} + for repre_id, repre_entity in repre_entities_by_id.items(): + repre_context = repre_entity["context"] + product_type = repre_context.get("product", {}).get("type") + if not product_type: + product_type = repre_context.get("family") + + product_type_by_repre_id[repre_id] = product_type for repre_id in representation_ids: + repre_entity = repre_entities_by_id.get(repre_id) product_type = product_type_by_repre_id[repre_id] if identifier == DOWNLOAD_IDENTIFIER: self._add_site( - project_name, repre_id, active_site, product_type + project_name, repre_entity, active_site, product_type ) elif identifier == UPLOAD_IDENTIFIER: self._add_site( - project_name, repre_id, remote_site, product_type + project_name, repre_entity, remote_site, product_type ) elif identifier == REMOVE_IDENTIFIER: @@ -487,19 +495,19 @@ class SiteSyncModel: representation_ids=representation_ids, ) - def _add_site(self, project_name, repre_id, site_name, product_type): + def _add_site(self, project_name, repre_entity, site_name, product_type): self._site_sync_addon.add_site( - project_name, repre_id, site_name, force=True + project_name, repre_entity["id"], site_name, force=True ) # TODO this should happen in site sync addon if product_type != "workfile": return - links = get_linked_representation_id( + links = self._get_linked_representation_id( project_name, - repre_id=repre_id, - link_type="reference" + repre_entity, + "reference" ) for link_repre_id in links: try: @@ -514,3 +522,83 @@ class SiteSyncModel: except Exception: # do not add/reset working site for references log.debug("Site present", exc_info=True) + + def _get_linked_representation_id( + self, + project_name, + repre_entity, + link_type, + max_depth=None + ): + """Returns list of linked ids of particular type (if provided). + + One of representation document or representation id must be passed. + Note: + Representation links now works only from representation through + version back to representations. + + Todos: + Missing depth query. Not sure how it did find more representations + in depth, probably links to version? + This function should probably live in sitesync addon? + + Args: + project_name (str): Name of project where look for links. + repre_entity (dict[str, Any]): Representation entity. + link_type (str): Type of link (e.g. 'reference', ...). + max_depth (int): Limit recursion level. Default: 0 + + Returns: + List[ObjectId] Linked representation ids. + """ + + if not repre_entity: + return [] + + version_id = repre_entity["versionId"] + if max_depth is None or max_depth == 0: + max_depth = 1 + + link_types = None + if link_type: + link_types = [link_type] + + # Store already found version ids to avoid recursion, and also to store + # output -> Don't forget to remove 'version_id' at the end!!! + linked_version_ids = {version_id} + # Each loop of depth will reset this variable + versions_to_check = {version_id} + for _ in range(max_depth): + if not versions_to_check: + break + + versions_links = get_versions_links( + project_name, + versions_to_check, + link_types=link_types, + link_direction="out") + + versions_to_check = set() + for links in versions_links.values(): + for link in links: + # Care only about version links + if link["entityType"] != "version": + continue + entity_id = link["entityId"] + # Skip already found linked version ids + if entity_id in linked_version_ids: + continue + linked_version_ids.add(entity_id) + versions_to_check.add(entity_id) + + linked_version_ids.remove(version_id) + if not linked_version_ids: + return [] + representations = get_representations( + project_name, + version_ids=linked_version_ids, + fields=["id"]) + return [ + repre["id"] + for repre in representations + ] From 3467727f110b4ca3396fcea196b4e7dda20a0ff6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 12:36:25 +0100 Subject: [PATCH 474/573] scene inventory is using representation entity --- .../tools/sceneinventory/delegates.py | 78 +- .../ayon_core/tools/sceneinventory/model.py | 134 ++-- .../tools/sceneinventory/models/site_sync.py | 13 +- .../sceneinventory/switch_dialog/dialog.py | 704 +++++++++--------- client/ayon_core/tools/sceneinventory/view.py | 206 ++--- 5 files changed, 533 insertions(+), 602 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/delegates.py b/client/ayon_core/tools/sceneinventory/delegates.py index 1f8bb81835..2126fa1cbe 100644 --- a/client/ayon_core/tools/sceneinventory/delegates.py +++ b/client/ayon_core/tools/sceneinventory/delegates.py @@ -1,9 +1,7 @@ import numbers -from ayon_core.client import ( - get_versions, - get_hero_versions, -) +import ayon_api + from ayon_core.pipeline import HeroVersionType from ayon_core.tools.utils.models import TreeModel from ayon_core.tools.utils.lib import format_version @@ -27,7 +25,7 @@ class VersionDelegate(QtWidgets.QStyledItemDelegate): def displayText(self, value, locale): if isinstance(value, HeroVersionType): - return format_version(value, True) + return format_version(value) if not isinstance(value, numbers.Integral): # For cases where no version is resolved like NOT FOUND cases # where a representation might not exist in current database @@ -113,71 +111,35 @@ class VersionDelegate(QtWidgets.QStyledItemDelegate): # Current value of the index item = index.data(TreeModel.ItemRole) value = index.data(QtCore.Qt.DisplayRole) - if item["version_document"]["type"] != "hero_version": - assert isinstance(value, numbers.Integral), ( - "Version is not integer" - ) project_name = self.get_project_name() # Add all available versions to the editor - parent_id = item["version_document"]["parent"] - version_docs = [ - version_doc - for version_doc in sorted( - get_versions(project_name, subset_ids=[parent_id]), - key=lambda item: item["name"] - ) - if version_doc["data"].get("active", True) - ] - - hero_versions = list( - get_hero_versions( - project_name, - subset_ids=[parent_id], - fields=["name", "data.tags", "version_id"] - ) - ) - hero_version_doc = None - if hero_versions: - hero_version_doc = hero_versions[0] - - doc_for_hero_version = None + product_id = item["version_entity"]["productId"] + version_entities = list(sorted( + ayon_api.get_versions( + project_name, product_ids={product_id}, active=True + ), + key=lambda item: abs(item["version"]) + )) selected = None items = [] - for version_doc in version_docs: - version_tags = version_doc["data"].get("tags") or [] - if "deleted" in version_tags: - continue + is_hero_version = value < 0 + for version_entity in version_entities: + version = version_entity["version"] + label = format_version(version) + item = QtGui.QStandardItem(label) + item.setData(version_entity, QtCore.Qt.UserRole) + items.append(item) if ( - hero_version_doc - and doc_for_hero_version is None - and hero_version_doc["version_id"] == version_doc["_id"] + version == value + or is_hero_version and version < 0 ): - doc_for_hero_version = version_doc - - label = format_version(version_doc["name"]) - item = QtGui.QStandardItem(label) - item.setData(version_doc, QtCore.Qt.UserRole) - items.append(item) - - if version_doc["name"] == value: selected = item - if hero_version_doc and doc_for_hero_version: - version_name = doc_for_hero_version["name"] - label = format_version(version_name, True) - if isinstance(value, HeroVersionType): - index = len(version_docs) - hero_version_doc["name"] = HeroVersionType(version_name) - - item = QtGui.QStandardItem(label) - item.setData(hero_version_doc, QtCore.Qt.UserRole) - items.append(item) - # Reverse items so latest versions be upper - items = list(reversed(items)) + items.reverse() for item in items: editor.model().appendRow(item) diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index 18fc56db0b..e53b6aa4c3 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -1,24 +1,15 @@ -import collections import re import logging import uuid -import copy from collections import defaultdict +import ayon_api from qtpy import QtCore, QtGui import qtawesome -from ayon_core.client import ( - get_assets, - get_subsets, - get_versions, - get_last_version_by_subset_id, - get_representations, -) from ayon_core.pipeline import ( get_current_project_name, - schema, HeroVersionType, ) from ayon_core.style import get_default_entity_icon_color @@ -249,29 +240,29 @@ class InventoryModel(TreeModel): not_found_ids.append(repre_id) continue - version = versions_by_id.get(representation["parent"]) - if not version: + version_entity = versions_by_id.get(representation["versionId"]) + if not version_entity: not_found["version"].extend(group_containers) not_found_ids.append(repre_id) continue - product = products_by_id.get(version["parent"]) - if not product: + product_entity = products_by_id.get(version_entity["productId"]) + if not product_entity: not_found["product"].extend(group_containers) not_found_ids.append(repre_id) continue - folder = folders_by_id.get(product["parent"]) - if not folder: + folder_entity = folders_by_id.get(product_entity["folderId"]) + if not folder_entity: not_found["folder"].extend(group_containers) not_found_ids.append(repre_id) continue group_dict.update({ "representation": representation, - "version": version, - "subset": product, - "asset": folder + "version": version_entity, + "product": product_entity, + "folder": folder_entity }) for _repre_id in not_found_ids: @@ -308,43 +299,34 @@ class InventoryModel(TreeModel): for repre_id, group_dict in sorted(grouped.items()): group_containers = group_dict["containers"] - representation = group_dict["representation"] - version = group_dict["version"] - subset = group_dict["subset"] - asset = group_dict["asset"] + repre_entity = group_dict["representation"] + version_entity = group_dict["version"] + folder_entity = group_dict["folder"] + product_entity = group_dict["product"] - # Get product type - maj_version, _ = schema.get_schema_version(subset["schema"]) - if maj_version < 3: - src_doc = version - else: - src_doc = subset - - product_type = src_doc["data"].get("family") - if not product_type: - families = src_doc["data"].get("families") - if families: - product_type = families[0] + product_type = product_entity["productType"] # Store the highest available version so the model can know # whether current version is currently up-to-date. - highest_version = get_last_version_by_subset_id( - project_name, version["parent"] + highest_version = ayon_api.get_last_version_by_product_id( + project_name, version_entity["productId"] ) # create the group header group_node = Item() group_node["Name"] = "{}_{}: ({})".format( - asset["name"], subset["name"], representation["name"] + folder_entity["name"], + product_entity["name"], + repre_entity["name"] ) group_node["representation"] = repre_id - group_node["version"] = version["name"] - group_node["highest_version"] = highest_version["name"] + group_node["version"] = version_entity["version"] + group_node["highest_version"] = highest_version["version"] group_node["productType"] = product_type or "" group_node["productTypeIcon"] = product_type_icon group_node["count"] = len(group_containers) group_node["isGroupNode"] = True - group_node["group"] = subset["data"].get("subsetGroup") + group_node["group"] = product_entity["attrib"].get("productGroup") # Site sync specific data progress = progress_by_id[repre_id] @@ -359,7 +341,8 @@ class InventoryModel(TreeModel): item_node.update(container) # store the current version on the item - item_node["version"] = version["name"] + item_node["version"] = version_entity["version"] + item_node["version_entity"] = version_entity # Remapping namespace to item name. # Noted that the name key is capital "N", by doing this, we @@ -404,73 +387,50 @@ class InventoryModel(TreeModel): if not filtered_repre_ids: return output - repre_docs = get_representations(project_name, repre_ids) + repre_entities = ayon_api.get_representations(project_name, repre_ids) repres_by_id.update({ - repre_doc["_id"]: repre_doc - for repre_doc in repre_docs + repre_entity["id"]: repre_entity + for repre_entity in repre_entities }) version_ids = { - repre_doc["parent"] for repre_doc in repres_by_id.values() + repre_entity["versionId"] + for repre_entity in repres_by_id.values() } if not version_ids: return output - version_docs = get_versions(project_name, version_ids, hero=True) versions_by_id.update({ - version_doc["_id"]: version_doc - for version_doc in version_docs - }) - hero_versions_by_subversion_id = collections.defaultdict(list) - for version_doc in versions_by_id.values(): - if version_doc["type"] != "hero_version": - continue - subversion = version_doc["version_id"] - hero_versions_by_subversion_id[subversion].append(version_doc) - - if hero_versions_by_subversion_id: - subversion_ids = set( - hero_versions_by_subversion_id.keys() + version_entity["id"]: version_entity + for version_entity in ayon_api.get_versions( + project_name, version_ids=version_ids ) - subversion_docs = get_versions(project_name, subversion_ids) - for subversion_doc in subversion_docs: - subversion_id = subversion_doc["_id"] - subversion_ids.discard(subversion_id) - h_version_docs = hero_versions_by_subversion_id[subversion_id] - for version_doc in h_version_docs: - version_doc["name"] = HeroVersionType( - subversion_doc["name"] - ) - version_doc["data"] = copy.deepcopy( - subversion_doc["data"] - ) - - for subversion_id in subversion_ids: - h_version_docs = hero_versions_by_subversion_id[subversion_id] - for version_doc in h_version_docs: - versions_by_id.pop(version_doc["_id"]) + }) product_ids = { - version_doc["parent"] - for version_doc in versions_by_id.values() + version_entity["productId"] + for version_entity in versions_by_id.values() } if not product_ids: return output - product_docs = get_subsets(project_name, product_ids) + products_by_id.update({ - product_doc["_id"]: product_doc - for product_doc in product_docs + product_entity["id"]: product_entity + for product_entity in ayon_api.get_products( + project_name, product_ids=product_ids + ) }) folder_ids = { - product_doc["parent"] - for product_doc in products_by_id.values() + product_entity["folderId"] + for product_entity in products_by_id.values() } if not folder_ids: return output - folder_docs = get_assets(project_name, folder_ids) folders_by_id.update({ - folder_doc["_id"]: folder_doc - for folder_doc in folder_docs + folder_entity["id"]: folder_entity + for folder_entity in ayon_api.get_folders( + project_name, folder_ids=folder_ids + ) }) return output diff --git a/client/ayon_core/tools/sceneinventory/models/site_sync.py b/client/ayon_core/tools/sceneinventory/models/site_sync.py index c7bc0b756d..7f09f2b25b 100644 --- a/client/ayon_core/tools/sceneinventory/models/site_sync.py +++ b/client/ayon_core/tools/sceneinventory/models/site_sync.py @@ -1,4 +1,5 @@ -from ayon_core.client import get_representations +import ayon_api + from ayon_core.addon import AddonsManager NOT_SET = object() @@ -69,14 +70,16 @@ class SiteSyncModel: project_name = self._controller.get_current_project_name() site_sync = self._get_sync_server_module() - repre_docs = get_representations(project_name, representation_ids) + repre_entities = ayon_api.get_representations( + project_name, representation_ids + ) active_site = self._get_active_site() remote_site = self._get_remote_site() - for repre_doc in repre_docs: - repre_output = output[repre_doc["_id"]] + for repre_entity in repre_entities: + repre_output = output[repre_entity["id"]] result = site_sync.get_progress_for_repre( - repre_doc, active_site, remote_site + repre_entity, active_site, remote_site ) repre_output["active_site"] = result[active_site] repre_output["remote_site"] = result[remote_site] diff --git a/client/ayon_core/tools/sceneinventory/switch_dialog/dialog.py b/client/ayon_core/tools/sceneinventory/switch_dialog/dialog.py index 89c3b652e1..823a4a6631 100644 --- a/client/ayon_core/tools/sceneinventory/switch_dialog/dialog.py +++ b/client/ayon_core/tools/sceneinventory/switch_dialog/dialog.py @@ -1,18 +1,10 @@ import collections import logging +import ayon_api from qtpy import QtWidgets, QtCore import qtawesome -from ayon_core.client import ( - get_assets, - get_subset_by_name, - get_subsets, - get_versions, - get_hero_versions, - get_last_versions, - get_representations, -) from ayon_core.pipeline.load import ( discover_loader_plugins, switch_container, @@ -135,16 +127,16 @@ class SwitchAssetDialog(QtWidgets.QDialog): # first asset field, this also allows to see the placeholder value. accept_btn.setFocus() - self._folder_docs_by_id = {} - self._product_docs_by_id = {} - self._version_docs_by_id = {} - self._repre_docs_by_id = {} + self._folder_entities_by_id = {} + self._product_entities_by_id = {} + self._version_entities_by_id = {} + self._repre_entities_by_id = {} self._missing_folder_ids = set() self._missing_product_ids = set() self._missing_version_ids = set() self._missing_repre_ids = set() - self._missing_docs = False + self._missing_entities = False self._inactive_folder_ids = set() self._inactive_product_ids = set() @@ -245,10 +237,10 @@ class SwitchAssetDialog(QtWidgets.QDialog): def find_last_versions(self, product_ids): project_name = self._project_name - return get_last_versions( + return ayon_api.get_last_versions( project_name, - subset_ids=product_ids, - fields=["_id", "parent", "type"] + product_ids, + fields={"id", "folderId", "version"} ) def _on_show_timer(self): @@ -265,124 +257,119 @@ class SwitchAssetDialog(QtWidgets.QDialog): } project_name = self._project_name - repres = list(get_representations( + repre_entities = list(ayon_api.get_representations( project_name, representation_ids=repre_ids, - archived=True, )) - repres_by_id = {str(repre["_id"]): repre for repre in repres} + repres_by_id = {r["id"]: r for r in repre_entities} - content_repre_docs_by_id = {} + content_repre_entities_by_id = {} inactive_repre_ids = set() missing_repre_ids = set() version_ids = set() for repre_id in repre_ids: - repre_doc = repres_by_id.get(repre_id) - if repre_doc is None: + repre_entity = repres_by_id.get(repre_id) + if repre_entity is None: missing_repre_ids.add(repre_id) - elif repres_by_id[repre_id]["type"] == "archived_representation": + elif not repres_by_id[repre_id]["active"]: inactive_repre_ids.add(repre_id) - version_ids.add(repre_doc["parent"]) + version_ids.add(repre_entity["versionId"]) else: - content_repre_docs_by_id[repre_id] = repre_doc - version_ids.add(repre_doc["parent"]) + content_repre_entities_by_id[repre_id] = repre_entity + version_ids.add(repre_entity["versionId"]) - version_docs = get_versions( + version_entities = ayon_api.get_versions( project_name, - version_ids=version_ids, - hero=True + version_ids=version_ids ) - content_version_docs_by_id = {} - for version_doc in version_docs: - version_id = version_doc["_id"] - content_version_docs_by_id[version_id] = version_doc + content_version_entities_by_id = {} + for version_entity in version_entities: + version_id = version_entity["id"] + content_version_entities_by_id[version_id] = version_entity missing_version_ids = set() product_ids = set() for version_id in version_ids: - version_doc = content_version_docs_by_id.get(version_id) - if version_doc is None: + version_entity = content_version_entities_by_id.get(version_id) + if version_entity is None: missing_version_ids.add(version_id) else: - product_ids.add(version_doc["parent"]) + product_ids.add(version_entity["productId"]) - product_docs = get_subsets( - project_name, subset_ids=product_ids, archived=True + product_entities = ayon_api.get_products( + project_name, product_ids=product_ids ) - product_docs_by_id = {sub["_id"]: sub for sub in product_docs} + product_entities_by_id = {p["id"]: p for p in product_entities} folder_ids = set() inactive_product_ids = set() missing_product_ids = set() - content_product_docs_by_id = {} + content_product_entities_by_id = {} for product_id in product_ids: - product_doc = product_docs_by_id.get(product_id) - if product_doc is None: + product_entity = product_entities_by_id.get(product_id) + if product_entity is None: missing_product_ids.add(product_id) - elif product_doc["type"] == "archived_subset": - folder_ids.add(product_doc["parent"]) - inactive_product_ids.add(product_id) else: - folder_ids.add(product_doc["parent"]) - content_product_docs_by_id[product_id] = product_doc + folder_ids.add(product_entity["folderId"]) + content_product_entities_by_id[product_id] = product_entity - folder_docs = get_assets( - project_name, asset_ids=folder_ids, archived=True + folder_entities = ayon_api.get_folders( + project_name, folder_ids=folder_ids, active=None ) - folder_docs_by_id = { - folder_doc["_id"]: folder_doc - for folder_doc in folder_docs + folder_entities_by_id = { + folder_entity["id"]: folder_entity + for folder_entity in folder_entities } missing_folder_ids = set() inactive_folder_ids = set() - content_folder_docs_by_id = {} + content_folder_entities_by_id = {} for folder_id in folder_ids: - folder_doc = folder_docs_by_id.get(folder_id) - if folder_doc is None: + folder_entity = folder_entities_by_id.get(folder_id) + if folder_entity is None: missing_folder_ids.add(folder_id) - elif folder_doc["type"] == "archived_asset": + elif not folder_entity["active"]: inactive_folder_ids.add(folder_id) else: - content_folder_docs_by_id[folder_id] = folder_doc + content_folder_entities_by_id[folder_id] = folder_entity # stash context values, works only for single representation init_folder_id = None init_product_name = None init_repre_name = None - if len(repres) == 1: - init_repre_doc = repres[0] - init_version_doc = content_version_docs_by_id.get( - init_repre_doc["parent"]) - init_product_doc = None - init_folder_doc = None - if init_version_doc: - init_product_doc = content_product_docs_by_id.get( - init_version_doc["parent"] + if len(repre_entities) == 1: + init_repre_entity = repre_entities[0] + init_version_entity = content_version_entities_by_id.get( + init_repre_entity["versionId"]) + init_product_entity = None + init_folder_entity = None + if init_version_entity: + init_product_entity = content_product_entities_by_id.get( + init_version_entity["productId"] ) - if init_product_doc: - init_folder_doc = content_folder_docs_by_id.get( - init_product_doc["parent"] + if init_product_entity: + init_folder_entity = content_folder_entities_by_id.get( + init_product_entity["folderId"] ) - if init_folder_doc: - init_repre_name = init_repre_doc["name"] - init_product_name = init_product_doc["name"] - init_folder_id = init_folder_doc["_id"] + if init_folder_entity: + init_repre_name = init_repre_entity["name"] + init_product_name = init_product_entity["name"] + init_folder_id = init_folder_entity["id"] self._init_folder_id = init_folder_id self._init_product_name = init_product_name self._init_repre_name = init_repre_name - self._folder_docs_by_id = content_folder_docs_by_id - self._product_docs_by_id = content_product_docs_by_id - self._version_docs_by_id = content_version_docs_by_id - self._repre_docs_by_id = content_repre_docs_by_id + self._folder_entities_by_id = content_folder_entities_by_id + self._product_entities_by_id = content_product_entities_by_id + self._version_entities_by_id = content_version_entities_by_id + self._repre_entities_by_id = content_repre_entities_by_id self._missing_folder_ids = missing_folder_ids self._missing_product_ids = missing_product_ids self._missing_version_ids = missing_version_ids self._missing_repre_ids = missing_repre_ids - self._missing_docs = ( + self._missing_entities = ( bool(missing_folder_ids) or bool(missing_version_ids) or bool(missing_product_ids) @@ -524,7 +511,7 @@ class SwitchAssetDialog(QtWidgets.QDialog): and not selected_product_name and not selected_repre ): - return list(self._repre_docs_by_id.keys()) + return list(self._repre_entities_by_id.keys()) # Everything is selected # [x] [x] [x] @@ -571,68 +558,68 @@ class SwitchAssetDialog(QtWidgets.QDialog): self, folder_id, selected_product_name, selected_repre ): project_name = self._project_name - product_doc = get_subset_by_name( + product_entity = ayon_api.get_product_by_name( project_name, selected_product_name, folder_id, - fields=["_id"] + fields={"id"} ) - product_id = product_doc["_id"] + product_id = product_entity["id"] last_versions_by_product_id = self.find_last_versions([product_id]) - version_doc = last_versions_by_product_id.get(product_id) - if not version_doc: + version_entity = last_versions_by_product_id.get(product_id) + if not version_entity: return [] - repre_docs = get_representations( + repre_entities = ayon_api.get_representations( project_name, - version_ids=[version_doc["_id"]], - representation_names=[selected_repre], - fields=["_id"] + version_ids={version_entity["id"]}, + representation_names={selected_repre}, + fields={"id"} ) - return [repre_doc["_id"] for repre_doc in repre_docs] + return {repre_entity["id"] for repre_entity in repre_entities} def _get_current_output_repre_ids_xxo(self, folder_id, product_name): project_name = self._project_name - product_doc = get_subset_by_name( + product_entity = ayon_api.get_product_by_name( project_name, product_name, folder_id, - fields=["_id"] + fields={"id"} ) - if not product_doc: + if not product_entity: return [] repre_names = set() - for repre_doc in self._repre_docs_by_id.values(): - repre_names.add(repre_doc["name"]) + for repre_entity in self._repre_entities_by_id.values(): + repre_names.add(repre_entity["name"]) # TODO where to take version ids? version_ids = [] - repre_docs = get_representations( + repre_entities = ayon_api.get_representations( project_name, representation_names=repre_names, version_ids=version_ids, - fields=["_id"] + fields={"id"} ) - return [repre_doc["_id"] for repre_doc in repre_docs] + return {repre_entity["id"] for repre_entity in repre_entities} def _get_current_output_repre_ids_xox(self, folder_id, selected_repre): product_names = { - product_doc["name"] - for product_doc in self._product_docs_by_id.values() + product_entity["name"] + for product_entity in self._product_entities_by_id.values() } project_name = self._project_name - product_docs = get_subsets( + product_entities = ayon_api.get_products( project_name, - asset_ids=[folder_id], - subset_names=product_names, - fields=["_id", "name"] + folder_ids=[folder_id], + product_names=product_names, + fields={"id", "name"} ) product_name_by_id = { - product_doc["_id"]: product_doc["name"] - for product_doc in product_docs + product_entity["id"]: product_entity["name"] + for product_entity in product_entities } product_ids = list(product_name_by_id.keys()) last_versions_by_product_id = self.find_last_versions(product_ids) @@ -640,35 +627,37 @@ class SwitchAssetDialog(QtWidgets.QDialog): for product_id, last_version in last_versions_by_product_id.items(): product_name = product_name_by_id[product_id] last_version_id_by_product_name[product_name] = ( - last_version["_id"] + last_version["id"] ) - repre_docs = get_representations( + repre_entities = ayon_api.get_representations( project_name, version_ids=last_version_id_by_product_name.values(), - representation_names=[selected_repre], - fields=["_id"] + representation_names={selected_repre}, + fields={"id"} ) - return [repre_doc["_id"] for repre_doc in repre_docs] + return {repre_entity["id"] for repre_entity in repre_entities} def _get_current_output_repre_ids_xoo(self, folder_id): project_name = self._project_name repres_by_product_name = collections.defaultdict(set) - for repre_doc in self._repre_docs_by_id.values(): - version_doc = self._version_docs_by_id[repre_doc["parent"]] - product_doc = self._product_docs_by_id[version_doc["parent"]] - product_name = product_doc["name"] - repres_by_product_name[product_name].add(repre_doc["name"]) + for repre_entity in self._repre_entities_by_id.values(): + version_id = repre_entity["versionId"] + version_entity = self._version_entities_by_id[version_id] + product_id = version_entity["productId"] + product_entity = self._product_entities_by_id[product_id] + product_name = product_entity["name"] + repres_by_product_name[product_name].add(repre_entity["name"]) - product_docs = list(get_subsets( + product_entities = list(ayon_api.get_products( project_name, - asset_ids=[folder_id], - subset_names=repres_by_product_name.keys(), - fields=["_id", "name"] + folder_ids=[folder_id], + product_names=repres_by_product_name.keys(), + fields={"id", "name"} )) product_name_by_id = { - product_doc["_id"]: product_doc["name"] - for product_doc in product_docs + product_entity["id"]: product_entity["name"] + for product_entity in product_entities } product_ids = list(product_name_by_id.keys()) last_versions_by_product_id = self.find_last_versions(product_ids) @@ -676,7 +665,7 @@ class SwitchAssetDialog(QtWidgets.QDialog): for product_id, last_version in last_versions_by_product_id.items(): product_name = product_name_by_id[product_id] last_version_id_by_product_name[product_name] = ( - last_version["_id"] + last_version["id"] ) repre_names_by_version_id = {} @@ -686,97 +675,103 @@ class SwitchAssetDialog(QtWidgets.QDialog): if version_id is not None: repre_names_by_version_id[version_id] = list(repre_names) - repre_docs = get_representations( + repre_entities = ayon_api.get_representations( project_name, names_by_version_ids=repre_names_by_version_id, - fields=["_id"] + fields={"id"} ) - return [repre_doc["_id"] for repre_doc in repre_docs] + return {repre_entity["id"] for repre_entity in repre_entities} def _get_current_output_repre_ids_oxx( self, product_name, selected_repre ): project_name = self._project_name - product_docs = get_subsets( + product_entities = ayon_api.get_products( project_name, - asset_ids=self._folder_docs_by_id.keys(), - subset_names=[product_name], - fields=["_id"] + folder_ids=self._folder_entities_by_id.keys(), + product_names=[product_name], + fields={"id"} ) - product_ids = [product_doc["_id"] for product_doc in product_docs] + product_ids = { + product_entity["id"] for product_entity in product_entities + } last_versions_by_product_id = self.find_last_versions(product_ids) - last_version_ids = [ - last_version["_id"] + last_version_ids = { + last_version["id"] for last_version in last_versions_by_product_id.values() - ] - repre_docs = get_representations( + } + + repre_entities = ayon_api.get_representations( project_name, version_ids=last_version_ids, - representation_names=[selected_repre], - fields=["_id"] + representation_names={selected_repre}, + fields={"id"} ) - return [repre_doc["_id"] for repre_doc in repre_docs] + return {repre_entity["id"] for repre_entity in repre_entities} def _get_current_output_repre_ids_oxo(self, product_name): project_name = self._project_name - product_docs = get_subsets( + product_entities = ayon_api.get_products( project_name, - asset_ids=self._folder_docs_by_id.keys(), - subset_names=[product_name], - fields=["_id", "parent"] + folder_ids=self._folder_entities_by_id.keys(), + product_names={product_name}, + fields={"id", "folderId"} ) - product_docs_by_id = { - product_doc["_id"]: product_doc - for product_doc in product_docs + product_entities_by_id = { + product_entity["id"]: product_entity + for product_entity in product_entities } - if not product_docs: + if not product_entities_by_id: return list() last_versions_by_product_id = self.find_last_versions( - product_docs_by_id.keys() + product_entities_by_id.keys() ) product_id_by_version_id = {} for product_id, last_version in last_versions_by_product_id.items(): - version_id = last_version["_id"] + version_id = last_version["id"] product_id_by_version_id[version_id] = product_id if not product_id_by_version_id: return list() repre_names_by_folder_id = collections.defaultdict(set) - for repre_doc in self._repre_docs_by_id.values(): - version_doc = self._version_docs_by_id[repre_doc["parent"]] - product_doc = self._product_docs_by_id[version_doc["parent"]] - folder_doc = self._folder_docs_by_id[product_doc["parent"]] - folder_id = folder_doc["_id"] - repre_names_by_folder_id[folder_id].add(repre_doc["name"]) + for repre_entity in self._repre_entities_by_id.values(): + version_id = repre_entity["versionId"] + version_entity = self._version_entities_by_id[version_id] + product_id = version_entity["productId"] + product_entity = self._product_entities_by_id[product_id] + folder_id = product_entity["folderId"] + folder_entity = self._folder_entities_by_id[folder_id] + folder_id = folder_entity["id"] + repre_names_by_folder_id[folder_id].add(repre_entity["name"]) repre_names_by_version_id = {} for last_version_id, product_id in product_id_by_version_id.items(): - product_doc = product_docs_by_id[product_id] - folder_id = product_doc["parent"] + product_entity = product_entities_by_id[product_id] + folder_id = product_entity["folderId"] repre_names = repre_names_by_folder_id.get(folder_id) if not repre_names: continue repre_names_by_version_id[last_version_id] = repre_names - repre_docs = get_representations( + repre_entities = ayon_api.get_representations( project_name, names_by_version_ids=repre_names_by_version_id, - fields=["_id"] + fields={"id"} ) - return [repre_doc["_id"] for repre_doc in repre_docs] + return {repre_entity["id"] for repre_entity in repre_entities} def _get_current_output_repre_ids_oox(self, selected_repre): project_name = self._project_name - repre_docs = get_representations( + repre_entities = ayon_api.get_representations( project_name, representation_names=[selected_repre], - version_ids=self._version_docs_by_id.keys(), - fields=["_id"] + version_ids=self._version_entities_by_id.keys(), + fields={"id"} ) - return [repre_doc["_id"] for repre_doc in repre_docs] + return {repre_entity["id"] for repre_entity in repre_entities} def _get_product_box_values(self): project_name = self._project_name @@ -784,18 +779,18 @@ class SwitchAssetDialog(QtWidgets.QDialog): if selected_folder_id: folder_ids = [selected_folder_id] else: - folder_ids = list(self._folder_docs_by_id.keys()) + folder_ids = list(self._folder_entities_by_id.keys()) - product_docs = get_subsets( + product_entities = ayon_api.get_products( project_name, - asset_ids=folder_ids, - fields=["parent", "name"] + folder_ids=folder_ids, + fields={"folderId", "name"} ) product_names_by_parent_id = collections.defaultdict(set) - for product_doc in product_docs: - product_names_by_parent_id[product_doc["parent"]].add( - product_doc["name"] + for product_entity in product_entities: + product_names_by_parent_id[product_entity["folderId"]].add( + product_entity["name"] ) possible_product_names = None @@ -824,15 +819,17 @@ class SwitchAssetDialog(QtWidgets.QDialog): # [ ] [ ] [?] if not selected_folder_id and not selected_product_name: # Find all representations of selection's products - possible_repres = get_representations( + possible_repres = ayon_api.get_representations( project_name, - version_ids=self._version_docs_by_id.keys(), - fields=["parent", "name"] + version_ids=self._version_entities_by_id.keys(), + fields={"versionId", "name"} ) possible_repres_by_parent = collections.defaultdict(set) for repre in possible_repres: - possible_repres_by_parent[repre["parent"]].add(repre["name"]) + possible_repres_by_parent[repre["versionId"]].add( + repre["name"] + ) output_repres = None for repre_names in possible_repres_by_parent.values(): @@ -848,44 +845,44 @@ class SwitchAssetDialog(QtWidgets.QDialog): # [x] [x] [?] if selected_folder_id and selected_product_name: - product_doc = get_subset_by_name( + product_entity = ayon_api.get_product_by_name( project_name, selected_product_name, selected_folder_id, - fields=["_id"] + fields={"id"} ) - product_id = product_doc["_id"] + product_id = product_entity["id"] last_versions_by_product_id = self.find_last_versions([product_id]) - version_doc = last_versions_by_product_id.get(product_id) - repre_docs = get_representations( + version_entity = last_versions_by_product_id.get(product_id) + repre_entities = ayon_api.get_representations( project_name, - version_ids=[version_doc["_id"]], - fields=["name"] + version_ids={version_entity["id"]}, + fields={"name"} ) - return [ - repre_doc["name"] - for repre_doc in repre_docs - ] + return { + repre_entity["name"] + for repre_entity in repre_entities + } # [x] [ ] [?] # If only folder is selected if selected_folder_id: # Filter products by names from content product_names = { - product_doc["name"] - for product_doc in self._product_docs_by_id.values() + product_entity["name"] + for product_entity in self._product_entities_by_id.values() } - product_docs = get_subsets( + product_entities = ayon_api.get_products( project_name, - asset_ids=[selected_folder_id], - subset_names=product_names, - fields=["_id"] + folder_ids={selected_folder_id}, + product_names=product_names, + fields={"id"} ) product_ids = { - product_doc["_id"] - for product_doc in product_docs + product_entity["id"] + for product_entity in product_entities } if not product_ids: return list() @@ -895,24 +892,24 @@ class SwitchAssetDialog(QtWidgets.QDialog): for product_id, last_version in ( last_versions_by_product_id.items() ): - version_id = last_version["_id"] + version_id = last_version["id"] product_id_by_version_id[version_id] = product_id if not product_id_by_version_id: return list() - repre_docs = list(get_representations( + repre_entities = list(ayon_api.get_representations( project_name, version_ids=product_id_by_version_id.keys(), - fields=["name", "parent"] + fields={"name", "versionId"} )) - if not repre_docs: + if not repre_entities: return list() repre_names_by_parent = collections.defaultdict(set) - for repre_doc in repre_docs: - repre_names_by_parent[repre_doc["parent"]].add( - repre_doc["name"] + for repre_entity in repre_entities: + repre_names_by_parent[repre_entity["versionId"]].add( + repre_entity["name"] ) available_repres = None @@ -926,46 +923,46 @@ class SwitchAssetDialog(QtWidgets.QDialog): return list(available_repres) # [ ] [x] [?] - product_docs = list(get_subsets( + product_entities = list(ayon_api.get_products( project_name, - asset_ids=self._folder_docs_by_id.keys(), - subset_names=[selected_product_name], - fields=["_id", "parent"] + folder_ids=self._folder_entities_by_id.keys(), + product_names=[selected_product_name], + fields={"id", "folderId"} )) - if not product_docs: + if not product_entities: return list() - product_docs_by_id = { - product_doc["_id"]: product_doc - for product_doc in product_docs + product_entities_by_id = { + product_entity["id"]: product_entity + for product_entity in product_entities } last_versions_by_product_id = self.find_last_versions( - product_docs_by_id.keys() + product_entities_by_id.keys() ) product_id_by_version_id = {} for product_id, last_version in last_versions_by_product_id.items(): - version_id = last_version["_id"] + version_id = last_version["id"] product_id_by_version_id[version_id] = product_id if not product_id_by_version_id: return list() - repre_docs = list( - get_representations( + repre_entities = list( + ayon_api.get_representations( project_name, version_ids=product_id_by_version_id.keys(), - fields=["name", "parent"] + fields={"name", "versionId"} ) ) - if not repre_docs: + if not repre_entities: return list() repre_names_by_folder_id = collections.defaultdict(set) - for repre_doc in repre_docs: - product_id = product_id_by_version_id[repre_doc["parent"]] - folder_id = product_docs_by_id[product_id]["parent"] - repre_names_by_folder_id[folder_id].add(repre_doc["name"]) + for repre_entity in repre_entities: + product_id = product_id_by_version_id[repre_entity["versionId"]] + folder_id = product_entities_by_id[product_id]["folderId"] + repre_names_by_folder_id[folder_id].add(repre_entity["name"]) available_repres = None for repre_names in repre_names_by_folder_id.values(): @@ -981,7 +978,7 @@ class SwitchAssetDialog(QtWidgets.QDialog): selected_folder_id = self._folders_field.get_selected_folder_id() if ( selected_folder_id is None - and (self._missing_docs or self._inactive_folder_ids) + and (self._missing_entities or self._inactive_folder_ids) ): validation_state.folder_ok = False @@ -1003,17 +1000,17 @@ class SwitchAssetDialog(QtWidgets.QDialog): # [x] [ ] [?] project_name = self._project_name - product_docs = get_subsets( - project_name, asset_ids=[selected_folder_id], fields=["name"] + product_entities = ayon_api.get_products( + project_name, folder_ids=[selected_folder_id], fields={"name"} ) product_names = set( - product_doc["name"] - for product_doc in product_docs + product_entity["name"] + for product_entity in product_entities ) - for product_doc in self._product_docs_by_id.values(): - if product_doc["name"] not in product_names: + for product_entity in self._product_entities_by_id.values(): + if product_entity["name"] not in product_names: validation_state.product_ok = False break @@ -1043,49 +1040,49 @@ class SwitchAssetDialog(QtWidgets.QDialog): selected_folder_id is not None and selected_product_name is not None ): - product_doc = get_subset_by_name( + product_entity = ayon_api.get_product_by_name( project_name, selected_product_name, selected_folder_id, - fields=["_id"] + fields={"id"} ) - product_id = product_doc["_id"] + product_id = product_entity["id"] last_versions_by_product_id = self.find_last_versions([product_id]) last_version = last_versions_by_product_id.get(product_id) if not last_version: validation_state.repre_ok = False return - repre_docs = get_representations( + repre_entities = ayon_api.get_representations( project_name, - version_ids=[last_version["_id"]], - fields=["name"] + version_ids={last_version["id"]}, + fields={"name"} ) repre_names = set( - repre_doc["name"] - for repre_doc in repre_docs + repre_entity["name"] + for repre_entity in repre_entities ) - for repre_doc in self._repre_docs_by_id.values(): - if repre_doc["name"] not in repre_names: + for repre_entity in self._repre_entities_by_id.values(): + if repre_entity["name"] not in repre_names: validation_state.repre_ok = False break return # [x] [ ] [ ] if selected_folder_id is not None: - product_docs = list(get_subsets( + product_entities = list(ayon_api.get_products( project_name, - asset_ids=[selected_folder_id], - fields=["_id", "name"] + folder_ids={selected_folder_id}, + fields={"id", "name"} )) product_name_by_id = {} product_ids = set() - for product_doc in product_docs: - product_id = product_doc["_id"] + for product_entity in product_entities: + product_id = product_entity["id"] product_ids.add(product_id) - product_name_by_id[product_id] = product_doc["name"] + product_name_by_id[product_id] = product_entity["name"] last_versions_by_product_id = self.find_last_versions(product_ids) @@ -1093,66 +1090,71 @@ class SwitchAssetDialog(QtWidgets.QDialog): for product_id, last_version in ( last_versions_by_product_id.items() ): - version_id = last_version["_id"] + version_id = last_version["id"] product_id_by_version_id[version_id] = product_id - repre_docs = get_representations( + repre_entities = ayon_api.get_representations( project_name, version_ids=product_id_by_version_id.keys(), - fields=["name", "parent"] + fields={"name", "versionId"} ) repres_by_product_name = collections.defaultdict(set) - for repre_doc in repre_docs: - product_id = product_id_by_version_id[repre_doc["parent"]] + for repre_entity in repre_entities: + version_id = repre_entity["versionId"] + product_id = product_id_by_version_id[version_id] product_name = product_name_by_id[product_id] - repres_by_product_name[product_name].add(repre_doc["name"]) + repres_by_product_name[product_name].add(repre_entity["name"]) - for repre_doc in self._repre_docs_by_id.values(): - version_doc = self._version_docs_by_id[repre_doc["parent"]] - product_doc = self._product_docs_by_id[version_doc["parent"]] - repre_names = repres_by_product_name[product_doc["name"]] - if repre_doc["name"] not in repre_names: + for repre_entity in self._repre_entities_by_id.values(): + version_id = repre_entity["versionId"] + version_entity = self._version_entities_by_id[version_id] + product_id = version_entity["productId"] + product_entity = self._product_entities_by_id[product_id] + repre_names = repres_by_product_name[product_entity["name"]] + if repre_entity["name"] not in repre_names: validation_state.repre_ok = False break return # [ ] [x] [ ] - # Product documents - product_docs = get_subsets( + # Product entities + product_entities = ayon_api.get_products( project_name, - asset_ids=self._folder_docs_by_id.keys(), - subset_names=[selected_product_name], - fields=["_id", "name", "parent"] + folder_ids=self._folder_entities_by_id.keys(), + product_names={selected_product_name}, + fields={"id", "name", "folderId"} ) - product_docs_by_id = {} - for product_doc in product_docs: - product_docs_by_id[product_doc["_id"]] = product_doc + product_entities_by_id = {} + for product_entity in product_entities: + product_entities_by_id[product_entity["id"]] = product_entity last_versions_by_product_id = self.find_last_versions( - product_docs_by_id.keys() + product_entities_by_id.keys() ) product_id_by_version_id = {} for product_id, last_version in last_versions_by_product_id.items(): - version_id = last_version["_id"] + version_id = last_version["id"] product_id_by_version_id[version_id] = product_id - repre_docs = get_representations( + repre_entities = ayon_api.get_representations( project_name, version_ids=product_id_by_version_id.keys(), - fields=["name", "parent"] + fields={"name", "versionId"} ) repres_by_folder_id = collections.defaultdict(set) - for repre_doc in repre_docs: - product_id = product_id_by_version_id[repre_doc["parent"]] - folder_id = product_docs_by_id[product_id]["parent"] - repres_by_folder_id[folder_id].add(repre_doc["name"]) + for repre_entity in repre_entities: + product_id = product_id_by_version_id[repre_entity["versionId"]] + folder_id = product_entities_by_id[product_id]["folderId"] + repres_by_folder_id[folder_id].add(repre_entity["name"]) - for repre_doc in self._repre_docs_by_id.values(): - version_doc = self._version_docs_by_id[repre_doc["parent"]] - product_doc = self._product_docs_by_id[version_doc["parent"]] - folder_id = product_doc["parent"] + for repre_entity in self._repre_entities_by_id.values(): + version_id = repre_entity["versionId"] + version_entity = self._version_entities_by_id[version_id] + product_id = version_entity["productId"] + product_entity = self._product_entities_by_id[product_id] + folder_id = product_entity["folderId"] repre_names = repres_by_folder_id[folder_id] - if repre_doc["name"] not in repre_names: + if repre_entity["name"] not in repre_names: validation_state.repre_ok = False break @@ -1182,57 +1184,59 @@ class SwitchAssetDialog(QtWidgets.QDialog): if selected_folder_id: folder_ids = {selected_folder_id} else: - folder_ids = set(self._folder_docs_by_id.keys()) + folder_ids = set(self._folder_entities_by_id.keys()) product_names = None if selected_product_name: product_names = [selected_product_name] - product_docs = list(get_subsets( + product_entities = list(ayon_api.get_products( project_name, - subset_names=product_names, - asset_ids=folder_ids + product_names=product_names, + folder_ids=folder_ids )) product_ids = set() - product_docs_by_parent_and_name = collections.defaultdict(dict) - for product_doc in product_docs: - product_ids.add(product_doc["_id"]) - folder_id = product_doc["parent"] - name = product_doc["name"] - product_docs_by_parent_and_name[folder_id][name] = product_doc + product_entities_by_parent_and_name = collections.defaultdict(dict) + for product_entity in product_entities: + product_ids.add(product_entity["id"]) + folder_id = product_entity["folderId"] + name = product_entity["name"] + product_entities_by_parent_and_name[folder_id][name] = ( + product_entity + ) # versions - _version_docs = get_versions(project_name, subset_ids=product_ids) - version_docs = list(reversed( - sorted(_version_docs, key=lambda item: item["name"]) - )) - - hero_version_docs = list(get_hero_versions( - project_name, subset_ids=product_ids + _version_entities = ayon_api.get_versions( + project_name, product_ids=product_ids + ) + version_entities = list(reversed( + sorted(_version_entities, key=lambda item: item["version"]) )) version_ids = set() - version_docs_by_parent_id_and_name = collections.defaultdict(dict) - for version_doc in version_docs: - version_ids.add(version_doc["_id"]) - product_id = version_doc["parent"] - name = version_doc["name"] - version_docs_by_parent_id_and_name[product_id][name] = version_doc + version_entities_by_product_id = collections.defaultdict(dict) + hero_version_entities_by_product_id = {} + for version_entity in version_entities: + version_ids.add(version_entity["id"]) + product_id = version_entity["productId"] + version = version_entity["version"] + if version < 0: + hero_version_entities_by_product_id[product_id] = ( + version_entity + ) + continue + version_entities_by_product_id[product_id][version] = ( + version_entity + ) - hero_version_docs_by_parent_id = {} - for hero_version_doc in hero_version_docs: - version_ids.add(hero_version_doc["_id"]) - parent_id = hero_version_doc["parent"] - hero_version_docs_by_parent_id[parent_id] = hero_version_doc - - repre_docs = get_representations( + repre_entities = ayon_api.get_representations( project_name, version_ids=version_ids ) - repre_docs_by_parent_id_by_name = collections.defaultdict(dict) - for repre_doc in repre_docs: - parent_id = repre_doc["parent"] - name = repre_doc["name"] - repre_docs_by_parent_id_by_name[parent_id][name] = repre_doc + repre_entities_by_name_version_id = collections.defaultdict(dict) + for repre_entity in repre_entities: + version_id = repre_entity["versionId"] + name = repre_entity["name"] + repre_entities_by_name_version_id[version_id][name] = repre_entity for container in self._items: self._switch_container( @@ -1241,10 +1245,10 @@ class SwitchAssetDialog(QtWidgets.QDialog): selected_folder_id, selected_product_name, selected_representation, - product_docs_by_parent_and_name, - version_docs_by_parent_id_and_name, - hero_version_docs_by_parent_id, - repre_docs_by_parent_id_by_name, + product_entities_by_parent_and_name, + version_entities_by_product_id, + hero_version_entities_by_product_id, + repre_entities_by_name_version_id, ) self.switched.emit() @@ -1258,81 +1262,81 @@ class SwitchAssetDialog(QtWidgets.QDialog): selected_folder_id, selected_product_name, selected_representation, - product_docs_by_parent_and_name, - version_docs_by_parent_id_and_name, - hero_version_docs_by_parent_id, - repre_docs_by_parent_id_by_name, + product_entities_by_parent_and_name, + version_entities_by_product_id, + hero_version_entities_by_product_id, + repre_entities_by_name_version_id, ): container_repre_id = container["representation"] - container_repre = self._repre_docs_by_id[container_repre_id] + container_repre = self._repre_entities_by_id[container_repre_id] container_repre_name = container_repre["name"] - container_version_id = container_repre["parent"] + container_version_id = container_repre["versionId"] - container_version = self._version_docs_by_id[container_version_id] + container_version = self._version_entities_by_id[container_version_id] - container_product_id = container_version["parent"] - container_product = self._product_docs_by_id[container_product_id] + container_product_id = container_version["productId"] + container_product = self._product_entities_by_id[container_product_id] container_product_name = container_product["name"] - container_folder_id = container_product["parent"] + container_folder_id = container_product["folderId"] if selected_folder_id: folder_id = selected_folder_id else: folder_id = container_folder_id - products_by_name = product_docs_by_parent_and_name[folder_id] + products_by_name = product_entities_by_parent_and_name[folder_id] if selected_product_name: - product_doc = products_by_name[selected_product_name] + product_entity = products_by_name[selected_product_name] else: - product_doc = products_by_name[container_product["name"]] + product_entity = products_by_name[container_product["name"]] - repre_doc = None - product_id = product_doc["_id"] - if container_version["type"] == "hero_version": - hero_version = hero_version_docs_by_parent_id.get( + repre_entity = None + product_id = product_entity["id"] + if container_version["version"] < 0: + hero_version = hero_version_entities_by_product_id.get( product_id ) if hero_version: - _repres = repre_docs_by_parent_id_by_name.get( - hero_version["_id"] + _repres = repre_entities_by_name_version_id.get( + hero_version["id"] ) if selected_representation: - repre_doc = _repres.get(selected_representation) + repre_entity = _repres.get(selected_representation) else: - repre_doc = _repres.get(container_repre_name) + repre_entity = _repres.get(container_repre_name) - if not repre_doc: - version_docs_by_name = ( - version_docs_by_parent_id_and_name[product_id] + if not repre_entity: + version_entities_by_version = ( + version_entities_by_product_id[product_id] ) - # If asset or subset are selected for switching, we use latest + # If folder or product are selected for switching, we use latest # version else we try to keep the current container version. - version_name = None + version = None if ( selected_folder_id in (None, container_folder_id) and selected_product_name in (None, container_product_name) ): - version_name = container_version.get("name") + version = container_version.get("version") - version_doc = None - if version_name is not None: - version_doc = version_docs_by_name.get(version_name) + version_entity = None + if version is not None: + version_entity = version_entities_by_version.get(version) - if version_doc is None: - version_name = max(version_docs_by_name) - version_doc = version_docs_by_name[version_name] + if version_entity is None: + version_name = max(version_entities_by_version) + version_entity = version_entities_by_version[version_name] - version_id = version_doc["_id"] - repres_by_name = repre_docs_by_parent_id_by_name[version_id] + version_id = version_entity["id"] + repres_by_name = repre_entities_by_name_version_id[version_id] if selected_representation: - repre_doc = repres_by_name[selected_representation] + repre_entity = repres_by_name[selected_representation] else: - repre_doc = repres_by_name[container_repre_name] + repre_entity = repres_by_name[container_repre_name] error = None try: - switch_container(container, repre_doc, loader) + switch_container(container, repre_entity, loader) except ( LoaderSwitchNotImplementedError, IncompatibleLoaderError, diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index 80c89338f5..140a4a32af 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -4,16 +4,10 @@ import logging import itertools from functools import partial +import ayon_api from qtpy import QtWidgets, QtCore import qtawesome -from ayon_core.client import ( - get_version_by_id, - get_versions, - get_hero_versions, - get_representation_by_id, - get_representations, -) from ayon_core import style from ayon_core.pipeline import ( HeroVersionType, @@ -97,50 +91,54 @@ class SceneInventoryView(QtWidgets.QTreeView): pass project_name = self._controller.get_current_project_name() - repre_docs = get_representations( - project_name, representation_ids=repre_ids, fields=["parent"] + repre_entities = ayon_api.get_representations( + project_name, + representation_ids=repre_ids, + fields={"versionId"} ) version_ids = { - repre_doc["parent"] - for repre_doc in repre_docs + repre_entity["versionId"] + for repre_entity in repre_entities } - loaded_versions = get_versions( - project_name, version_ids=version_ids, hero=True + loaded_versions = ayon_api.get_versions( + project_name, version_ids=version_ids ) loaded_hero_versions = [] - versions_by_parent_id = collections.defaultdict(list) + versions_by_product_id = collections.defaultdict(list) product_ids = set() - for version in loaded_versions: - if version["type"] == "hero_version": - loaded_hero_versions.append(version) + for version_entity in loaded_versions: + version = version_entity["version"] + if version < 0: + loaded_hero_versions.append(version_entity) else: - parent_id = version["parent"] - versions_by_parent_id[parent_id].append(version) - product_ids.add(parent_id) + product_id = version_entity["productId"] + versions_by_product_id[product_id].append(version_entity) + product_ids.add(product_id) - all_versions = get_versions( - project_name, subset_ids=product_ids, hero=True + all_versions = ayon_api.get_versions( + project_name, product_ids=product_ids ) hero_versions = [] - versions = [] - for version in all_versions: - if version["type"] == "hero_version": - hero_versions.append(version) + version_entities = [] + for version_entity in all_versions: + version = version_entity["version"] + if version < 0: + hero_versions.append(version_entity) else: - versions.append(version) + version_entities.append(version_entity) has_loaded_hero_versions = len(loaded_hero_versions) > 0 has_available_hero_version = len(hero_versions) > 0 has_outdated = False - for version in versions: - parent_id = version["parent"] - current_versions = versions_by_parent_id[parent_id] + for version_entity in version_entities: + product_id = version_entity["productId"] + current_versions = versions_by_product_id[product_id] for current_version in current_versions: - if current_version["name"] < version["name"]: + if current_version["version"] < version_entity["version"]: has_outdated = True break @@ -155,46 +153,51 @@ class SceneInventoryView(QtWidgets.QTreeView): for item in items } - repre_docs = get_representations( + repre_entities = ayon_api.get_representations( project_name, representation_ids=repre_ids, - fields=["parent"] + fields={"id", "versionId"} ) - version_ids = set() version_id_by_repre_id = {} - for repre_doc in repre_docs: - version_id = repre_doc["parent"] - repre_id = str(repre_doc["_id"]) + for repre_entity in repre_entities: + repre_id = repre_entity["id"] + version_id = repre_entity["versionId"] version_id_by_repre_id[repre_id] = version_id - version_ids.add(version_id) + version_ids = set(version_id_by_repre_id.values()) - hero_versions = get_hero_versions( - project_name, - version_ids=version_ids, - fields=["version_id"] + src_version_entity_by_id = { + version_entity["id"]: version_entity + for version_entity in ayon_api.get_versions( + project_name, + version_ids, + fields={"productId", "version"} + ) + } + hero_versions_by_product_id = {} + for version_entity in src_version_entity_by_id.values(): + version = version_entity["version"] + if version < 0: + product_id = version_entity["productId"] + hero_versions_by_product_id[product_id] = abs(version) + + if not hero_versions_by_product_id: + return + + standard_versions = ayon_api.get_versions( + product_ids=hero_versions_by_product_id.keys(), + versions=hero_versions_by_product_id.values() ) - - hero_src_version_ids = set() - for hero_version in hero_versions: - version_id = hero_version["version_id"] - hero_src_version_ids.add(version_id) - hero_version_id = hero_version["_id"] - for _repre_id, current_version_id in ( - version_id_by_repre_id.items() - ): - if current_version_id == hero_version_id: - version_id_by_repre_id[_repre_id] = version_id - - version_docs = get_versions( - project_name, - version_ids=hero_src_version_ids, - fields=["name"] - ) - version_name_by_id = {} - for version_doc in version_docs: - version_name_by_id[version_doc["_id"]] = \ - version_doc["name"] + standard_version_by_product_id = { + product_id: {} + for product_id in hero_versions_by_product_id.keys() + } + for version_entity in standard_versions: + product_id = version_entity["productId"] + version = version_entity["version"] + standard_version_by_product_id[product_id][version] = ( + version_entity + ) # Specify version per item to update to update_items = [] @@ -202,10 +205,20 @@ class SceneInventoryView(QtWidgets.QTreeView): for item in items: repre_id = item["representation"] version_id = version_id_by_repre_id.get(repre_id) - version_name = version_name_by_id.get(version_id) - if version_name is not None: + version_entity = src_version_entity_by_id.get(version_id) + if not version_entity or version_entity["version"] >= 0: + continue + product_id = version_entity["productId"] + version_entities_by_version = ( + standard_version_by_product_id[product_id] + ) + new_version = hero_versions_by_product_id.get(product_id) + new_version_entity = version_entities_by_version.get( + new_version + ) + if new_version_entity is not None: update_items.append(item) - update_versions.append(version_name) + update_versions.append(new_version) self._update_containers(update_items, update_versions) update_icon = qtawesome.icon( @@ -249,8 +262,9 @@ class SceneInventoryView(QtWidgets.QTreeView): menu ) change_to_hero.triggered.connect( - lambda: self._update_containers(items, - version=HeroVersionType(-1)) + lambda: self._update_containers( + items, version=HeroVersionType(-1) + ) ) # set version @@ -608,44 +622,31 @@ class SceneInventoryView(QtWidgets.QTreeView): project_name = self._controller.get_current_project_name() # Get available versions for active representation - repre_doc = get_representation_by_id( + repre_entity = ayon_api.get_representation_by_id( project_name, active["representation"], - fields=["parent"] + fields={"versionId"} ) - repre_version_doc = get_version_by_id( + repre_version_entity = ayon_api.get_version_by_id( project_name, - repre_doc["parent"], - fields=["parent"] + repre_entity["versionId"], + fields={"productId"} ) - version_docs = list(get_versions( + version_entities = list(ayon_api.get_versions( project_name, - subset_ids=[repre_version_doc["parent"]], - hero=True + product_ids={repre_version_entity["productId"]}, )) hero_version = None standard_versions = [] - for version_doc in version_docs: - if version_doc["type"] == "hero_version": - hero_version = version_doc + for version_entity in version_entities: + if version_entity["version"] < 0: + hero_version = version_entity else: - standard_versions.append(version_doc) - versions = list(reversed( - sorted(standard_versions, key=lambda item: item["name"]) - )) - if hero_version: - _version_id = hero_version["version_id"] - for _version in versions: - if _version["_id"] != _version_id: - continue - - hero_version["name"] = HeroVersionType( - _version["name"] - ) - hero_version["data"] = _version["data"] - break + standard_versions.append(version_entity) + standard_versions.sort(key=lambda item: item["version"]) + standard_versions.reverse() # Get index among the listed versions current_item = None @@ -653,15 +654,15 @@ class SceneInventoryView(QtWidgets.QTreeView): if isinstance(current_version, HeroVersionType): current_item = hero_version else: - for version in versions: - if version["name"] == current_version: - current_item = version + for version_entity in standard_versions: + if version_entity["version"] == current_version: + current_item = version_entity break all_versions = [] if hero_version: all_versions.append(hero_version) - all_versions.extend(versions) + all_versions.extend(standard_versions) if current_item: index = all_versions.index(current_item) @@ -670,11 +671,10 @@ class SceneInventoryView(QtWidgets.QTreeView): versions_by_label = dict() labels = [] - for version in all_versions: - is_hero = version["type"] == "hero_version" - label = format_version(version["name"], is_hero) + for version_entity in all_versions: + label = format_version(version_entity["version"]) labels.append(label) - versions_by_label[label] = version["name"] + versions_by_label[label] = version_entity["version"] label, state = QtWidgets.QInputDialog.getItem( self, @@ -689,6 +689,8 @@ class SceneInventoryView(QtWidgets.QTreeView): if label: version = versions_by_label[label] + if version < 0: + version = HeroVersionType(version) self._update_containers(items, version) def _show_switch_dialog(self, items): From 9acedf2201ce0469fd4f1723b5ec30a5e43cf34b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 12:36:48 +0100 Subject: [PATCH 475/573] blender using representation entity --- .../ayon_core/hosts/blender/api/pipeline.py | 4 +- .../hosts/blender/plugins/load/load_abc.py | 12 ++-- .../hosts/blender/plugins/load/load_action.py | 8 +-- .../hosts/blender/plugins/load/load_audio.py | 14 ++-- .../hosts/blender/plugins/load/load_blend.py | 14 ++-- .../blender/plugins/load/load_blendscene.py | 12 ++-- .../blender/plugins/load/load_camera_abc.py | 12 ++-- .../blender/plugins/load/load_camera_fbx.py | 12 ++-- .../hosts/blender/plugins/load/load_fbx.py | 12 ++-- .../blender/plugins/load/load_layout_json.py | 14 ++-- .../hosts/blender/plugins/load/load_look.py | 10 +-- .../blender/plugins/publish/extract_layout.py | 70 +++++++++++-------- .../plugins/publish/integrate_animation.py | 2 +- 13 files changed, 102 insertions(+), 94 deletions(-) diff --git a/client/ayon_core/hosts/blender/api/pipeline.py b/client/ayon_core/hosts/blender/api/pipeline.py index a2019b3288..84e78d0883 100644 --- a/client/ayon_core/hosts/blender/api/pipeline.py +++ b/client/ayon_core/hosts/blender/api/pipeline.py @@ -484,7 +484,7 @@ def containerise(name: str, "name": name, "namespace": namespace or '', "loader": str(loader), - "representation": str(context["representation"]["_id"]), + "representation": context["representation"]["id"], } metadata_update(container, data) @@ -523,7 +523,7 @@ def containerise_existing( "name": name, "namespace": namespace or '', "loader": str(loader), - "representation": str(context["representation"]["_id"]), + "representation": context["representation"]["id"], } metadata_update(container, data) diff --git a/client/ayon_core/hosts/blender/plugins/load/load_abc.py b/client/ayon_core/hosts/blender/plugins/load/load_abc.py index 976697984c..7ae9936ed5 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_abc.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_abc.py @@ -168,10 +168,10 @@ class CacheModelLoader(plugin.AssetLoader): "name": name, "namespace": namespace or '', "loader": str(self.__class__.__name__), - "representation": str(context["representation"]["_id"]), + "representation": context["representation"]["id"], "libpath": libpath, "asset_name": asset_name, - "parent": str(context["representation"]["parent"]), + "parent": context["representation"]["versionId"], "productType": product_type, "objectName": group_name } @@ -191,16 +191,16 @@ class CacheModelLoader(plugin.AssetLoader): Warning: No nested collections are supported at the moment! """ - repre_doc = context["representation"] + repre_entity = context["representation"] object_name = container["objectName"] asset_group = bpy.data.objects.get(object_name) - libpath = Path(get_representation_path(repre_doc)) + libpath = Path(get_representation_path(repre_entity)) extension = libpath.suffix.lower() self.log.info( "Container: %s\nRepresentation: %s", pformat(container, indent=2), - pformat(repre_doc, indent=2), + pformat(repre_entity, indent=2), ) assert asset_group, ( @@ -245,7 +245,7 @@ class CacheModelLoader(plugin.AssetLoader): asset_group.matrix_basis = mat metadata["libpath"] = str(libpath) - metadata["representation"] = str(repre_doc["_id"]) + metadata["representation"] = repre_entity["id"] def exec_remove(self, container: Dict) -> bool: """Remove an existing container from a Blender scene. diff --git a/client/ayon_core/hosts/blender/plugins/load/load_action.py b/client/ayon_core/hosts/blender/plugins/load/load_action.py index c28040e614..a079387b9f 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_action.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_action.py @@ -126,18 +126,18 @@ class BlendActionLoader(plugin.AssetLoader): Warning: No nested collections are supported at the moment! """ - repre_doc = context["representation"] + repre_entity = context["representation"] collection = bpy.data.collections.get( container["objectName"] ) - libpath = Path(get_representation_path(repre_doc)) + libpath = Path(get_representation_path(repre_entity)) extension = libpath.suffix.lower() logger.info( "Container: %s\nRepresentation: %s", pformat(container, indent=2), - pformat(repre_doc, indent=2), + pformat(repre_entity, indent=2), ) assert collection, ( @@ -241,7 +241,7 @@ class BlendActionLoader(plugin.AssetLoader): # Save the list of objects in the metadata container collection_metadata["objects"] = objects_list collection_metadata["libpath"] = str(libpath) - collection_metadata["representation"] = str(repre_doc["_id"]) + collection_metadata["representation"] = repre_entity["id"] bpy.ops.object.select_all(action='DESELECT') diff --git a/client/ayon_core/hosts/blender/plugins/load/load_audio.py b/client/ayon_core/hosts/blender/plugins/load/load_audio.py index d3cafe710e..de74bf95d3 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_audio.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_audio.py @@ -83,10 +83,10 @@ class AudioLoader(plugin.AssetLoader): "name": name, "namespace": namespace or '', "loader": str(self.__class__.__name__), - "representation": str(context["representation"]["_id"]), + "representation": context["representation"]["id"], "libpath": libpath, "asset_name": asset_name, - "parent": str(context["representation"]["parent"]), + "parent": context["representation"]["versionId"], "productType": context["product"]["productType"], "objectName": group_name, "audio": audio @@ -105,15 +105,15 @@ class AudioLoader(plugin.AssetLoader): representation (openpype:representation-1.0): Representation to update, from `host.ls()`. """ - repre_doc = context["representation"] + repre_entity = context["representation"] object_name = container["objectName"] asset_group = bpy.data.objects.get(object_name) - libpath = Path(get_representation_path(repre_doc)) + libpath = Path(get_representation_path(repre_entity)) self.log.info( "Container: %s\nRepresentation: %s", pformat(container, indent=2), - pformat(repre_doc, indent=2), + pformat(repre_entity, indent=2), ) assert asset_group, ( @@ -176,8 +176,8 @@ class AudioLoader(plugin.AssetLoader): window_manager.windows[-1].screen.areas[0].type = old_type metadata["libpath"] = str(libpath) - metadata["representation"] = str(repre_doc["_id"]) - metadata["parent"] = str(repre_doc["parent"]) + metadata["representation"] = repre_entity["id"] + metadata["parent"] = repre_entity["versionId"] metadata["audio"] = new_audio def exec_remove(self, container: Dict) -> bool: diff --git a/client/ayon_core/hosts/blender/plugins/load/load_blend.py b/client/ayon_core/hosts/blender/plugins/load/load_blend.py index c8c01be49e..465485ba86 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_blend.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_blend.py @@ -135,7 +135,7 @@ class BlendLoader(plugin.AssetLoader): except ValueError: product_type = "model" - representation = str(context["representation"]["_id"]) + representation = context["representation"]["id"] asset_name = plugin.prepare_scene_name(folder_name, product_name) unique_number = plugin.get_unique_number(folder_name, product_name) @@ -162,10 +162,10 @@ class BlendLoader(plugin.AssetLoader): "name": name, "namespace": namespace or '', "loader": str(self.__class__.__name__), - "representation": str(context["representation"]["_id"]), + "representation": context["representation"]["id"], "libpath": libpath, "asset_name": asset_name, - "parent": str(context["representation"]["parent"]), + "parent": context["representation"]["versionId"], "productType": context["product"]["productType"], "objectName": group_name, "members": members, @@ -185,10 +185,10 @@ class BlendLoader(plugin.AssetLoader): """ Update the loaded asset. """ - repre_doc = context["representation"] + repre_entity = context["representation"] group_name = container["objectName"] asset_group = bpy.data.objects.get(group_name) - libpath = Path(get_representation_path(repre_doc)).as_posix() + libpath = Path(get_representation_path(repre_entity)).as_posix() assert asset_group, ( f"The asset is not loaded: {container['objectName']}" @@ -235,8 +235,8 @@ class BlendLoader(plugin.AssetLoader): new_data = { "libpath": libpath, - "representation": str(repre_doc["_id"]), - "parent": str(repre_doc["parent"]), + "representation": repre_entity["id"], + "parent": repre_entity["versionId"], "members": members, } diff --git a/client/ayon_core/hosts/blender/plugins/load/load_blendscene.py b/client/ayon_core/hosts/blender/plugins/load/load_blendscene.py index 107b586604..2c93b739f8 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_blendscene.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_blendscene.py @@ -114,10 +114,10 @@ class BlendSceneLoader(plugin.AssetLoader): "name": name, "namespace": namespace or '', "loader": str(self.__class__.__name__), - "representation": str(context["representation"]["_id"]), + "representation": context["representation"]["id"], "libpath": libpath, "asset_name": asset_name, - "parent": str(context["representation"]["parent"]), + "parent": context["representation"]["versionId"], "productType": context["product"]["productType"], "objectName": group_name, "members": members, @@ -137,10 +137,10 @@ class BlendSceneLoader(plugin.AssetLoader): """ Update the loaded asset. """ - repre_doc = context["representation"] + repre_entity = context["representation"] group_name = container["objectName"] asset_group = bpy.data.collections.get(group_name) - libpath = Path(get_representation_path(repre_doc)).as_posix() + libpath = Path(get_representation_path(repre_entity)).as_posix() assert asset_group, ( f"The asset is not loaded: {container['objectName']}" @@ -202,8 +202,8 @@ class BlendSceneLoader(plugin.AssetLoader): new_data = { "libpath": libpath, - "representation": str(repre_doc["_id"]), - "parent": str(repre_doc["parent"]), + "representation": repre_entity["id"], + "parent": repre_entity["versionId"], "members": members, } diff --git a/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py b/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py index 54e466127e..98a4e2c2ec 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py @@ -119,10 +119,10 @@ class AbcCameraLoader(plugin.AssetLoader): "name": name, "namespace": namespace or "", "loader": str(self.__class__.__name__), - "representation": str(context["representation"]["_id"]), + "representation": context["representation"]["id"], "libpath": libpath, "asset_name": asset_name, - "parent": str(context["representation"]["parent"]), + "parent": context["representation"]["versionId"], "productType": context["product"]["productType"], "objectName": group_name, } @@ -142,16 +142,16 @@ class AbcCameraLoader(plugin.AssetLoader): Warning: No nested collections are supported at the moment! """ - repre_doc = context["representation"] + repre_entity = context["representation"] object_name = container["objectName"] asset_group = bpy.data.objects.get(object_name) - libpath = Path(get_representation_path(repre_doc)) + libpath = Path(get_representation_path(repre_entity)) extension = libpath.suffix.lower() self.log.info( "Container: %s\nRepresentation: %s", pformat(container, indent=2), - pformat(repre_doc, indent=2), + pformat(repre_entity, indent=2), ) assert asset_group, ( @@ -186,7 +186,7 @@ class AbcCameraLoader(plugin.AssetLoader): asset_group.matrix_basis = mat metadata["libpath"] = str(libpath) - metadata["representation"] = str(repre_doc["_id"]) + metadata["representation"] = repre_entity["id"] def exec_remove(self, container: Dict) -> bool: """Remove an existing container from a Blender scene. diff --git a/client/ayon_core/hosts/blender/plugins/load/load_camera_fbx.py b/client/ayon_core/hosts/blender/plugins/load/load_camera_fbx.py index b96e8fb46d..f31e53270e 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_camera_fbx.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_camera_fbx.py @@ -122,10 +122,10 @@ class FbxCameraLoader(plugin.AssetLoader): "name": name, "namespace": namespace or '', "loader": str(self.__class__.__name__), - "representation": str(context["representation"]["_id"]), + "representation": context["representation"]["id"], "libpath": libpath, "asset_name": asset_name, - "parent": str(context["representation"]["parent"]), + "parent": context["representation"]["versionId"], "productType": context["product"]["productType"], "objectName": group_name } @@ -145,16 +145,16 @@ class FbxCameraLoader(plugin.AssetLoader): Warning: No nested collections are supported at the moment! """ - repre_doc = context["representation"] + repre_entity = context["representation"] object_name = container["objectName"] asset_group = bpy.data.objects.get(object_name) - libpath = Path(get_representation_path(repre_doc)) + libpath = Path(get_representation_path(repre_entity)) extension = libpath.suffix.lower() self.log.info( "Container: %s\nRepresentation: %s", pformat(container, indent=2), - pformat(repre_doc, indent=2), + pformat(repre_entity, indent=2), ) assert asset_group, ( @@ -196,7 +196,7 @@ class FbxCameraLoader(plugin.AssetLoader): asset_group.matrix_basis = mat metadata["libpath"] = str(libpath) - metadata["representation"] = str(repre_doc["_id"]) + metadata["representation"] = repre_entity["id"] def exec_remove(self, container: Dict) -> bool: """Remove an existing container from a Blender scene. diff --git a/client/ayon_core/hosts/blender/plugins/load/load_fbx.py b/client/ayon_core/hosts/blender/plugins/load/load_fbx.py index 64d37d5e8b..1eaab4e7b7 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_fbx.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_fbx.py @@ -166,10 +166,10 @@ class FbxModelLoader(plugin.AssetLoader): "name": name, "namespace": namespace or '', "loader": str(self.__class__.__name__), - "representation": str(context["representation"]["_id"]), + "representation": context["representation"]["id"], "libpath": libpath, "asset_name": asset_name, - "parent": str(context["representation"]["parent"]), + "parent": context["representation"]["versionId"], "productType": context["product"]["productType"], "objectName": group_name } @@ -189,16 +189,16 @@ class FbxModelLoader(plugin.AssetLoader): Warning: No nested collections are supported at the moment! """ - repre_doc = context["representation"] + repre_entity = context["representation"] object_name = container["objectName"] asset_group = bpy.data.objects.get(object_name) - libpath = Path(get_representation_path(repre_doc)) + libpath = Path(get_representation_path(repre_entity)) extension = libpath.suffix.lower() self.log.info( "Container: %s\nRepresentation: %s", pformat(container, indent=2), - pformat(repre_doc, indent=2), + pformat(repre_entity, indent=2), ) assert asset_group, ( @@ -251,7 +251,7 @@ class FbxModelLoader(plugin.AssetLoader): asset_group.matrix_basis = mat metadata["libpath"] = str(libpath) - metadata["representation"] = str(repre_doc["_id"]) + metadata["representation"] = repre_entity["id"] def exec_remove(self, container: Dict) -> bool: """Remove an existing container from a Blender scene. diff --git a/client/ayon_core/hosts/blender/plugins/load/load_layout_json.py b/client/ayon_core/hosts/blender/plugins/load/load_layout_json.py index 80424bb610..f3d14ac018 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_layout_json.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_layout_json.py @@ -132,7 +132,7 @@ class JsonLayoutLoader(plugin.AssetLoader): # # name=f"{unique_number}_{product[name]}_animation", # asset=asset, # options={"useSelection": False} - # # data={"dependencies": str(context["representation"]["_id"])} + # # data={"dependencies": context["representation"]["id"]} # ) def process_asset(self, @@ -177,10 +177,10 @@ class JsonLayoutLoader(plugin.AssetLoader): "name": name, "namespace": namespace or '', "loader": str(self.__class__.__name__), - "representation": str(context["representation"]["_id"]), + "representation": context["representation"]["id"], "libpath": libpath, "asset_name": asset_name, - "parent": str(context["representation"]["parent"]), + "parent": context["representation"]["versionId"], "productType": context["product"]["productType"], "objectName": group_name } @@ -197,16 +197,16 @@ class JsonLayoutLoader(plugin.AssetLoader): will not be removed, only unlinked. Normally this should not be the case though. """ - repre_doc = context["representation"] + repre_entity = context["representation"] object_name = container["objectName"] asset_group = bpy.data.objects.get(object_name) - libpath = Path(get_representation_path(repre_doc)) + libpath = Path(get_representation_path(repre_entity)) extension = libpath.suffix.lower() self.log.info( "Container: %s\nRepresentation: %s", pformat(container, indent=2), - pformat(repre_doc, indent=2), + pformat(repre_entity, indent=2), ) assert asset_group, ( @@ -270,7 +270,7 @@ class JsonLayoutLoader(plugin.AssetLoader): asset_group.matrix_basis = mat metadata["libpath"] = str(libpath) - metadata["representation"] = str(repre_doc["_id"]) + metadata["representation"] = repre_entity["id"] def exec_remove(self, container: Dict) -> bool: """Remove an existing container from a Blender scene. diff --git a/client/ayon_core/hosts/blender/plugins/load/load_look.py b/client/ayon_core/hosts/blender/plugins/load/load_look.py index 2c84415861..7cefbd5148 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_look.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_look.py @@ -130,7 +130,7 @@ class BlendLookLoader(plugin.AssetLoader): metadata["objects"] = objects metadata["materials"] = materials - metadata["parent"] = str(context["representation"]["parent"]) + metadata["parent"] = context["representation"]["versionId"] metadata["product_type"] = context["product"]["productType"] nodes = list(container.objects) @@ -140,14 +140,14 @@ class BlendLookLoader(plugin.AssetLoader): def update(self, container: Dict, context: Dict): collection = bpy.data.collections.get(container["objectName"]) - repre_doc = context["representation"] - libpath = Path(get_representation_path(repre_doc)) + repre_entity = context["representation"] + libpath = Path(get_representation_path(repre_entity)) extension = libpath.suffix.lower() self.log.info( "Container: %s\nRepresentation: %s", pformat(container, indent=2), - pformat(repre_doc, indent=2), + pformat(repre_entity, indent=2), ) assert collection, ( @@ -202,7 +202,7 @@ class BlendLookLoader(plugin.AssetLoader): collection_metadata["objects"] = objects collection_metadata["materials"] = materials collection_metadata["libpath"] = str(libpath) - collection_metadata["representation"] = str(repre_doc["_id"]) + collection_metadata["representation"] = repre_entity["id"] def remove(self, container: Dict) -> bool: collection = bpy.data.collections.get(container["objectName"]) diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_layout.py b/client/ayon_core/hosts/blender/plugins/publish/extract_layout.py index 6787ab7916..0679483dd5 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_layout.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_layout.py @@ -5,7 +5,8 @@ import bpy import bpy_extras import bpy_extras.anim_utils -from ayon_core.client import get_representation_by_name +from ayon_api import get_representations + from ayon_core.pipeline import publish from ayon_core.hosts.blender.api import plugin from ayon_core.hosts.blender.api.pipeline import AVALON_PROPERTY @@ -134,6 +135,8 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin): fbx_count = 0 project_name = instance.context.data["projectName"] + version_ids = set() + filtered_assets = [] for asset in asset_group.children: metadata = asset.get(AVALON_PROPERTY) if not metadata: @@ -146,42 +149,47 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin): ) continue + filtered_assets.append((asset, metadata)) + version_ids.add(metadata["parent"]) + + repre_entities = get_representations( + project_name, + representation_names={"blend", "fbx", "abc"}, + version_ids=version_ids, + fields={"id", "versionId", "name"} + ) + repre_mapping_by_version_id = { + version_id: {} + for version_id in version_ids + } + for repre_entity in repre_entities: + version_id = repre_entity["versionId"] + repre_mapping_by_version_id[version_id][repre_entity["name"]] = ( + repre_entity + ) + + for asset, metadata in filtered_assets: version_id = metadata["parent"] product_type = metadata.get("product_type") if product_type is None: product_type = metadata["family"] + repres_by_name = repre_mapping_by_version_id[version_id] + self.log.debug("Parent: {}".format(version_id)) - # Get blend reference - blend = get_representation_by_name( - project_name, "blend", version_id, fields=["_id"] - ) - blend_id = None - if blend: - blend_id = blend["_id"] - # Get fbx reference - fbx = get_representation_by_name( - project_name, "fbx", version_id, fields=["_id"] - ) - fbx_id = None - if fbx: - fbx_id = fbx["_id"] - # Get abc reference - abc = get_representation_by_name( - project_name, "abc", version_id, fields=["_id"] - ) - abc_id = None - if abc: - abc_id = abc["_id"] - - json_element = {} - if blend_id: - json_element["reference"] = str(blend_id) - if fbx_id: - json_element["reference_fbx"] = str(fbx_id) - if abc_id: - json_element["reference_abc"] = str(abc_id) - + # Get blend, fbx and abc reference + blend_id = repres_by_name.get("blend", {}).get("id") + fbx_id = repres_by_name.get("fbx", {}).get("id") + abc_id = repres_by_name.get("abc", {}).get("id") + json_element = { + key: value + for key, value in ( + ("reference", blend_id), + ("reference_fbx", fbx_id), + ("reference_abc", abc_id), + ) + if value + } json_element["product_type"] = product_type json_element["instance_name"] = asset.name json_element["asset_name"] = metadata["asset_name"] diff --git a/client/ayon_core/hosts/blender/plugins/publish/integrate_animation.py b/client/ayon_core/hosts/blender/plugins/publish/integrate_animation.py index a10144ebf5..5d3a1dac93 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/integrate_animation.py +++ b/client/ayon_core/hosts/blender/plugins/publish/integrate_animation.py @@ -44,7 +44,7 @@ class IntegrateAnimation( break if not rep: continue - obj_id = rep["representation"]["_id"] + obj_id = rep["representation"]["id"] if obj_id: json_dict["representation_id"] = str(obj_id) From 7b19e06787dc00681369126136e0cc76d7fa4d82 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 8 Mar 2024 19:37:03 +0800 Subject: [PATCH 476/573] removing remove instances --- .../hosts/max/plugins/create/create_workfile.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/create/create_workfile.py b/client/ayon_core/hosts/max/plugins/create/create_workfile.py index 1552149413..058fe10eb2 100644 --- a/client/ayon_core/hosts/max/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/max/plugins/create/create_workfile.py @@ -98,21 +98,6 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): created_inst.data_to_store() ) - def remove_instances(self, instances): - """Remove specified instance from the scene. - - This is only removing `id` parameter so instance is no longer - instance, because it might contain valuable data for artist. - - """ - for instance in instances: - instance_node = rt.GetNodeByName( - instance.data.get("instance_node")) - if instance_node: - rt.Delete(instance_node) - - self._remove_instance_from_context(instance) - def create_node(self, product_name): if rt.getNodeByName(product_name): node = rt.getNodeByName(product_name) From 86b89c77aed6dd9d19e2305522b9a22e4c5d740a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 12:37:38 +0100 Subject: [PATCH 477/573] AE use representation entity --- client/ayon_core/hosts/aftereffects/api/pipeline.py | 2 +- .../hosts/aftereffects/plugins/load/load_background.py | 6 +++--- .../hosts/aftereffects/plugins/load/load_file.py | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/hosts/aftereffects/api/pipeline.py b/client/ayon_core/hosts/aftereffects/api/pipeline.py index 7ed244fd1d..105fee64b9 100644 --- a/client/ayon_core/hosts/aftereffects/api/pipeline.py +++ b/client/ayon_core/hosts/aftereffects/api/pipeline.py @@ -271,7 +271,7 @@ def containerise(name, "name": name, "namespace": namespace, "loader": str(loader), - "representation": str(context["representation"]["_id"]), + "representation": context["representation"]["id"], "members": comp.members or [comp.id] } diff --git a/client/ayon_core/hosts/aftereffects/plugins/load/load_background.py b/client/ayon_core/hosts/aftereffects/plugins/load/load_background.py index 4e608efef8..e45de5524e 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/load/load_background.py +++ b/client/ayon_core/hosts/aftereffects/plugins/load/load_background.py @@ -61,7 +61,7 @@ class BackgroundLoader(api.AfterEffectsLoader): stub = self.get_stub() folder_name = context["folder"]["name"] product_name = context["product"]["name"] - repre_doc = context["representation"] + repre_entity = context["representation"] _ = container.pop("layer") @@ -80,7 +80,7 @@ class BackgroundLoader(api.AfterEffectsLoader): else: # switching version - keep same name comp_name = container["namespace"] - path = get_representation_path(repre_doc) + path = get_representation_path(repre_entity) layers = get_background_layers(path) comp = stub.reload_background(container["members"][1], @@ -88,7 +88,7 @@ class BackgroundLoader(api.AfterEffectsLoader): layers) # update container - container["representation"] = str(repre_doc["_id"]) + container["representation"] = repre_entity["id"] container["name"] = product_name container["namespace"] = comp_name container["members"] = comp.members diff --git a/client/ayon_core/hosts/aftereffects/plugins/load/load_file.py b/client/ayon_core/hosts/aftereffects/plugins/load/load_file.py index 4d376f058b..29536945dd 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/load/load_file.py +++ b/client/ayon_core/hosts/aftereffects/plugins/load/load_file.py @@ -38,7 +38,7 @@ class FileLoader(api.AfterEffectsLoader): import_options['sequence'] = True if not path: - repr_id = context["representation"]["_id"] + repr_id = context["representation"]["id"] self.log.warning( "Representation id `{}` is failing to load".format(repr_id)) return @@ -74,7 +74,7 @@ class FileLoader(api.AfterEffectsLoader): folder_name = context["folder"]["name"] product_name = context["product"]["name"] - repre_doc = context["representation"] + repre_entity = context["representation"] namespace_from_container = re.sub(r'_\d{3}$', '', container["namespace"]) @@ -88,11 +88,11 @@ class FileLoader(api.AfterEffectsLoader): "{}_{}".format(folder_name, product_name)) else: # switching version - keep same name layer_name = container["namespace"] - path = get_representation_path(repre_doc) + path = get_representation_path(repre_entity) # with aftereffects.maintained_selection(): # TODO stub.replace_item(layer.id, path, stub.LOADED_ICON + layer_name) stub.imprint( - layer.id, {"representation": str(repre_doc["_id"]), + layer.id, {"representation": repre_entity["id"], "name": product_name, "namespace": layer_name} ) From bd594e677073b6427674488ba65b87d1207b9913 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 12:37:52 +0100 Subject: [PATCH 478/573] flam use representation entity --- client/ayon_core/hosts/flame/api/pipeline.py | 2 +- client/ayon_core/hosts/flame/api/plugin.py | 2 +- client/ayon_core/hosts/flame/plugins/load/load_clip.py | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/hosts/flame/api/pipeline.py b/client/ayon_core/hosts/flame/api/pipeline.py index 532f89b5e9..a902b9ee73 100644 --- a/client/ayon_core/hosts/flame/api/pipeline.py +++ b/client/ayon_core/hosts/flame/api/pipeline.py @@ -73,7 +73,7 @@ def containerise(flame_clip_segment, "name": str(name), "namespace": str(namespace), "loader": str(loader), - "representation": str(context["representation"]["_id"]), + "representation": context["representation"]["id"], } if data: diff --git a/client/ayon_core/hosts/flame/api/plugin.py b/client/ayon_core/hosts/flame/api/plugin.py index b00ff636ed..c5667eb75a 100644 --- a/client/ayon_core/hosts/flame/api/plugin.py +++ b/client/ayon_core/hosts/flame/api/plugin.py @@ -757,7 +757,7 @@ class ClipLoader(LoaderPlugin): or colorspace == "Unknown" ): colorspace = context["representation"]["data"].get( - "colorspace", None) + "colorspace") return colorspace diff --git a/client/ayon_core/hosts/flame/plugins/load/load_clip.py b/client/ayon_core/hosts/flame/plugins/load/load_clip.py index 80b2eb9ec7..faa2b7145d 100644 --- a/client/ayon_core/hosts/flame/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/flame/plugins/load/load_clip.py @@ -186,20 +186,20 @@ class LoadClip(opfapi.ClipLoader): # """ Updating previously loaded clips # """ # # load clip to timeline and get main variables - # repre_doc = context['representation'] + # repre_entity = context['representation'] # name = container['name'] # namespace = container['namespace'] # track_item = phiero.get_track_items( # track_item_name=namespace) # version = io.find_one({ # "type": "version", - # "_id": repre_doc["parent"] + # "id": repre_entity["versionId"] # }) # version_data = version.get("data", {}) # version_name = version.get("name", None) # colorspace = version_data.get("colorSpace", None) # object_name = "{}_{}".format(name, namespace) - # file = get_representation_path(repre_doc).replace("\\", "/") + # file = get_representation_path(repre_entity).replace("\\", "/") # clip = track_item.source() # # reconnect media to new path @@ -224,7 +224,7 @@ class LoadClip(opfapi.ClipLoader): # # add variables related to version context # data_imprint.update({ - # "representation": str(repre_doc["_id"]), + # "representation": repre_entity["id"], # "version": version_name, # "colorspace": colorspace, # "objectName": object_name From 43c09219b91600b7969ce2cf181fc9f9f7f82d4a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 12:38:16 +0100 Subject: [PATCH 479/573] fusion use representation entity --- client/ayon_core/hosts/fusion/api/pipeline.py | 2 +- client/ayon_core/hosts/fusion/plugins/load/load_alembic.py | 6 +++--- client/ayon_core/hosts/fusion/plugins/load/load_fbx.py | 6 +++--- client/ayon_core/hosts/fusion/plugins/load/load_sequence.py | 6 ++---- client/ayon_core/hosts/fusion/plugins/load/load_usd.py | 6 +++--- 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/client/ayon_core/hosts/fusion/api/pipeline.py b/client/ayon_core/hosts/fusion/api/pipeline.py index 0e9e0724c7..3bb66619a9 100644 --- a/client/ayon_core/hosts/fusion/api/pipeline.py +++ b/client/ayon_core/hosts/fusion/api/pipeline.py @@ -252,7 +252,7 @@ def imprint_container(tool, ("name", str(name)), ("namespace", str(namespace)), ("loader", str(loader)), - ("representation", str(context["representation"]["_id"])), + ("representation", context["representation"]["id"]), ] for key, value in data: diff --git a/client/ayon_core/hosts/fusion/plugins/load/load_alembic.py b/client/ayon_core/hosts/fusion/plugins/load/load_alembic.py index b0c45ad0e9..d78a8b0ba0 100644 --- a/client/ayon_core/hosts/fusion/plugins/load/load_alembic.py +++ b/client/ayon_core/hosts/fusion/plugins/load/load_alembic.py @@ -54,14 +54,14 @@ class FusionLoadAlembicMesh(load.LoaderPlugin): assert tool.ID == self.tool_type, f"Must be {self.tool_type}" comp = tool.Comp() - repre_doc = context["representation"] - path = get_representation_path(repre_doc) + repre_entity = context["representation"] + path = get_representation_path(repre_entity) with comp_lock_and_undo_chunk(comp, "Update tool"): tool["Filename"] = path # Update the imprinted representation - tool.SetData("avalon.representation", str(repre_doc["_id"])) + tool.SetData("avalon.representation", repre_entity["id"]) def remove(self, container): tool = container["_tool"] diff --git a/client/ayon_core/hosts/fusion/plugins/load/load_fbx.py b/client/ayon_core/hosts/fusion/plugins/load/load_fbx.py index 04fe90f72e..d2e1885b0c 100644 --- a/client/ayon_core/hosts/fusion/plugins/load/load_fbx.py +++ b/client/ayon_core/hosts/fusion/plugins/load/load_fbx.py @@ -69,14 +69,14 @@ class FusionLoadFBXMesh(load.LoaderPlugin): assert tool.ID == self.tool_type, f"Must be {self.tool_type}" comp = tool.Comp() - repre_doc = context["representation"] - path = get_representation_path(repre_doc) + repre_entity = context["representation"] + path = get_representation_path(repre_entity) with comp_lock_and_undo_chunk(comp, "Update tool"): tool["ImportFile"] = path # Update the imprinted representation - tool.SetData("avalon.representation", str(repre_doc["_id"])) + tool.SetData("avalon.representation", repre_entity["id"]) def remove(self, container): tool = container["_tool"] diff --git a/client/ayon_core/hosts/fusion/plugins/load/load_sequence.py b/client/ayon_core/hosts/fusion/plugins/load/load_sequence.py index f53553ad1a..dfd7e4231b 100644 --- a/client/ayon_core/hosts/fusion/plugins/load/load_sequence.py +++ b/client/ayon_core/hosts/fusion/plugins/load/load_sequence.py @@ -1,7 +1,6 @@ import contextlib import ayon_core.pipeline.load as load -from ayon_core.pipeline.load import get_representation_context from ayon_core.hosts.fusion.api import ( imprint_container, get_current_comp, @@ -224,8 +223,7 @@ class FusionLoadSequence(load.LoaderPlugin): assert tool.ID == "Loader", "Must be Loader" comp = tool.Comp() - repre_doc = context["representation"] - context = get_representation_context(repre_doc) + repre_entity = context["representation"] path = self.filepath_from_context(context) # Get start frame from version data @@ -256,7 +254,7 @@ class FusionLoadSequence(load.LoaderPlugin): ) # Update the imprinted representation - tool.SetData("avalon.representation", str(repre_doc["_id"])) + tool.SetData("avalon.representation", repre_entity["id"]) def remove(self, container): tool = container["_tool"] diff --git a/client/ayon_core/hosts/fusion/plugins/load/load_usd.py b/client/ayon_core/hosts/fusion/plugins/load/load_usd.py index 16af9505d5..a609af6197 100644 --- a/client/ayon_core/hosts/fusion/plugins/load/load_usd.py +++ b/client/ayon_core/hosts/fusion/plugins/load/load_usd.py @@ -69,14 +69,14 @@ class FusionLoadUSD(load.LoaderPlugin): assert tool.ID == self.tool_type, f"Must be {self.tool_type}" comp = tool.Comp() - repre_doc = context["representation"] - path = get_representation_path(repre_doc) + repre_entity = context["representation"] + path = get_representation_path(repre_entity) with comp_lock_and_undo_chunk(comp, "Update tool"): tool["Filename"] = path # Update the imprinted representation - tool.SetData("avalon.representation", str(repre_doc["_id"])) + tool.SetData("avalon.representation", repre_entity["id"]) def remove(self, container): tool = container["_tool"] From 67572d56c0f57f4bc9b8fa0eb8b3a62a82483f20 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 12:38:33 +0100 Subject: [PATCH 480/573] harmony use representation entity --- client/ayon_core/hosts/harmony/api/README.md | 6 +++--- client/ayon_core/hosts/harmony/api/pipeline.py | 2 +- .../hosts/harmony/plugins/load/load_background.py | 8 ++++---- .../hosts/harmony/plugins/load/load_imagesequence.py | 8 ++++---- .../ayon_core/hosts/harmony/plugins/load/load_palette.py | 8 ++++---- .../ayon_core/hosts/harmony/plugins/load/load_template.py | 6 +++--- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/client/ayon_core/hosts/harmony/api/README.md b/client/ayon_core/hosts/harmony/api/README.md index 055381928c..b9c21e1882 100644 --- a/client/ayon_core/hosts/harmony/api/README.md +++ b/client/ayon_core/hosts/harmony/api/README.md @@ -614,9 +614,9 @@ class ImageSequenceLoader(load.LoaderPlugin): def update(self, container, context): node = container.pop("node") - repre_doc = context["representation"] + repre_entity = context["representation"] project_name = get_current_project_name() - version = get_version_by_id(project_name, repre_doc["parent"]) + version = get_version_by_id(project_name, repre_entity["versionId"]) files = [] for f in version["data"]["files"]: files.append( @@ -633,7 +633,7 @@ class ImageSequenceLoader(load.LoaderPlugin): ) harmony.imprint( - node, {"representation": str(repre_doc["_id"])} + node, {"representation": repre_entity["id"]} ) def remove(self, container): diff --git a/client/ayon_core/hosts/harmony/api/pipeline.py b/client/ayon_core/hosts/harmony/api/pipeline.py index aaff5dc019..a753a32ebb 100644 --- a/client/ayon_core/hosts/harmony/api/pipeline.py +++ b/client/ayon_core/hosts/harmony/api/pipeline.py @@ -337,7 +337,7 @@ def containerise(name, "name": name, "namespace": namespace, "loader": str(loader), - "representation": str(context["representation"]["_id"]), + "representation": context["representation"]["id"], "nodes": nodes } diff --git a/client/ayon_core/hosts/harmony/plugins/load/load_background.py b/client/ayon_core/hosts/harmony/plugins/load/load_background.py index 64d5d7ad0d..72b26c826a 100644 --- a/client/ayon_core/hosts/harmony/plugins/load/load_background.py +++ b/client/ayon_core/hosts/harmony/plugins/load/load_background.py @@ -281,8 +281,8 @@ class BackgroundLoader(load.LoaderPlugin): ) def update(self, container, context): - repre_doc = context["representation"] - path = get_representation_path(repre_doc) + repre_entity = context["representation"] + path = get_representation_path(repre_entity) with open(path) as json_file: data = json.load(json_file) @@ -302,7 +302,7 @@ class BackgroundLoader(load.LoaderPlugin): print(container) - is_latest = is_representation_from_latest(repre_doc) + is_latest = is_representation_from_latest(repre_entity) for layer in sorted(layers): file_to_import = [ os.path.join(bg_folder, layer).replace("\\", "/") @@ -354,7 +354,7 @@ class BackgroundLoader(load.LoaderPlugin): harmony.imprint( container['name'], { - "representation": str(repre_doc["_id"]), + "representation": repre_entity["id"], "nodes": container["nodes"] } ) diff --git a/client/ayon_core/hosts/harmony/plugins/load/load_imagesequence.py b/client/ayon_core/hosts/harmony/plugins/load/load_imagesequence.py index de5a196bf5..0fbcd03c92 100644 --- a/client/ayon_core/hosts/harmony/plugins/load/load_imagesequence.py +++ b/client/ayon_core/hosts/harmony/plugins/load/load_imagesequence.py @@ -83,8 +83,8 @@ class ImageSequenceLoader(load.LoaderPlugin): self_name = self.__class__.__name__ node = container.get("nodes").pop() - repre_doc = context["representation"] - path = get_representation_path(repre_doc) + repre_entity = context["representation"] + path = get_representation_path(repre_entity) collections, remainder = clique.assemble( os.listdir(os.path.dirname(path)) ) @@ -111,7 +111,7 @@ class ImageSequenceLoader(load.LoaderPlugin): ) # Colour node. - if is_representation_from_latest(repre_doc): + if is_representation_from_latest(repre_entity): harmony.send( { "function": "PypeHarmony.setColor", @@ -125,7 +125,7 @@ class ImageSequenceLoader(load.LoaderPlugin): }) harmony.imprint( - node, {"representation": str(repre_doc["_id"])} + node, {"representation": repre_entity["id"]} ) def remove(self, container): diff --git a/client/ayon_core/hosts/harmony/plugins/load/load_palette.py b/client/ayon_core/hosts/harmony/plugins/load/load_palette.py index 7ef15fad52..79ae2fb154 100644 --- a/client/ayon_core/hosts/harmony/plugins/load/load_palette.py +++ b/client/ayon_core/hosts/harmony/plugins/load/load_palette.py @@ -28,14 +28,14 @@ class ImportPaletteLoader(load.LoaderPlugin): def load_palette(self, context): product_name = context["product"]["name"] - repre_doc = context["representation"] + repre_entity = context["representation"] name = product_name.replace("palette", "") # Overwrite palette on disk. scene_path = harmony.send( {"function": "scene.currentProjectPath"} )["result"] - src = get_representation_path(repre_doc) + src = get_representation_path(repre_entity) dst = os.path.join( scene_path, "palette-library", @@ -67,7 +67,7 @@ class ImportPaletteLoader(load.LoaderPlugin): self.remove(container) name = self.load_palette(context) - repre_doc = context["representation"] - container["representation"] = str(repre_doc["_id"]) + repre_entity = context["representation"] + container["representation"] = repre_entity["id"] container["name"] = name harmony.imprint(name, container) diff --git a/client/ayon_core/hosts/harmony/plugins/load/load_template.py b/client/ayon_core/hosts/harmony/plugins/load/load_template.py index 82bc53a3d5..2d9af362eb 100644 --- a/client/ayon_core/hosts/harmony/plugins/load/load_template.py +++ b/client/ayon_core/hosts/harmony/plugins/load/load_template.py @@ -82,8 +82,8 @@ class TemplateLoader(load.LoaderPlugin): node = harmony.find_node_by_name(node_name, "GROUP") self_name = self.__class__.__name__ - repre_doc = context["representation"] - if is_representation_from_latest(repre_doc): + repre_entity = context["representation"] + if is_representation_from_latest(repre_entity): self._set_green(node) else: self._set_red(node) @@ -111,7 +111,7 @@ class TemplateLoader(load.LoaderPlugin): None, container["data"]) harmony.imprint( - node, {"representation": str(repre_doc["_id"])} + node, {"representation": repre_entity["id"]} ) def remove(self, container): From ff20964dc061f5384eb2a28b3ede6906d2543fc0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 12:38:46 +0100 Subject: [PATCH 481/573] hiero use representation entity --- client/ayon_core/hosts/hiero/api/pipeline.py | 2 +- client/ayon_core/hosts/hiero/api/plugin.py | 2 +- client/ayon_core/hosts/hiero/plugins/load/load_clip.py | 7 ++++--- client/ayon_core/hosts/hiero/plugins/load/load_effects.py | 8 ++++---- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/hosts/hiero/api/pipeline.py b/client/ayon_core/hosts/hiero/api/pipeline.py index a9ba2e4df3..327a4ae29c 100644 --- a/client/ayon_core/hosts/hiero/api/pipeline.py +++ b/client/ayon_core/hosts/hiero/api/pipeline.py @@ -101,7 +101,7 @@ def containerise(track_item, "name": str(name), "namespace": str(namespace), "loader": str(loader), - "representation": str(context["representation"]["_id"]), + "representation": context["representation"]["id"], }) if data: diff --git a/client/ayon_core/hosts/hiero/api/plugin.py b/client/ayon_core/hosts/hiero/api/plugin.py index cf3b4dadaf..6a665dc9c5 100644 --- a/client/ayon_core/hosts/hiero/api/plugin.py +++ b/client/ayon_core/hosts/hiero/api/plugin.py @@ -458,7 +458,7 @@ class ClipLoader: # gets file path file = get_representation_path_from_context(self.context) if not file: - repr_id = repr["_id"] + repr_id = repr["id"] log.warning( "Representation id `{}` is failing to load".format(repr_id)) return None diff --git a/client/ayon_core/hosts/hiero/plugins/load/load_clip.py b/client/ayon_core/hosts/hiero/plugins/load/load_clip.py index b35c5b2f1f..c2ff907650 100644 --- a/client/ayon_core/hosts/hiero/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/hiero/plugins/load/load_clip.py @@ -150,7 +150,7 @@ class LoadClip(phiero.SequenceLoader): """ Updating previously loaded clips """ version_entity = context["version"] - repre_doc = context["representation"] + repre_entity = context["representation"] # load clip to timeline and get main variables name = container["name"] @@ -162,7 +162,8 @@ class LoadClip(phiero.SequenceLoader): version_name = version_entity["version"] colorspace = version_attributes.get("colorSpace") object_name = "{}_{}".format(name, namespace) - file = get_representation_path(repre_doc).replace("\\", "/") + + file = get_representation_path(repre_entity).replace("\\", "/") clip = track_item.source() # reconnect media to new path @@ -191,7 +192,7 @@ class LoadClip(phiero.SequenceLoader): # add variables related to version context data_imprint.update({ - "representation": str(repre_doc["_id"]), + "representation": repre_entity["id"], "version": version_name, "colorspace": colorspace, "objectName": object_name diff --git a/client/ayon_core/hosts/hiero/plugins/load/load_effects.py b/client/ayon_core/hosts/hiero/plugins/load/load_effects.py index 90666406fd..521a7c4494 100644 --- a/client/ayon_core/hosts/hiero/plugins/load/load_effects.py +++ b/client/ayon_core/hosts/hiero/plugins/load/load_effects.py @@ -156,9 +156,9 @@ class LoadEffects(load.LoaderPlugin): """ Updating previously loaded effects """ version_entity = context["version"] - repre_doc = context["representation"] + repre_entity = context["representation"] active_track = container["_item"] - file = get_representation_path(repre_doc).replace("\\", "/") + file = get_representation_path(repre_entity).replace("\\", "/") # get main variables name = container['name'] @@ -192,7 +192,7 @@ class LoadEffects(load.LoaderPlugin): data_imprint = { "objectName": object_name, "name": name, - "representation": str(repre_doc["_id"]), + "representation": repre_entity["id"], "children_names": [] } @@ -293,7 +293,7 @@ class LoadEffects(load.LoaderPlugin): "name": str(name), "namespace": str(namespace), "loader": str(loader), - "representation": str(context["representation"]["_id"]), + "representation": context["representation"]["id"], } } From 630966e5b129445b99a003417fa4b69e8bd68cf9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 12:39:20 +0100 Subject: [PATCH 482/573] houdini use representation entity --- client/ayon_core/hosts/houdini/api/pipeline.py | 2 +- .../ayon_core/hosts/houdini/plugins/load/load_alembic.py | 6 +++--- .../hosts/houdini/plugins/load/load_alembic_archive.py | 6 +++--- client/ayon_core/hosts/houdini/plugins/load/load_ass.py | 6 +++--- client/ayon_core/hosts/houdini/plugins/load/load_bgeo.py | 8 ++++---- .../ayon_core/hosts/houdini/plugins/load/load_camera.py | 6 +++--- client/ayon_core/hosts/houdini/plugins/load/load_fbx.py | 6 +++--- client/ayon_core/hosts/houdini/plugins/load/load_hda.py | 6 +++--- client/ayon_core/hosts/houdini/plugins/load/load_image.py | 8 ++++---- .../hosts/houdini/plugins/load/load_redshift_proxy.py | 8 ++++---- .../hosts/houdini/plugins/load/load_usd_layer.py | 8 ++++---- .../hosts/houdini/plugins/load/load_usd_reference.py | 8 ++++---- client/ayon_core/hosts/houdini/plugins/load/load_vdb.py | 8 ++++---- .../hosts/houdini/plugins/publish/extract_usd_layered.py | 5 ++--- 14 files changed, 45 insertions(+), 46 deletions(-) diff --git a/client/ayon_core/hosts/houdini/api/pipeline.py b/client/ayon_core/hosts/houdini/api/pipeline.py index 4b0580c5af..d5144200cf 100644 --- a/client/ayon_core/hosts/houdini/api/pipeline.py +++ b/client/ayon_core/hosts/houdini/api/pipeline.py @@ -235,7 +235,7 @@ def containerise(name, "name": name, "namespace": namespace, "loader": str(loader), - "representation": str(context["representation"]["_id"]), + "representation": context["representation"]["id"], } lib.imprint(container, data) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_alembic.py b/client/ayon_core/hosts/houdini/plugins/load/load_alembic.py index 9ee72a270c..5235923b4c 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_alembic.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_alembic.py @@ -82,7 +82,7 @@ class AbcLoader(load.LoaderPlugin): ) def update(self, container, context): - repre_doc = context["representation"] + repre_entity = context["representation"] node = container["node"] try: alembic_node = next( @@ -93,13 +93,13 @@ class AbcLoader(load.LoaderPlugin): return # Update the file path - file_path = get_representation_path(repre_doc) + file_path = get_representation_path(repre_entity) file_path = file_path.replace("\\", "/") alembic_node.setParms({"fileName": file_path}) # Update attribute - node.setParms({"representation": str(repre_doc["_id"])}) + node.setParms({"representation": repre_entity["id"]}) def remove(self, container): diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_alembic_archive.py b/client/ayon_core/hosts/houdini/plugins/load/load_alembic_archive.py index bc65863a76..6585df3f17 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_alembic_archive.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_alembic_archive.py @@ -56,16 +56,16 @@ class AbcArchiveLoader(load.LoaderPlugin): suffix="") def update(self, container, context): - repre_doc = context["representation"] + repre_entity = context["representation"] node = container["node"] # Update the file path - file_path = get_representation_path(repre_doc) + file_path = get_representation_path(repre_entity) file_path = file_path.replace("\\", "/") # Update attributes node.setParms({"fileName": file_path, - "representation": str(repre_doc["_id"])}) + "representation": repre_entity["id"]}) # Rebuild node.parm("buildHierarchy").pressButton() diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_ass.py b/client/ayon_core/hosts/houdini/plugins/load/load_ass.py index b687b07199..628b5b2f34 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_ass.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_ass.py @@ -50,12 +50,12 @@ class AssLoader(load.LoaderPlugin): def update(self, container, context): # Update the file path - repre_doc = context["representation"] + repre_entity = context["representation"] procedural = container["node"] - procedural.setParms({"ar_filename": self.format_path(repre_doc)}) + procedural.setParms({"ar_filename": self.format_path(repre_entity)}) # Update attribute - procedural.setParms({"representation": str(repre_doc["_id"])}) + procedural.setParms({"representation": repre_entity["id"]}) def remove(self, container): node = container["node"] diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_bgeo.py b/client/ayon_core/hosts/houdini/plugins/load/load_bgeo.py index 246d1b3524..f02067db75 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_bgeo.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_bgeo.py @@ -83,7 +83,7 @@ class BgeoLoader(load.LoaderPlugin): return filename def update(self, container, context): - repre_doc = context["representation"] + repre_entity = context["representation"] node = container["node"] try: file_node = next( @@ -94,13 +94,13 @@ class BgeoLoader(load.LoaderPlugin): return # Update the file path - file_path = get_representation_path(repre_doc) - file_path = self.format_path(file_path, repre_doc) + file_path = get_representation_path(repre_entity) + file_path = self.format_path(file_path, repre_entity) file_node.setParms({"file": file_path}) # Update attribute - node.setParms({"representation": str(repre_doc["_id"])}) + node.setParms({"representation": repre_entity["id"]}) def remove(self, container): diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_camera.py b/client/ayon_core/hosts/houdini/plugins/load/load_camera.py index 63b50cd6fe..50fc7f4eb6 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_camera.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_camera.py @@ -133,16 +133,16 @@ class CameraLoader(load.LoaderPlugin): suffix="") def update(self, container, context): - repre_doc = context["representation"] + repre_entity = context["representation"] node = container["node"] # Update the file path - file_path = get_representation_path(repre_doc) + file_path = get_representation_path(repre_entity) file_path = file_path.replace("\\", "/") # Update attributes node.setParms({"fileName": file_path, - "representation": str(repre_doc["_id"])}) + "representation": repre_entity["id"]}) # Store the cam temporarily next to the Alembic Archive # so that we can preserve parm values the user set on it diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_fbx.py b/client/ayon_core/hosts/houdini/plugins/load/load_fbx.py index 9ce7c79998..2ebbed5e12 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_fbx.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_fbx.py @@ -48,7 +48,7 @@ class FbxLoader(load.LoaderPlugin): return containerised_nodes def update(self, container, context): - repre_doc = context["representation"] + repre_entity = context["representation"] node = container["node"] try: file_node = next( @@ -59,13 +59,13 @@ class FbxLoader(load.LoaderPlugin): return # Update the file path from representation - file_path = get_representation_path(repre_doc) + file_path = get_representation_path(repre_entity) file_path = file_path.replace("\\", "/") file_node.setParms({"file": file_path}) # Update attribute - node.setParms({"representation": str(repre_doc["_id"])}) + node.setParms({"representation": repre_entity["id"]}) def remove(self, container): diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_hda.py b/client/ayon_core/hosts/houdini/plugins/load/load_hda.py index 0f7d30977a..07949fd177 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_hda.py @@ -51,9 +51,9 @@ class HdaLoader(load.LoaderPlugin): def update(self, container, context): import hou - repre_doc = context["representation"] + repre_entity = context["representation"] hda_node = container["node"] - file_path = get_representation_path(repre_doc) + file_path = get_representation_path(repre_entity) file_path = file_path.replace("\\", "/") hou.hda.installFile(file_path) defs = hda_node.type().allInstalledDefinitions() @@ -61,7 +61,7 @@ class HdaLoader(load.LoaderPlugin): new = def_paths.index(file_path) defs[new].setIsPreferred(True) hda_node.setParms({ - "representation": str(repre_doc["_id"]) + "representation": repre_entity["id"] }) def remove(self, container): diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_image.py b/client/ayon_core/hosts/houdini/plugins/load/load_image.py index 2cc8d0df18..cc9dcc30c8 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_image.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_image.py @@ -79,7 +79,7 @@ class ImageLoader(load.LoaderPlugin): "name": node_name, "namespace": namespace, "loader": str(self.__class__.__name__), - "representation": str(context["representation"]["_id"]), + "representation": context["representation"]["id"], } # todo: add folder="Avalon" @@ -88,11 +88,11 @@ class ImageLoader(load.LoaderPlugin): return node def update(self, container, context): - repre_doc = context["representation"] + repre_entity = context["representation"] node = container["node"] # Update the file path - file_path = get_representation_path(repre_doc) + file_path = get_representation_path(repre_entity) file_path = file_path.replace("\\", "/") file_path = self._get_file_sequence(file_path) @@ -100,7 +100,7 @@ class ImageLoader(load.LoaderPlugin): node.setParms( { "filename1": file_path, - "representation": str(repre_doc["_id"]), + "representation": repre_entity["id"], } ) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_redshift_proxy.py b/client/ayon_core/hosts/houdini/plugins/load/load_redshift_proxy.py index 479c5bb300..86c7ae0272 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_redshift_proxy.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_redshift_proxy.py @@ -73,18 +73,18 @@ class RedshiftProxyLoader(load.LoaderPlugin): ) def update(self, container, context): - repre_doc = context["representation"] + repre_entity = context["representation"] # Update the file path - file_path = get_representation_path(repre_doc) + file_path = get_representation_path(repre_entity) node = container["node"] node.setParms({ "RS_objprop_proxy_file": self.format_path( - file_path, repre_doc) + file_path, repre_entity) }) # Update attribute - node.setParms({"representation": str(repre_doc["_id"])}) + node.setParms({"representation": repre_entity["id"]}) def remove(self, container): diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_usd_layer.py b/client/ayon_core/hosts/houdini/plugins/load/load_usd_layer.py index fa3f3683ae..6ee21f87ec 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_usd_layer.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_usd_layer.py @@ -49,7 +49,7 @@ class USDSublayerLoader(load.LoaderPlugin): "name": node_name, "namespace": namespace, "loader": str(self.__class__.__name__), - "representation": str(context["representation"]["_id"]), + "representation": context["representation"]["id"], } # todo: add folder="Avalon" @@ -58,18 +58,18 @@ class USDSublayerLoader(load.LoaderPlugin): return container def update(self, container, context): - repre_doc = context["representation"] + repre_entity = context["representation"] node = container["node"] # Update the file path - file_path = get_representation_path(repre_doc) + file_path = get_representation_path(repre_entity) file_path = file_path.replace("\\", "/") # Update attributes node.setParms( { "filepath1": file_path, - "representation": str(repre_doc["_id"]), + "representation": repre_entity["id"], } ) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_usd_reference.py b/client/ayon_core/hosts/houdini/plugins/load/load_usd_reference.py index 38540b54c2..d0421083c6 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_usd_reference.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_usd_reference.py @@ -49,7 +49,7 @@ class USDReferenceLoader(load.LoaderPlugin): "name": node_name, "namespace": namespace, "loader": str(self.__class__.__name__), - "representation": str(context["representation"]["_id"]), + "representation": context["representation"]["id"], } # todo: add folder="Avalon" @@ -58,18 +58,18 @@ class USDReferenceLoader(load.LoaderPlugin): return container def update(self, container, context): - repre_doc = context["representation"] + repre_entity = context["representation"] node = container["node"] # Update the file path - file_path = get_representation_path(repre_doc) + file_path = get_representation_path(repre_entity) file_path = file_path.replace("\\", "/") # Update attributes node.setParms( { "filepath1": file_path, - "representation": str(repre_doc["_id"]), + "representation": repre_entity["id"], } ) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_vdb.py b/client/ayon_core/hosts/houdini/plugins/load/load_vdb.py index 8af458aeb0..7b2803ab5d 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_vdb.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_vdb.py @@ -80,7 +80,7 @@ class VdbLoader(load.LoaderPlugin): return filename def update(self, container, context): - repre_doc = context["representation"] + repre_entity = context["representation"] node = container["node"] try: file_node = next( @@ -91,13 +91,13 @@ class VdbLoader(load.LoaderPlugin): return # Update the file path - file_path = get_representation_path(repre_doc) - file_path = self.format_path(file_path, repre_doc) + file_path = get_representation_path(repre_entity) + file_path = self.format_path(file_path, repre_entity) file_node.setParms({"file": file_path}) # Update attribute - node.setParms({"representation": str(repre_doc["_id"])}) + node.setParms({"representation": repre_entity["id"]}) def remove(self, container): diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py index 33ab7ef43c..2e5c9a892c 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py @@ -1,13 +1,12 @@ import os import contextlib -import hou import sys from collections import deque +import hou import ayon_api import pyblish.api -from ayon_core.client import get_representation_by_name from ayon_core.pipeline import ( get_representation_path, publish, @@ -301,7 +300,7 @@ class ExtractUSDLayered(publish.Extractor): self.log.debug("No existing version..") return False - representation = get_representation_by_name( + representation = ayon_api.get_representation_by_name( project_name, ext.lstrip("."), version_entity["id"] ) if not representation: From 756ab2dda00fabab1485e210ffec4d8b6cd5f0fd Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 12:39:28 +0100 Subject: [PATCH 483/573] fix houdini actions --- .../hosts/houdini/plugins/load/actions.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/load/actions.py b/client/ayon_core/hosts/houdini/plugins/load/actions.py index 2cffa565b1..049869be25 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/actions.py +++ b/client/ayon_core/hosts/houdini/plugins/load/actions.py @@ -26,11 +26,10 @@ class SetFrameRangeLoader(load.LoaderPlugin): import hou - version = context["version"] - version_data = version.get("data", {}) + version_attributes = context["version"]["attrib"] - start = version_data.get("frameStart", None) - end = version_data.get("frameEnd", None) + start = version_attributes.get("frameStart") + end = version_attributes.get("frameEnd") if start is None or end is None: print( @@ -64,11 +63,10 @@ class SetFrameRangeWithHandlesLoader(load.LoaderPlugin): import hou - version = context["version"] - version_data = version.get("data", {}) + version_attributes = context["version"]["attrib"] - start = version_data.get("frameStart", None) - end = version_data.get("frameEnd", None) + start = version_attributes.get("frameStart") + end = version_attributes.get("frameEnd") if start is None or end is None: print( From ef38f9d765a2f0f7dd1038049fb10c28bbe240d0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 12:39:54 +0100 Subject: [PATCH 484/573] 3dsMax is using representation entity --- client/ayon_core/hosts/max/api/pipeline.py | 2 +- client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py | 6 +++--- client/ayon_core/hosts/max/plugins/load/load_max_scene.py | 6 +++--- client/ayon_core/hosts/max/plugins/load/load_model.py | 6 +++--- client/ayon_core/hosts/max/plugins/load/load_model_fbx.py | 6 +++--- client/ayon_core/hosts/max/plugins/load/load_model_obj.py | 6 +++--- client/ayon_core/hosts/max/plugins/load/load_model_usd.py | 6 +++--- client/ayon_core/hosts/max/plugins/load/load_pointcache.py | 6 +++--- .../hosts/max/plugins/load/load_pointcache_ornatrix.py | 6 +++--- client/ayon_core/hosts/max/plugins/load/load_pointcloud.py | 6 +++--- .../ayon_core/hosts/max/plugins/load/load_redshift_proxy.py | 6 +++--- client/ayon_core/hosts/max/plugins/load/load_tycache.py | 6 +++--- 12 files changed, 34 insertions(+), 34 deletions(-) diff --git a/client/ayon_core/hosts/max/api/pipeline.py b/client/ayon_core/hosts/max/api/pipeline.py index 1486f7218d..6fd0a501ff 100644 --- a/client/ayon_core/hosts/max/api/pipeline.py +++ b/client/ayon_core/hosts/max/api/pipeline.py @@ -169,7 +169,7 @@ def containerise(name: str, nodes: list, context, "name": name, "namespace": namespace or "", "loader": loader, - "representation": context["representation"]["_id"], + "representation": context["representation"]["id"], } container_name = f"{namespace}:{name}{suffix}" container = rt.container(name=container_name) diff --git a/client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py b/client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py index d56445c695..664904eb4e 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py +++ b/client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py @@ -54,8 +54,8 @@ class FbxLoader(load.LoaderPlugin): def update(self, container, context): from pymxs import runtime as rt - repre_doc = context["representation"] - path = get_representation_path(repre_doc) + repre_entity = context["representation"] + path = get_representation_path(repre_entity) node_name = container["instance_node"] node = rt.getNodeByName(node_name) namespace, _ = get_namespace(node_name) @@ -88,7 +88,7 @@ class FbxLoader(load.LoaderPlugin): update_custom_attribute_data(node, fbx_objects) lib.imprint(container["instance_node"], { - "representation": str(repre_doc["_id"]) + "representation": repre_entity["id"] }) def switch(self, container, context): diff --git a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py index 39bb3b568d..f68e934d54 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py +++ b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py @@ -51,8 +51,8 @@ class MaxSceneLoader(load.LoaderPlugin): def update(self, container, context): from pymxs import runtime as rt - repre_doc = context["representation"] - path = get_representation_path(repre_doc) + repre_entity = context["representation"] + path = get_representation_path(repre_entity) node_name = container["instance_node"] node = rt.getNodeByName(node_name) namespace, _ = get_namespace(node_name) @@ -87,7 +87,7 @@ class MaxSceneLoader(load.LoaderPlugin): update_custom_attribute_data(node, max_objects) lib.imprint(container["instance_node"], { - "representation": str(repre_doc["_id"]) + "representation": repre_entity["id"] }) def switch(self, container, context): diff --git a/client/ayon_core/hosts/max/plugins/load/load_model.py b/client/ayon_core/hosts/max/plugins/load/load_model.py index e0241bdb73..0c39c1ba0d 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_model.py +++ b/client/ayon_core/hosts/max/plugins/load/load_model.py @@ -73,8 +73,8 @@ class ModelAbcLoader(load.LoaderPlugin): def update(self, container, context): from pymxs import runtime as rt - repre_doc = context["representation"] - path = get_representation_path(repre_doc) + repre_entity = context["representation"] + path = get_representation_path(repre_entity) node = rt.GetNodeByName(container["instance_node"]) node_list = [n for n in get_previous_loaded_object(node) if rt.ClassOf(n) == rt.AlembicContainer] @@ -91,7 +91,7 @@ class ModelAbcLoader(load.LoaderPlugin): abc_obj.source = path lib.imprint( container["instance_node"], - {"representation": str(repre_doc["_id"])}, + {"representation": repre_entity["id"]}, ) def switch(self, container, context): diff --git a/client/ayon_core/hosts/max/plugins/load/load_model_fbx.py b/client/ayon_core/hosts/max/plugins/load/load_model_fbx.py index 03ba901b32..7f5f1255ec 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_model_fbx.py +++ b/client/ayon_core/hosts/max/plugins/load/load_model_fbx.py @@ -50,8 +50,8 @@ class FbxModelLoader(load.LoaderPlugin): def update(self, container, context): from pymxs import runtime as rt - repre_doc = context["representation"] - path = get_representation_path(repre_doc) + repre_entity = context["representation"] + path = get_representation_path(repre_entity) node_name = container["instance_node"] node = rt.getNodeByName(node_name) if not node: @@ -86,7 +86,7 @@ class FbxModelLoader(load.LoaderPlugin): rt.Select(node) update_custom_attribute_data(node, fbx_objects) lib.imprint(container["instance_node"], { - "representation": str(repre_doc["_id"]) + "representation": repre_entity["id"] }) def switch(self, container, context): diff --git a/client/ayon_core/hosts/max/plugins/load/load_model_obj.py b/client/ayon_core/hosts/max/plugins/load/load_model_obj.py index a6c3d2a2fe..4b8d260921 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_model_obj.py +++ b/client/ayon_core/hosts/max/plugins/load/load_model_obj.py @@ -50,8 +50,8 @@ class ObjLoader(load.LoaderPlugin): def update(self, container, context): from pymxs import runtime as rt - repre_doc = context["representation"] - path = get_representation_path(repre_doc) + repre_entity = context["representation"] + path = get_representation_path(repre_entity) node_name = container["instance_node"] node = rt.getNodeByName(node_name) namespace, _ = get_namespace(node_name) @@ -78,7 +78,7 @@ class ObjLoader(load.LoaderPlugin): rt.Select(node) lib.imprint(node_name, { - "representation": str(repre_doc["_id"]) + "representation": repre_entity["id"] }) def switch(self, container, context): diff --git a/client/ayon_core/hosts/max/plugins/load/load_model_usd.py b/client/ayon_core/hosts/max/plugins/load/load_model_usd.py index 6673a2e48b..6bcff2b6a5 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_model_usd.py +++ b/client/ayon_core/hosts/max/plugins/load/load_model_usd.py @@ -66,8 +66,8 @@ class ModelUSDLoader(load.LoaderPlugin): namespace, loader=self.__class__.__name__) def update(self, container, context): - repre_doc = context["representation"] - path = get_representation_path(repre_doc) + repre_entity = context["representation"] + path = get_representation_path(repre_entity) node_name = container["instance_node"] node = rt.GetNodeByName(node_name) namespace, name = get_namespace(node_name) @@ -108,7 +108,7 @@ class ModelUSDLoader(load.LoaderPlugin): rt.Select(node) lib.imprint(node_name, { - "representation": str(repre_doc["_id"]) + "representation": repre_entity["id"] }) def switch(self, container, context): diff --git a/client/ayon_core/hosts/max/plugins/load/load_pointcache.py b/client/ayon_core/hosts/max/plugins/load/load_pointcache.py index 6f79caea42..613d240447 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_pointcache.py +++ b/client/ayon_core/hosts/max/plugins/load/load_pointcache.py @@ -79,8 +79,8 @@ class AbcLoader(load.LoaderPlugin): def update(self, container, context): from pymxs import runtime as rt - repre_doc = context["representation"] - path = get_representation_path(repre_doc) + repre_entity = context["representation"] + path = get_representation_path(repre_entity) node = rt.GetNodeByName(container["instance_node"]) abc_container = [n for n in get_previous_loaded_object(node) if rt.ClassOf(n) == rt.AlembicContainer] @@ -97,7 +97,7 @@ class AbcLoader(load.LoaderPlugin): abc_obj.source = path lib.imprint( container["instance_node"], - {"representation": str(repre_doc["_id"])}, + {"representation": repre_entity["id"]}, ) def switch(self, container, context): diff --git a/client/ayon_core/hosts/max/plugins/load/load_pointcache_ornatrix.py b/client/ayon_core/hosts/max/plugins/load/load_pointcache_ornatrix.py index 67d1374266..7cf7d162c1 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_pointcache_ornatrix.py +++ b/client/ayon_core/hosts/max/plugins/load/load_pointcache_ornatrix.py @@ -63,8 +63,8 @@ class OxAbcLoader(load.LoaderPlugin): ) def update(self, container, context): - repre_doc = context["representation"] - path = get_representation_path(repre_doc) + repre_entity = context["representation"] + path = get_representation_path(repre_entity) node_name = container["instance_node"] namespace, name = get_namespace(node_name) node = rt.getNodeByName(node_name) @@ -99,7 +99,7 @@ class OxAbcLoader(load.LoaderPlugin): update_custom_attribute_data(node, ox_abc_objects) lib.imprint( container["instance_node"], - {"representation": str(repre_doc["_id"])}, + {"representation": repre_entity["id"]}, ) def switch(self, container, context): diff --git a/client/ayon_core/hosts/max/plugins/load/load_pointcloud.py b/client/ayon_core/hosts/max/plugins/load/load_pointcloud.py index 894648ff23..6b9e5d6fb1 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_pointcloud.py +++ b/client/ayon_core/hosts/max/plugins/load/load_pointcloud.py @@ -45,8 +45,8 @@ class PointCloudLoader(load.LoaderPlugin): """update the container""" from pymxs import runtime as rt - repre_doc = context["representation"] - path = get_representation_path(repre_doc) + repre_entity = context["representation"] + path = get_representation_path(repre_entity) node = rt.GetNodeByName(container["instance_node"]) node_list = get_previous_loaded_object(node) update_custom_attribute_data( @@ -56,7 +56,7 @@ class PointCloudLoader(load.LoaderPlugin): for prt in rt.Selection: prt.filename = path lib.imprint(container["instance_node"], { - "representation": str(repre_doc["_id"]) + "representation": repre_entity["id"] }) def switch(self, container, context): diff --git a/client/ayon_core/hosts/max/plugins/load/load_redshift_proxy.py b/client/ayon_core/hosts/max/plugins/load/load_redshift_proxy.py index 7395a6eca5..40c7701797 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_redshift_proxy.py +++ b/client/ayon_core/hosts/max/plugins/load/load_redshift_proxy.py @@ -55,8 +55,8 @@ class RedshiftProxyLoader(load.LoaderPlugin): def update(self, container, context): from pymxs import runtime as rt - repre_doc = context["representation"] - path = get_representation_path(repre_doc) + repre_entity = context["representation"] + path = get_representation_path(repre_entity) node = rt.getNodeByName(container["instance_node"]) node_list = get_previous_loaded_object(node) rt.Select(node_list) @@ -66,7 +66,7 @@ class RedshiftProxyLoader(load.LoaderPlugin): proxy.file = path lib.imprint(container["instance_node"], { - "representation": str(repre_doc["_id"]) + "representation": repre_entity["id"] }) def switch(self, container, context): diff --git a/client/ayon_core/hosts/max/plugins/load/load_tycache.py b/client/ayon_core/hosts/max/plugins/load/load_tycache.py index 5acc759b4a..24cb762b2d 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_tycache.py +++ b/client/ayon_core/hosts/max/plugins/load/load_tycache.py @@ -43,8 +43,8 @@ class TyCacheLoader(load.LoaderPlugin): """update the container""" from pymxs import runtime as rt - repre_doc = context["representation"] - path = get_representation_path(repre_doc) + repre_entity = context["representation"] + path = get_representation_path(repre_entity) node = rt.GetNodeByName(container["instance_node"]) node_list = get_previous_loaded_object(node) update_custom_attribute_data(node, node_list) @@ -52,7 +52,7 @@ class TyCacheLoader(load.LoaderPlugin): for tyc in node_list: tyc.filename = path lib.imprint(container["instance_node"], { - "representation": str(repre_doc["_id"]) + "representation": repre_entity["id"] }) def switch(self, container, context): From 39aa9d7b5063482eaea9c24b7fd3ee0e7cfe82f5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 12:44:14 +0100 Subject: [PATCH 485/573] maya uses representation entity --- client/ayon_core/hosts/maya/api/commands.py | 20 +-- client/ayon_core/hosts/maya/api/lib.py | 7 +- client/ayon_core/hosts/maya/api/pipeline.py | 2 +- client/ayon_core/hosts/maya/api/plugin.py | 14 +- client/ayon_core/hosts/maya/api/setdress.py | 22 ++-- .../plugins/inventory/import_modelrender.py | 122 +++++++++++++----- .../rig_recreate_animation_instance.py | 15 ++- .../maya/plugins/load/load_arnold_standin.py | 6 +- .../hosts/maya/plugins/load/load_audio.py | 6 +- .../hosts/maya/plugins/load/load_gpucache.py | 6 +- .../hosts/maya/plugins/load/load_image.py | 23 ++-- .../maya/plugins/load/load_image_plane.py | 6 +- .../hosts/maya/plugins/load/load_look.py | 2 +- .../hosts/maya/plugins/load/load_maya_usd.py | 6 +- .../maya/plugins/load/load_multiverse_usd.py | 11 +- .../plugins/load/load_multiverse_usd_over.py | 10 +- .../maya/plugins/load/load_redshift_proxy.py | 6 +- .../maya/plugins/load/load_rendersetup.py | 6 +- .../maya/plugins/load/load_vdb_to_arnold.py | 12 +- .../maya/plugins/load/load_vdb_to_redshift.py | 10 +- .../maya/plugins/load/load_vdb_to_vray.py | 6 +- .../hosts/maya/plugins/load/load_vrayproxy.py | 13 +- .../hosts/maya/plugins/load/load_vrayscene.py | 6 +- .../hosts/maya/plugins/load/load_xgen.py | 4 +- .../maya/plugins/load/load_yeti_cache.py | 6 +- .../maya/plugins/publish/extract_layout.py | 15 ++- .../hosts/maya/tools/mayalookassigner/lib.py | 9 +- 27 files changed, 223 insertions(+), 148 deletions(-) diff --git a/client/ayon_core/hosts/maya/api/commands.py b/client/ayon_core/hosts/maya/api/commands.py index aa01d91369..e63800e542 100644 --- a/client/ayon_core/hosts/maya/api/commands.py +++ b/client/ayon_core/hosts/maya/api/commands.py @@ -39,16 +39,16 @@ class ToolWindows: cls._windows[tool] = window -def _resolution_from_document(doc): - if not doc: - print("Entered document is not valid. \"{}\"".format( - str(doc) +def _resolution_from_entity(entity): + if not entity: + print("Entered entity is not valid. \"{}\"".format( + str(entity) )) return None - attributes = doc.get("attrib") + attributes = entity.get("attrib") if attributes is None: - attributes = doc.get("data", {}) + attributes = entity.get("data", {}) resolution_width = attributes.get("resolutionWidth") resolution_height = attributes.get("resolutionHeight") @@ -60,7 +60,9 @@ def _resolution_from_document(doc): # Make sure both width and height are set if resolution_width is None or resolution_height is None: cmds.warning( - "No resolution information found for \"{}\"".format(doc["name"]) + "No resolution information found for \"{}\"".format( + entity["name"] + ) ) return None @@ -76,7 +78,7 @@ def reset_resolution(): project_name = get_current_project_name() folder_path = get_current_folder_path() folder_entity = get_folder_by_path(project_name, folder_path) - resolution = _resolution_from_document(folder_entity) + resolution = _resolution_from_entity(folder_entity) # Try get resolution from project if resolution is None: # TODO go through visualParents @@ -85,7 +87,7 @@ def reset_resolution(): " Trying to get resolution from project" ).format(folder_path)) project_entity = get_project(project_name) - resolution = _resolution_from_document(project_entity) + resolution = _resolution_from_entity(project_entity) if resolution is None: msg = "Using default resolution {}x{}" diff --git a/client/ayon_core/hosts/maya/api/lib.py b/client/ayon_core/hosts/maya/api/lib.py index d3b3fd4d26..7c3c739d7c 100644 --- a/client/ayon_core/hosts/maya/api/lib.py +++ b/client/ayon_core/hosts/maya/api/lib.py @@ -21,7 +21,6 @@ from maya.api import OpenMaya import ayon_api -from ayon_core.client import get_representation_by_name from ayon_core.settings import get_project_settings from ayon_core.pipeline import ( get_current_project_name, @@ -1907,16 +1906,16 @@ def assign_look_by_version(nodes, version_id): project_name = get_current_project_name() # Get representations of shader file and relationships - look_representation = get_representation_by_name( + look_representation = ayon_api.get_representation_by_name( project_name, "ma", version_id ) - json_representation = get_representation_by_name( + json_representation = ayon_api.get_representation_by_name( project_name, "json", version_id ) # See if representation is already loaded, if so reuse it. host = registered_host() - representation_id = str(look_representation['_id']) + representation_id = look_representation["id"] for container in host.ls(): if (container['loader'] == "LookLoader" and container['representation'] == representation_id): diff --git a/client/ayon_core/hosts/maya/api/pipeline.py b/client/ayon_core/hosts/maya/api/pipeline.py index 49b27c43eb..9792a4a5fe 100644 --- a/client/ayon_core/hosts/maya/api/pipeline.py +++ b/client/ayon_core/hosts/maya/api/pipeline.py @@ -449,7 +449,7 @@ def containerise(name, ("name", name), ("namespace", namespace), ("loader", loader), - ("representation", context["representation"]["_id"]), + ("representation", context["representation"]["id"]), ] for key, value in data: diff --git a/client/ayon_core/hosts/maya/api/plugin.py b/client/ayon_core/hosts/maya/api/plugin.py index 48ed8213b5..f2268088e6 100644 --- a/client/ayon_core/hosts/maya/api/plugin.py +++ b/client/ayon_core/hosts/maya/api/plugin.py @@ -809,9 +809,9 @@ class ReferenceLoader(Loader): node = container["objectName"] project_name = context["project"]["name"] - repre_doc = context["representation"] + repre_entity = context["representation"] - path = get_representation_path(repre_doc) + path = get_representation_path(repre_entity) # Get reference node from container members members = get_container_members(node) @@ -824,9 +824,9 @@ class ReferenceLoader(Loader): "abc": "Alembic", "fbx": "FBX", "usd": "USD Import" - }.get(repre_doc["name"]) + }.get(repre_entity["name"]) - assert file_type, "Unsupported representation: %s" % repre_doc + assert file_type, "Unsupported representation: %s" % repre_entity assert os.path.exists(path), "%s does not exist." % path @@ -834,7 +834,7 @@ class ReferenceLoader(Loader): # them to incoming data. alembic_attrs = ["speed", "offset", "cycleType", "time"] alembic_data = {} - if repre_doc["name"] == "abc": + if repre_entity["name"] == "abc": alembic_nodes = cmds.ls( "{}:*".format(namespace), type="AlembicNode" ) @@ -875,7 +875,7 @@ class ReferenceLoader(Loader): self._organize_containers(content, container["objectName"]) # Reapply alembic settings. - if repre_doc["name"] == "abc" and alembic_data: + if repre_entity["name"] == "abc" and alembic_data: alembic_nodes = cmds.ls( "{}:*".format(namespace), type="AlembicNode" ) @@ -909,7 +909,7 @@ class ReferenceLoader(Loader): # Update metadata cmds.setAttr("{}.representation".format(node), - str(repre_doc["_id"]), + repre_entity["id"], type="string") # When an animation or pointcache gets connected to an Xgen container, diff --git a/client/ayon_core/hosts/maya/api/setdress.py b/client/ayon_core/hosts/maya/api/setdress.py index a69bf8d39b..7276ae254c 100644 --- a/client/ayon_core/hosts/maya/api/setdress.py +++ b/client/ayon_core/hosts/maya/api/setdress.py @@ -10,11 +10,6 @@ import ayon_api from maya import cmds -from ayon_core.client import ( - get_representation_by_id, - get_representation_by_name, - get_representation_parents, -) from ayon_core.pipeline import ( schema, discover_loader_plugins, @@ -289,8 +284,9 @@ def update_package_version(container, version): # Versioning (from `core.maya.pipeline`) project_name = get_current_project_name() - current_representation = get_representation_by_id( - project_name, container["representation"] + repre_id = container["representation"] + current_representation = ayon_api.get_representation_by_id( + project_name, repre_id ) assert current_representation is not None, "This is a bug" @@ -300,7 +296,7 @@ def update_package_version(container, version): product_entity, folder_entity, project_entity - ) = get_representation_parents(project_name, current_representation) + ) = ayon_api.get_representation_parents(project_name, repre_id) if version == -1: new_version = ayon_api.get_last_version_by_product_id( @@ -315,7 +311,7 @@ def update_package_version(container, version): raise ValueError("Version not found: {}".format(version)) # Get the new representation (new file) - new_representation = get_representation_by_name( + new_representation = ayon_api.get_representation_by_name( project_name, current_representation["name"], new_version["id"] ) # TODO there is 'get_representation_context' to get the context which @@ -344,8 +340,8 @@ def update_package(set_container, context): # Load the original package data project_name = context["project"]["name"] - repre_doc = context["representation"] - current_representation = get_representation_by_id( + repre_entity = context["representation"] + current_representation = ayon_api.get_representation_by_id( project_name, set_container["representation"] ) @@ -355,7 +351,7 @@ def update_package(set_container, context): current_data = json.load(fp) # Load the new package data - new_file = get_representation_path(repre_doc) + new_file = get_representation_path(repre_entity) assert new_file.endswith(".json") with open(new_file, "r") as fp: new_data = json.load(fp) @@ -366,7 +362,7 @@ def update_package(set_container, context): # TODO: This should be handled by the pipeline itself cmds.setAttr(set_container['objectName'] + ".representation", - str(repre_doc['_id']), type="string") + context["representation"]["id"], type="string") def update_scene(set_container, containers, current_data, new_data, new_file): diff --git a/client/ayon_core/hosts/maya/plugins/inventory/import_modelrender.py b/client/ayon_core/hosts/maya/plugins/inventory/import_modelrender.py index e2cac22836..4655017ae5 100644 --- a/client/ayon_core/hosts/maya/plugins/inventory/import_modelrender.py +++ b/client/ayon_core/hosts/maya/plugins/inventory/import_modelrender.py @@ -1,13 +1,11 @@ import re import json -from ayon_core.client import ( - get_representation_by_id, - get_representations -) +import ayon_api + +from ayon_core.pipeline.load import get_representation_contexts_by_ids from ayon_core.pipeline import ( InventoryAction, - get_representation_context, get_current_project_name, ) from ayon_core.hosts.maya.api.lib import ( @@ -35,7 +33,69 @@ class ImportModelRender(InventoryAction): def process(self, containers): from maya import cmds # noqa: F401 + # --- Query entities that will be used --- project_name = get_current_project_name() + # Collect representation ids from all containers + repre_ids = { + container["representation"] + for container in containers + } + # Create mapping of representation id to version id + # - used in containers loop + version_id_by_repre_id = { + repre_entity["id"]: repre_entity["versionId"] + for repre_entity in ayon_api.get_representations( + project_name, + representation_ids=repre_ids, + fields={"id", "versionId"} + ) + } + + # Find all representations of the versions + version_ids = set(version_id_by_repre_id.values()) + repre_entities = ayon_api.get_representations( + project_name, + version_ids=version_ids, + fields={"id", "name", "versionId"} + ) + repre_entities_by_version_id = { + version_id: [] + for version_id in version_ids + } + for repre_entity in repre_entities: + version_id = repre_entity["versionId"] + repre_entities_by_version_id[version_id].append(repre_entity) + + look_repres_by_version_id = {} + look_repre_ids = set() + for version_id, repre_entities in ( + repre_entities_by_version_id.items() + ): + json_repre = None + look_repres = [] + scene_type_regex = re.compile(self.scene_type_regex) + for repre_entity in repre_entities: + repre_name = repre_entity["name"] + if repre_name == self.look_data_type: + json_repre = repre_entity + + elif scene_type_regex.fullmatch(repre_name): + look_repres.append(repre_entity) + + look_repre = look_repres[0] if look_repres else None + if look_repre: + look_repre_ids.add(look_repre["id"]) + if json_repre: + look_repre_ids.add(json_repre["id"]) + + look_repres_by_version_id[version_id] = (json_repre, look_repre) + + contexts_by_repre_id = get_representation_contexts_by_ids( + project_name, look_repre_ids + ) + + # --- Real process logic --- + # Loop over containers and assign the looks for container in containers: con_name = container["objectName"] nodes = [] @@ -45,22 +105,34 @@ class ImportModelRender(InventoryAction): else: nodes.append(n) - repr_doc = get_representation_by_id( - project_name, container["representation"], fields=["parent"] - ) - version_id = repr_doc["parent"] + repre_id = container["representation"] + version_id = version_id_by_repre_id.get(repre_id) + if version_id is None: + print("Representation '{}' was not found".format(repre_id)) + continue + + json_repre, look_repre = look_repres_by_version_id[version_id] print("Importing render sets for model %r" % con_name) - self.assign_model_render_by_version(nodes, version_id) + self._assign_model_render( + nodes, json_repre, look_repre, contexts_by_repre_id + ) - def assign_model_render_by_version(self, nodes, version_id): + def _assign_model_render( + self, nodes, json_repre, look_repre, contexts_by_repre_id + ): """Assign nodes a specific published model render data version by id. This assumes the nodes correspond with the asset. Args: - nodes(list): nodes to assign render data to - version_id (bson.ObjectId): database id of the version of model + nodes (list): nodes to assign render data to + json_repre (dict[str, Any]): Representation entity of the json + file. + look_repre (dict[str, Any]): First representation entity of the + look files. + contexts_by_repre_id (dict[str, Any]): Mapping of representation + id to its context. Returns: None @@ -68,33 +140,17 @@ class ImportModelRender(InventoryAction): from maya import cmds # noqa: F401 - project_name = get_current_project_name() - repre_docs = get_representations( - project_name, version_ids=[version_id], fields=["_id", "name"] - ) - # Get representations of shader file and relationships - json_repre = None - look_repres = [] - scene_type_regex = re.compile(self.scene_type_regex) - for repre_doc in repre_docs: - repre_name = repre_doc["name"] - if repre_name == self.look_data_type: - json_repre = repre_doc - continue - - if scene_type_regex.fullmatch(repre_name): - look_repres.append(repre_doc) - - look_repre = look_repres[0] if look_repres else None # QUESTION shouldn't be json representation validated too? if not look_repre: print("No model render sets for this model version..") return - context = get_representation_context(look_repre["_id"]) + # TODO use 'get_representation_path_with_anatomy' instead + # of 'filepath_from_context' + context = contexts_by_repre_id.get(look_repre["id"]) maya_file = self.filepath_from_context(context) - context = get_representation_context(json_repre["_id"]) + context = contexts_by_repre_id.get(json_repre["id"]) json_file = self.filepath_from_context(context) # Import the look file diff --git a/client/ayon_core/hosts/maya/plugins/inventory/rig_recreate_animation_instance.py b/client/ayon_core/hosts/maya/plugins/inventory/rig_recreate_animation_instance.py index 36d9864e99..cbff293cd7 100644 --- a/client/ayon_core/hosts/maya/plugins/inventory/rig_recreate_animation_instance.py +++ b/client/ayon_core/hosts/maya/plugins/inventory/rig_recreate_animation_instance.py @@ -1,7 +1,8 @@ from ayon_core.pipeline import ( InventoryAction, - get_representation_context + get_current_project_name, ) +from ayon_core.pipeline.load import get_representation_contexts_by_ids from ayon_core.hosts.maya.api.lib import ( create_rig_animation_instance, get_container_members, @@ -23,13 +24,21 @@ class RecreateRigAnimationInstance(InventoryAction): ) def process(self, containers): + project_name = get_current_project_name() + repre_ids = { + container["representation"] + for container in containers + } + contexts_by_repre_id = get_representation_contexts_by_ids( + project_name, repre_ids + ) for container in containers: # todo: delete an existing entry if it exist or skip creation namespace = container["namespace"] - representation_id = container["representation"] - context = get_representation_context(representation_id) + repre_id = container["representation"] + context = contexts_by_repre_id[repre_id] nodes = get_container_members(container) create_rig_animation_instance(nodes, context, namespace) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py b/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py index c7b5ed9d6c..0219a72515 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py @@ -189,8 +189,8 @@ class ArnoldStandinLoader(load.LoaderPlugin): if cmds.nodeType(shapes[0]) == "aiStandIn": standin = shapes[0] - repre_doc = context["representation"] - path = get_representation_path(repre_doc) + repre_entity = context["representation"] + path = get_representation_path(repre_entity) proxy_basename, proxy_path = self._get_proxy_path(path) # Whether there is proxy or so, we still update the string operator. @@ -216,7 +216,7 @@ class ArnoldStandinLoader(load.LoaderPlugin): cmds.setAttr( container["objectName"] + ".representation", - str(repre_doc["_id"]), + repre_entity["id"], type="string" ) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_audio.py b/client/ayon_core/hosts/maya/plugins/load/load_audio.py index df17214d34..ee37a7ad8c 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_audio.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_audio.py @@ -46,7 +46,7 @@ class AudioLoader(load.LoaderPlugin): ) def update(self, container, context): - repre_doc = context["representation"] + repre_entity = context["representation"] members = get_container_members(container) audio_nodes = cmds.ls(members, type="audio") @@ -61,7 +61,7 @@ class AudioLoader(load.LoaderPlugin): ) activate_sound = current_sound == audio_node - path = get_representation_path(repre_doc) + path = get_representation_path(repre_entity) cmds.sound( audio_node, @@ -94,7 +94,7 @@ class AudioLoader(load.LoaderPlugin): cmds.setAttr( container["objectName"] + ".representation", - str(repre_doc["_id"]), + repre_entity["id"], type="string" ) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_gpucache.py b/client/ayon_core/hosts/maya/plugins/load/load_gpucache.py index aacbf2db7c..080d20b0a6 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_gpucache.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_gpucache.py @@ -74,8 +74,8 @@ class GpuCacheLoader(load.LoaderPlugin): loader=self.__class__.__name__) def update(self, container, context): - repre_doc = context["representation"] - path = get_representation_path(repre_doc) + repre_entity = context["representation"] + path = get_representation_path(repre_entity) # Update the cache members = cmds.sets(container['objectName'], query=True) @@ -87,7 +87,7 @@ class GpuCacheLoader(load.LoaderPlugin): cmds.setAttr(cache + ".cacheFileName", path, type="string") cmds.setAttr(container["objectName"] + ".representation", - str(repre_doc["_id"]), + repre_entity["id"], type="string") def switch(self, container, context): diff --git a/client/ayon_core/hosts/maya/plugins/load/load_image.py b/client/ayon_core/hosts/maya/plugins/load/load_image.py index b55fb76a4d..d595aa2987 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_image.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_image.py @@ -146,7 +146,7 @@ class FileNodeLoader(load.LoaderPlugin): ) def update(self, container, context): - repre_doc = context["representation"] + repre_entity = context["representation"] members = cmds.sets(container['objectName'], query=True) file_node = cmds.ls(members, type="file")[0] @@ -156,7 +156,7 @@ class FileNodeLoader(load.LoaderPlugin): # Update representation cmds.setAttr( container["objectName"] + ".representation", - str(repre_doc["_id"]), + repre_entity["id"], type="string" ) @@ -222,15 +222,18 @@ class FileNodeLoader(load.LoaderPlugin): def _is_sequence(self, context): """Check whether frameStart and frameEnd are not the same.""" - version = context.get("version", {}) - representation = context.get("representation", {}) + version = context["version"] + representation = context["representation"] - for doc in [representation, version]: + # TODO this is invalid logic, it should be based only on + # representation entity + for entity in [representation, version]: # Frame range can be set on version or representation. # When set on representation it overrides version data. - data = doc.get("data", {}) - start = data.get("frameStartHandle", data.get("frameStart", None)) - end = data.get("frameEndHandle", data.get("frameEnd", None)) + attributes = entity["attrib"] + data = entity["data"] + start = data.get("frameStartHandle", attributes.get("frameStart")) + end = data.get("frameEndHandle", attributes.get("frameEnd")) if start is None or end is None: continue @@ -300,7 +303,7 @@ class FileNodeLoader(load.LoaderPlugin): context = copy.deepcopy(context) representation = context["representation"] - template = representation.get("data", {}).get("template") + template = representation.get("attrib", {}).get("template") if not template: # No template to find token locations for return get_representation_path_from_context(context) @@ -326,7 +329,7 @@ class FileNodeLoader(load.LoaderPlugin): has_tokens = True # Replace with our custom template that has the tokens set - representation["data"]["template"] = template + representation["attrib"]["template"] = template path = get_representation_path_from_context(context) if has_tokens: diff --git a/client/ayon_core/hosts/maya/plugins/load/load_image_plane.py b/client/ayon_core/hosts/maya/plugins/load/load_image_plane.py index 43cca4f35d..71ae892150 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_image_plane.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_image_plane.py @@ -201,19 +201,19 @@ class ImagePlaneLoader(load.LoaderPlugin): def update(self, container, context): folder_entity = context["folder"] - repre_doc = context["representation"] + repre_entity = context["representation"] members = get_container_members(container) image_planes = cmds.ls(members, type="imagePlane") assert image_planes, "Image plane not found." image_plane_shape = image_planes[0] - path = get_representation_path(repre_doc) + path = get_representation_path(repre_entity) cmds.setAttr("{}.imageName".format(image_plane_shape), path, type="string") cmds.setAttr("{}.representation".format(container["objectName"]), - str(repre_doc["_id"]), + repre_entity["id"], type="string") # Set frame range. diff --git a/client/ayon_core/hosts/maya/plugins/load/load_look.py b/client/ayon_core/hosts/maya/plugins/load/load_look.py index 186c6638b8..9226c7b16f 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_look.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_look.py @@ -4,8 +4,8 @@ import json from collections import defaultdict from qtpy import QtWidgets +from ayon_api import get_representation_by_name -from ayon_core.client import get_representation_by_name from ayon_core.pipeline import get_representation_path import ayon_core.hosts.maya.api.plugin from ayon_core.hosts.maya.api import lib diff --git a/client/ayon_core/hosts/maya/plugins/load/load_maya_usd.py b/client/ayon_core/hosts/maya/plugins/load/load_maya_usd.py index 5bd4afb3af..0a8adaf87f 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_maya_usd.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_maya_usd.py @@ -78,13 +78,13 @@ class MayaUsdLoader(load.LoaderPlugin): members = cmds.sets(node, query=True) or [] shapes = cmds.ls(members, type="mayaUsdProxyShape") - repre_doc = context["representation"] - path = get_representation_path(repre_doc) + repre_entity = context["representation"] + path = get_representation_path(repre_entity) for shape in shapes: cmds.setAttr("{}.filePath".format(shape), path, type="string") cmds.setAttr("{}.representation".format(node), - str(repre_doc["_id"]), + repre_entity["id"], type="string") def switch(self, container, context): diff --git a/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd.py b/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd.py index a482b406e1..aab87fc546 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd.py @@ -3,6 +3,8 @@ import maya.cmds as cmds from maya import mel import os +from ayon_api import get_representation_by_id + from ayon_core.pipeline import ( load, get_representation_path @@ -13,7 +15,6 @@ from ayon_core.hosts.maya.api.lib import ( unique_namespace ) from ayon_core.hosts.maya.api.pipeline import containerise -from ayon_core.client import get_representation_by_id class MultiverseUsdLoader(load.LoaderPlugin): @@ -71,12 +72,12 @@ class MultiverseUsdLoader(load.LoaderPlugin): assert shapes, "Cannot find mvUsdCompoundShape in container" project_name = context["project"]["name"] - repre_doc = context["representation"] - path = get_representation_path(repre_doc) + repre_entity = context["representation"] + path = get_representation_path(repre_entity) prev_representation_id = cmds.getAttr("{}.representation".format(node)) prev_representation = get_representation_by_id(project_name, prev_representation_id) - prev_path = os.path.normpath(prev_representation["data"]["path"]) + prev_path = os.path.normpath(prev_representation["attrib"]["path"]) # Make sure we can load the plugin cmds.loadPlugin("MultiverseForMaya", quiet=True) @@ -96,7 +97,7 @@ class MultiverseUsdLoader(load.LoaderPlugin): multiverse.SetUsdCompoundAssetPaths(shape, asset_paths) cmds.setAttr("{}.representation".format(node), - str(repre_doc["_id"]), + repre_entity["id"], type="string") mel.eval('refreshEditorTemplates;') diff --git a/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd_over.py b/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd_over.py index 6de03fe306..70069d3ae6 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd_over.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd_over.py @@ -4,6 +4,7 @@ from maya import mel import os import qargparse +from ayon_api import get_representation_by_id from ayon_core.pipeline import ( load, @@ -13,7 +14,6 @@ from ayon_core.hosts.maya.api.lib import ( maintained_selection ) from ayon_core.hosts.maya.api.pipeline import containerise -from ayon_core.client import get_representation_by_id class MultiverseUsdOverLoader(load.LoaderPlugin): @@ -89,13 +89,13 @@ class MultiverseUsdOverLoader(load.LoaderPlugin): assert mvShape, "Missing mv source" project_name = context["project"]["name"] - repre_doc = context["representation"] + repre_entity = context["representation"] prev_representation_id = cmds.getAttr("{}.representation".format(node)) prev_representation = get_representation_by_id(project_name, prev_representation_id) - prev_path = os.path.normpath(prev_representation["data"]["path"]) + prev_path = os.path.normpath(prev_representation["attrib"]["path"]) - path = get_representation_path(repre_doc) + path = get_representation_path(repre_entity) for shape in shapes: asset_paths = multiverse.GetUsdCompoundAssetPaths(shape) @@ -108,7 +108,7 @@ class MultiverseUsdOverLoader(load.LoaderPlugin): multiverse.SetUsdCompoundAssetPaths(shape, asset_paths) cmds.setAttr("{}.representation".format(node), - str(repre_doc["_id"]), + repre_entity["id"], type="string") mel.eval('refreshEditorTemplates;') diff --git a/client/ayon_core/hosts/maya/plugins/load/load_redshift_proxy.py b/client/ayon_core/hosts/maya/plugins/load/load_redshift_proxy.py index 4ec4dc2b2d..a0ed7cd6e7 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_redshift_proxy.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_redshift_proxy.py @@ -83,8 +83,8 @@ class RedshiftProxyLoader(load.LoaderPlugin): members = cmds.sets(node, query=True) or [] rs_meshes = cmds.ls(members, type="RedshiftProxyMesh") assert rs_meshes, "Cannot find RedshiftProxyMesh in container" - repre_doc = context["representation"] - filename = get_representation_path(repre_doc) + repre_entity = context["representation"] + filename = get_representation_path(repre_entity) for rs_mesh in rs_meshes: cmds.setAttr("{}.fileName".format(rs_mesh), @@ -93,7 +93,7 @@ class RedshiftProxyLoader(load.LoaderPlugin): # Update metadata cmds.setAttr("{}.representation".format(node), - str(repre_doc["_id"]), + repre_entity["id"], type="string") def remove(self, container): diff --git a/client/ayon_core/hosts/maya/plugins/load/load_rendersetup.py b/client/ayon_core/hosts/maya/plugins/load/load_rendersetup.py index 167f8aa833..0f01a65539 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_rendersetup.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_rendersetup.py @@ -91,8 +91,8 @@ class RenderSetupLoader(load.LoaderPlugin): "Render setup setting will be overwritten by new version. All " "setting specified by user not included in loaded version " "will be lost.") - repre_doc = context["representation"] - path = get_representation_path(repre_doc) + repre_entity = context["representation"] + path = get_representation_path(repre_entity) with open(path, "r") as file: try: renderSetup.instance().decode( @@ -104,7 +104,7 @@ class RenderSetupLoader(load.LoaderPlugin): # Update metadata node = container["objectName"] cmds.setAttr("{}.representation".format(node), - str(repre_doc["_id"]), + repre_entity["id"], type="string") self.log.info("... updated") diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py index 516fd7f496..05dac0e260 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py @@ -84,9 +84,9 @@ class LoadVDBtoArnold(load.LoaderPlugin): from maya import cmds - repre_doc = context["representation"] + repre_entity = context["representation"] - path = get_representation_path(repre_doc) + path = get_representation_path(repre_entity) # Find VRayVolumeGrid members = cmds.sets(container['objectName'], query=True) @@ -94,11 +94,11 @@ class LoadVDBtoArnold(load.LoaderPlugin): assert len(grid_nodes) == 1, "This is a bug" # Update the VRayVolumeGrid - self._set_path(grid_nodes[0], path=path, representation=repre_doc) + self._set_path(grid_nodes[0], path=path, representation=repre_entity) # Update container representation cmds.setAttr(container["objectName"] + ".representation", - str(repre_doc["_id"]), + repre_entity["id"], type="string") def switch(self, container, context): @@ -124,14 +124,14 @@ class LoadVDBtoArnold(load.LoaderPlugin): @staticmethod def _set_path(grid_node, path, - representation): + repre_entity): """Apply the settings for the VDB path to the aiVolume node""" from maya import cmds if not os.path.exists(path): raise RuntimeError("Path does not exist: %s" % path) - is_sequence = bool(representation["context"].get("frame")) + is_sequence = "frame" in repre_entity["context"] cmds.setAttr(grid_node + ".useFrameExtension", is_sequence) # Set file path diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_redshift.py b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_redshift.py index c5eadb48e6..b6cd513d9e 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_redshift.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_redshift.py @@ -96,8 +96,8 @@ class LoadVDBtoRedShift(load.LoaderPlugin): def update(self, container, context): from maya import cmds - repre_doc = context["representation"] - path = get_representation_path(repre_doc) + repre_entity = context["representation"] + path = get_representation_path(repre_entity) # Find VRayVolumeGrid members = cmds.sets(container['objectName'], query=True) @@ -105,11 +105,11 @@ class LoadVDBtoRedShift(load.LoaderPlugin): assert len(grid_nodes) == 1, "This is a bug" # Update the VRayVolumeGrid - self._set_path(grid_nodes[0], path=path, representation=repre_doc) + self._set_path(grid_nodes[0], path=path, representation=repre_entity) # Update container representation cmds.setAttr(container["objectName"] + ".representation", - str(repre_doc["_id"]), + repre_entity["id"], type="string") def remove(self, container): @@ -141,7 +141,7 @@ class LoadVDBtoRedShift(load.LoaderPlugin): if not os.path.exists(path): raise RuntimeError("Path does not exist: %s" % path) - is_sequence = bool(representation["context"].get("frame")) + is_sequence = "frame" in representation["context"] cmds.setAttr(grid_node + ".useFrameExtension", is_sequence) # Set file path diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py index 590cb6b975..8b5923bdbb 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py @@ -254,9 +254,9 @@ class LoadVDBtoVRay(load.LoaderPlugin): type="string") def update(self, container, context): - repre_doc = context["representation"] + repre_entity = context["representation"] - path = get_representation_path(repre_doc) + path = get_representation_path(repre_entity) # Find VRayVolumeGrid members = cmds.sets(container['objectName'], query=True) @@ -269,7 +269,7 @@ class LoadVDBtoVRay(load.LoaderPlugin): # Update container representation cmds.setAttr(container["objectName"] + ".representation", - str(repre_doc["_id"]), + repre_entity["id"], type="string") def switch(self, container, context): diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py b/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py index 3e77fd41fa..fe1f8425d8 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py @@ -7,13 +7,12 @@ loader will use them instead of native vray vrmesh format. """ import os +from ayon_api import get_representation_by_name import maya.cmds as cmds -from ayon_core.client import get_representation_by_name from ayon_core.settings import get_project_settings from ayon_core.pipeline import ( load, - get_current_project_name, get_representation_path, ) from ayon_core.hosts.maya.api.lib import ( @@ -109,12 +108,12 @@ class VRayProxyLoader(load.LoaderPlugin): assert vraymeshes, "Cannot find VRayMesh in container" # get all representations for this version - repre_doc = context["representation"] + repre_entity = context["representation"] filename = self._get_abc( context["project"]["name"], context["version"]["id"] ) if not filename: - filename = get_representation_path(repre_doc) + filename = get_representation_path(repre_entity) for vray_mesh in vraymeshes: cmds.setAttr("{}.fileName".format(vray_mesh), @@ -123,7 +122,7 @@ class VRayProxyLoader(load.LoaderPlugin): # Update metadata cmds.setAttr("{}.representation".format(node), - str(repre_doc["_id"]), + repre_entity["id"], type="string") def remove(self, container): @@ -191,7 +190,9 @@ class VRayProxyLoader(load.LoaderPlugin): """ self.log.debug( "Looking for abc in published representations of this version.") - abc_rep = get_representation_by_name(project_name, "abc", version_id) + abc_rep = ayon_api.get_representation_by_name( + project_name, "abc", version_id + ) if abc_rep: self.log.debug("Found, we'll link alembic to vray proxy.") file_name = get_representation_path(abc_rep) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py b/client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py index da10aaf719..3d21464edc 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py @@ -80,8 +80,8 @@ class VRaySceneLoader(load.LoaderPlugin): vraymeshes = cmds.ls(members, type="VRayScene") assert vraymeshes, "Cannot find VRayScene in container" - repre_doc = context["representation"] - filename = get_representation_path(repre_doc) + repre_entity = context["representation"] + filename = get_representation_path(repre_entity) for vray_mesh in vraymeshes: cmds.setAttr("{}.FilePath".format(vray_mesh), @@ -90,7 +90,7 @@ class VRaySceneLoader(load.LoaderPlugin): # Update metadata cmds.setAttr("{}.representation".format(node), - str(repre_doc["_id"]), + repre_entity["id"], type="string") def remove(self, container): diff --git a/client/ayon_core/hosts/maya/plugins/load/load_xgen.py b/client/ayon_core/hosts/maya/plugins/load/load_xgen.py index fdac62a250..58a6d86292 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_xgen.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_xgen.py @@ -147,8 +147,8 @@ class XgenLoader(ayon_core.hosts.maya.api.plugin.ReferenceLoader): self.set_palette_attributes(xgen_palette, xgen_file, xgd_file) - repre_doc = context["representation"] - maya_file = get_representation_path(repre_doc) + repre_entity = context["representation"] + maya_file = get_representation_path(repre_entity) _, extension = os.path.splitext(maya_file) new_xgen_file = maya_file.replace(extension, ".xgen") data_path = "" diff --git a/client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py b/client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py index 671230583d..18a094c29d 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py @@ -123,11 +123,11 @@ class YetiCacheLoader(load.LoaderPlugin): cmds.namespace(removeNamespace=namespace, deleteNamespaceContent=True) def update(self, container, context): - repre_doc = context["representation"] + repre_entity = context["representation"] namespace = container["namespace"] container_node = container["objectName"] - path = get_representation_path(repre_doc) + path = get_representation_path(repre_entity) settings = self.read_settings(path) # Collect scene information of asset @@ -216,7 +216,7 @@ class YetiCacheLoader(load.LoaderPlugin): set_attribute(attr, value, yeti_node) cmds.setAttr("{}.representation".format(container_node), - str(repre_doc["_id"]), + repre_entity["id"], typ="string") def switch(self, container, context): diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_layout.py b/client/ayon_core/hosts/maya/plugins/publish/extract_layout.py index 441610b749..b025a1605a 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_layout.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_layout.py @@ -4,8 +4,8 @@ import json from maya import cmds from maya.api import OpenMaya as om +from ayon_api import get_representation_by_id -from ayon_core.client import get_representation_by_id from ayon_core.pipeline import publish @@ -44,6 +44,8 @@ class ExtractLayout(publish.Extractor): grp_loaded_ass = instance.data.get("groupLoadedAssets", False) if grp_loaded_ass: asset_list = cmds.listRelatives(asset, children=True) + # WARNING This does override 'asset' variable from parent loop + # is it correct? for asset in asset_list: grp_name = asset.split(':')[0] else: @@ -61,13 +63,18 @@ class ExtractLayout(publish.Extractor): representation = get_representation_by_id( project_name, representation_id, - fields=["parent", "context.family"] + fields={"versionId", "context"} ) self.log.debug(representation) - version_id = representation.get("parent") - product_type = representation.get("context").get("family") + version_id = representation["versionId"] + # TODO use product entity to get product type rather than + # data in representation 'context' + repre_context = representation["context"] + product_type = repre_context.get("product", {}).get("type") + if not product_type: + product_type = repre_context.get("family") json_element = { "product_type": product_type, diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/lib.py b/client/ayon_core/hosts/maya/tools/mayalookassigner/lib.py index e3ebddb7d4..78fded12a9 100644 --- a/client/ayon_core/hosts/maya/tools/mayalookassigner/lib.py +++ b/client/ayon_core/hosts/maya/tools/mayalookassigner/lib.py @@ -1,6 +1,8 @@ import json import logging +from ayon_api import get_representation_by_name + from ayon_core.pipeline import ( get_current_project_name, get_representation_path, @@ -9,7 +11,6 @@ from ayon_core.pipeline import ( loaders_from_representation, load_container ) -from ayon_core.client import get_representation_by_name from ayon_core.hosts.maya.api import lib @@ -29,7 +30,7 @@ def get_look_relationships(version_id): project_name = get_current_project_name() json_representation = get_representation_by_name( - project_name, representation_name="json", version_id=version_id + project_name, "json", version_id ) # Load relationships @@ -57,12 +58,12 @@ def load_look(version_id): project_name = get_current_project_name() # Get representations of shader file and relationships look_representation = get_representation_by_name( - project_name, representation_name="ma", version_id=version_id + project_name, "ma", version_id ) # See if representation is already loaded, if so reuse it. host = registered_host() - representation_id = str(look_representation['_id']) + representation_id = look_representation["id"] for container in host.ls(): if (container['loader'] == "LookLoader" and container['representation'] == representation_id): From 6e7677dd372643af06d27f4a8f0ae14ab135ebb7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 12:44:51 +0100 Subject: [PATCH 486/573] nuke uses representation entitiy --- client/ayon_core/hosts/nuke/api/lib.py | 22 +++++----- client/ayon_core/hosts/nuke/api/pipeline.py | 2 +- .../nuke/api/workfile_template_builder.py | 2 +- .../hosts/nuke/plugins/load/load_backdrop.py | 6 +-- .../nuke/plugins/load/load_camera_abc.py | 6 +-- .../hosts/nuke/plugins/load/load_clip.py | 43 ++++++++++--------- .../hosts/nuke/plugins/load/load_effects.py | 6 +-- .../nuke/plugins/load/load_effects_ip.py | 6 +-- .../hosts/nuke/plugins/load/load_gizmo.py | 6 +-- .../hosts/nuke/plugins/load/load_gizmo_ip.py | 6 +-- .../hosts/nuke/plugins/load/load_image.py | 24 +++++------ .../hosts/nuke/plugins/load/load_model.py | 6 +-- .../hosts/nuke/plugins/load/load_ociolook.py | 6 +-- .../nuke/plugins/load/load_script_precomp.py | 6 +-- 14 files changed, 74 insertions(+), 73 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index 88e3583410..374a4608fc 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -14,8 +14,6 @@ import nuke from qtpy import QtCore, QtWidgets import ayon_api -from ayon_core.client import get_representations - from ayon_core.host import HostDirmap from ayon_core.tools.utils import host_tools from ayon_core.pipeline.workfile.workfile_template_builder import ( @@ -846,19 +844,19 @@ def check_inventory_versions(): project_name = get_current_project_name() # Find representations based on found containers - repre_docs = get_representations( + repre_entities = ayon_api.get_representations( project_name, representation_ids=repre_ids, - fields=["_id", "parent"] + fields={"id", "versionId"} ) # Store representations by id and collect version ids - repre_docs_by_id = {} + repre_entities_by_id = {} version_ids = set() - for repre_doc in repre_docs: + for repre_entity in repre_entities: # Use stringed representation id to match value in containers - repre_id = str(repre_doc["_id"]) - repre_docs_by_id[repre_id] = repre_doc - version_ids.add(repre_doc["parent"]) + repre_id = repre_entity["id"] + repre_entities_by_id[repre_id] = repre_entity + version_ids.add(repre_entity["versionId"]) version_entities = ayon_api.get_versions( project_name, @@ -881,15 +879,15 @@ def check_inventory_versions(): for item in node_with_repre_id: # Some python versions of nuke can't unfold tuple in for loop node, repre_id = item - repre_doc = repre_docs_by_id.get(repre_id) + repre_entity = repre_entities_by_id.get(repre_id) # Failsafe for not finding the representation. - if not repre_doc: + if not repre_entity: log.warning(( "Could not find the representation on node \"{}\"" ).format(node.name())) continue - version_id = repre_doc["parent"] + version_id = repre_entity["versionId"] version_entity = version_entities_by_id.get(version_id) if not version_entity: log.warning(( diff --git a/client/ayon_core/hosts/nuke/api/pipeline.py b/client/ayon_core/hosts/nuke/api/pipeline.py index 1189f79a07..a8876f6aa7 100644 --- a/client/ayon_core/hosts/nuke/api/pipeline.py +++ b/client/ayon_core/hosts/nuke/api/pipeline.py @@ -432,7 +432,7 @@ def containerise(node, ("name", name), ("namespace", namespace), ("loader", str(loader)), - ("representation", context["representation"]["_id"]), + ("representation", context["representation"]["id"]), ], **data or dict() diff --git a/client/ayon_core/hosts/nuke/api/workfile_template_builder.py b/client/ayon_core/hosts/nuke/api/workfile_template_builder.py index 218ba97dd5..495edd9e5f 100644 --- a/client/ayon_core/hosts/nuke/api/workfile_template_builder.py +++ b/client/ayon_core/hosts/nuke/api/workfile_template_builder.py @@ -167,7 +167,7 @@ class NukePlaceholderLoadPlugin(NukePlaceholderPlugin, PlaceholderLoadMixin): placeholder.data["nodes_init"] = nuke.allNodes() def _before_repre_load(self, placeholder, representation): - placeholder.data["last_repre_id"] = str(representation["_id"]) + placeholder.data["last_repre_id"] = representation["id"] def collect_placeholders(self): output = [] diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py b/client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py index 888e8ec62a..51fe7dac34 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py @@ -186,12 +186,12 @@ class LoadBackdropNodes(load.LoaderPlugin): # Get version from io project_name = context["project"]["name"] version_entity = context["version"] - repre_doc = context["representation"] + repre_entity = context["representation"] # get corresponding node GN = container["node"] - file = get_representation_path(repre_doc).replace("\\", "/") + file = get_representation_path(repre_entity).replace("\\", "/") name = container["name"] namespace = container["namespace"] @@ -201,7 +201,7 @@ class LoadBackdropNodes(load.LoaderPlugin): colorspace = version_attributes.get("colorSpace") data_imprint = { - "representation": str(repre_doc["_id"]), + "representation": repre_entity["id"], "version": version_entity["version"], "colorspaceInput": colorspace, } diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py b/client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py index 79d8a1d063..bb7c8ea7c8 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py @@ -106,7 +106,7 @@ class AlembicCameraLoader(load.LoaderPlugin): """ # Get version from io version_entity = context["version"] - repre_doc = context["representation"] + repre_entity = context["representation"] # get main variables version_attributes = version_entity["attrib"] @@ -116,7 +116,7 @@ class AlembicCameraLoader(load.LoaderPlugin): # prepare data for imprinting data_imprint = { - "representation": str(repre_doc["_id"]), + "representation": repre_entity["id"], "frameStart": first, "frameEnd": last, "version": version_entity["version"] @@ -127,7 +127,7 @@ class AlembicCameraLoader(load.LoaderPlugin): data_imprint[k] = version_attributes[k] # getting file path - file = get_representation_path(repre_doc).replace("\\", "/") + file = get_representation_path(repre_entity).replace("\\", "/") with maintained_selection(): camera_node = container["node"] diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py index c43c8853d8..559eab463a 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py @@ -110,7 +110,7 @@ class LoadClip(plugin.NukeLoader): add_retime = options.get( "add_retime", self.options_defaults["add_retime"]) - repre_id = representation["_id"] + repre_id = representation["id"] self.log.debug( "Representation id `{}` ".format(repre_id)) @@ -212,17 +212,18 @@ class LoadClip(plugin.NukeLoader): def switch(self, container, context): self.update(container, context) - def _representation_with_hash_in_frame(self, representation): + def _representation_with_hash_in_frame(self, repre_entity): """Convert frame key value to padded hash Args: - representation (dict): representation data + repre_entity (dict): Representation entity. Returns: dict: altered representation data + """ - representation = deepcopy(representation) - context = representation["context"] + new_repre_entity = deepcopy(repre_entity) + context = new_repre_entity["context"] # Get the frame from the context and hash it frame = context["frame"] @@ -230,7 +231,7 @@ class LoadClip(plugin.NukeLoader): # Replace the frame with the hash in the originalBasename if ( - "{originalBasename}" in representation["data"]["template"] + "{originalBasename}" in new_repre_entity["attrib"]["template"] ): origin_basename = context["originalBasename"] context["originalBasename"] = origin_basename.replace( @@ -238,8 +239,8 @@ class LoadClip(plugin.NukeLoader): ) # Replace the frame with the hash in the frame - representation["context"]["frame"] = hashed_frame - return representation + new_repre_entity["context"]["frame"] = hashed_frame + return new_repre_entity def update(self, container, context): """Update the Loader's path @@ -252,21 +253,23 @@ class LoadClip(plugin.NukeLoader): project_name = context["project"]["name"] version_entity = context["version"] - repre_doc = context["representation"] + repre_entity = context["representation"] version_attributes = version_entity["attrib"] version_data = version_entity["data"] - is_sequence = len(repre_doc["files"]) > 1 + is_sequence = len(repre_entity["files"]) > 1 read_node = container["node"] if is_sequence: - repre_doc = self._representation_with_hash_in_frame( - repre_doc + repre_entity = self._representation_with_hash_in_frame( + repre_entity ) - filepath = get_representation_path(repre_doc).replace("\\", "/") + filepath = ( + get_representation_path(repre_entity) + ).replace("\\", "/") self.log.debug("_ filepath: {}".format(filepath)) start_at_workfile = "start at" in read_node['frame_mode'].value() @@ -276,11 +279,11 @@ class LoadClip(plugin.NukeLoader): if "addRetime" in key ] - repre_id = repre_doc["_id"] + repre_id = repre_entity["id"] # colorspace profile colorspace = ( - repre_doc["data"].get("colorspace") + repre_entity["data"].get("colorspace") or version_attributes.get("colorSpace") ) @@ -313,7 +316,7 @@ class LoadClip(plugin.NukeLoader): self._set_range_to_node(read_node, first, last, start_at_workfile) updated_dict = { - "representation": str(repre_doc["_id"]), + "representation": repre_entity["id"], "frameStart": str(first), "frameEnd": str(last), "version": str(version_entity["version"]), @@ -441,10 +444,10 @@ class LoadClip(plugin.NukeLoader): def _get_node_name(self, context): folder_entity = context["folder"] product_name = context["product"]["name"] - repre_doc = context["representation"] + repre_entity = context["representation"] folder_name = folder_entity["name"] - repre_cont = repre_doc["context"] + repre_cont = repre_entity["context"] name_data = { "folder": { "name": folder_name, @@ -454,9 +457,9 @@ class LoadClip(plugin.NukeLoader): }, "asset": folder_name, "subset": product_name, - "representation": repre_doc["name"], + "representation": repre_entity["name"], "ext": repre_cont["representation"], - "id": repre_doc["_id"], + "id": repre_entity["id"], "class_name": self.__class__.__name__ } diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_effects.py b/client/ayon_core/hosts/nuke/plugins/load/load_effects.py index dade7bfff0..50ce0d1ad7 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_effects.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_effects.py @@ -160,12 +160,12 @@ class LoadEffects(load.LoaderPlugin): # Get version from io project_name = context["project"]["name"] version_entity = context["version"] - repre_doc = context["representation"] + repre_entity = context["representation"] # get corresponding node GN = container["node"] - file = get_representation_path(repre_doc).replace("\\", "/") + file = get_representation_path(repre_entity).replace("\\", "/") version_attributes = version_entity["attrib"] first = version_attributes.get("frameStart") @@ -176,7 +176,7 @@ class LoadEffects(load.LoaderPlugin): namespace = container["namespace"] data_imprint = { - "representation": str(repre_doc["_id"]), + "representation": repre_entity["id"], "frameStart": first, "frameEnd": last, "version": version_entity["version"], diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py b/client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py index 47b84abc6a..8c58241316 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py @@ -164,12 +164,12 @@ class LoadEffectsInputProcess(load.LoaderPlugin): # Get version from io project_name = context["project"]["name"] version_entity = context["version"] - repre_doc = context["representation"] + repre_entity = context["representation"] # get corresponding node GN = container["node"] - file = get_representation_path(repre_doc).replace("\\", "/") + file = get_representation_path(repre_entity).replace("\\", "/") version_attributes = version_entity["attrib"] first = version_attributes.get("frameStart") @@ -179,7 +179,7 @@ class LoadEffectsInputProcess(load.LoaderPlugin): workfile_first_frame = int(nuke.root()["first_frame"].getValue()) data_imprint = { - "representation": str(repre_doc["_id"]), + "representation": repre_entity["id"], "frameStart": first, "frameEnd": last, "version": version_entity["version"], diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py b/client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py index dcba0e6720..5130b825c4 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py @@ -112,21 +112,21 @@ class LoadGizmo(load.LoaderPlugin): # Get version from io project_name = context["project"]["name"] version_entity = context["version"] - repre_doc = context["representation"] + repre_entity = context["representation"] version_attributes = version_entity["attrib"] # get corresponding node group_node = container["node"] - file = get_representation_path(repre_doc).replace("\\", "/") + file = get_representation_path(repre_entity).replace("\\", "/") first = version_attributes.get("frameStart") last = version_attributes.get("frameEnd") colorspace = version_attributes.get("colorSpace") data_imprint = { - "representation": str(repre_doc["_id"]), + "representation": repre_entity["id"], "frameStart": first, "frameEnd": last, "version": version_entity["version"], diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py b/client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py index 6c5f4f402b..8aa73ca0e4 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py @@ -119,12 +119,12 @@ class LoadGizmoInputProcess(load.LoaderPlugin): # Get version from io project_name = context["project"]["name"] version_entity = context["version"] - repre_doc = context["representation"] + repre_entity = context["representation"] # get corresponding node group_node = container["node"] - file = get_representation_path(repre_doc).replace("\\", "/") + file = get_representation_path(repre_entity).replace("\\", "/") version_attributes = version_entity["attrib"] first = version_attributes.get("frameStart") @@ -132,7 +132,7 @@ class LoadGizmoInputProcess(load.LoaderPlugin): colorspace = version_attributes.get("colorSpace") data_imprint = { - "representation": str(repre_doc["_id"]), + "representation": repre_entity["id"], "frameStart": first, "frameEnd": last, "version": version_entity["version"], diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_image.py b/client/ayon_core/hosts/nuke/plugins/load/load_image.py index 0e5096fda6..002592758f 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_image.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_image.py @@ -70,8 +70,8 @@ class LoadImage(load.LoaderPlugin): version_entity = context["version"] version_attributes = version_entity["attrib"] - repre_doc = context["representation"] - repre_id = repre_doc["_id"] + repre_entity = context["representation"] + repre_id = repre_entity["id"] self.log.debug( "Representation id `{}` ".format(repre_id)) @@ -91,7 +91,7 @@ class LoadImage(load.LoaderPlugin): file = file.replace("\\", "/") - frame = repre_doc["context"].get("frame") + frame = repre_entity["context"].get("frame") if frame: padding = len(frame) file = file.replace( @@ -163,14 +163,14 @@ class LoadImage(load.LoaderPlugin): project_name = context["project"]["name"] version_entity = context["version"] - repre_doc = context["representation"] + repre_entity = context["representation"] - repr_cont = repre_doc["context"] + repr_cont = repre_entity["context"] - file = get_representation_path(repre_doc) + file = get_representation_path(repre_entity) if not file: - repre_id = repre_doc["_id"] + repre_id = repre_entity["id"] self.log.warning( "Representation id `{}` is failing to load".format(repre_id)) return @@ -200,7 +200,7 @@ class LoadImage(load.LoaderPlugin): version_attributes = version_entity["attrib"] updated_dict = { - "representation": str(repre_doc["_id"]), + "representation": repre_entity["id"], "frameStart": str(first), "frameEnd": str(last), "version": str(version_entity["version"]), @@ -233,10 +233,10 @@ class LoadImage(load.LoaderPlugin): def _get_node_name(self, context): folder_entity = context["folder"] product_name = context["product"]["name"] - repre_doc = context["representation"] + repre_entity = context["representation"] folder_name = folder_entity["name"] - repre_cont = repre_doc["context"] + repre_cont = repre_entity["context"] name_data = { "folder": { "name": folder_name, @@ -246,9 +246,9 @@ class LoadImage(load.LoaderPlugin): }, "asset": folder_name, "subset": product_name, - "representation": repre_doc["name"], + "representation": repre_entity["name"], "ext": repre_cont["representation"], - "id": repre_doc["_id"], + "id": repre_entity["id"], "class_name": self.__class__.__name__ } diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_model.py b/client/ayon_core/hosts/nuke/plugins/load/load_model.py index b9032f7c28..fcf1c2eda9 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_model.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_model.py @@ -110,7 +110,7 @@ class AlembicModelLoader(load.LoaderPlugin): # Get version from io project_name = context["project"]["name"] version_entity = context["version"] - repre_doc = context["representation"] + repre_entity = context["representation"] # get corresponding node model_node = container["node"] @@ -123,7 +123,7 @@ class AlembicModelLoader(load.LoaderPlugin): # prepare data for imprinting data_imprint = { - "representation": str(repre_doc["_id"]), + "representation": repre_entity["id"], "frameStart": first, "frameEnd": last, "version": version_entity["version"] @@ -134,7 +134,7 @@ class AlembicModelLoader(load.LoaderPlugin): data_imprint[k] = version_attributes[k] # getting file path - file = get_representation_path(repre_doc).replace("\\", "/") + file = get_representation_path(repre_entity).replace("\\", "/") with maintained_selection(): model_node['selected'].setValue(True) diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_ociolook.py b/client/ayon_core/hosts/nuke/plugins/load/load_ociolook.py index e09e715db5..94af7c40e2 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_ociolook.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_ociolook.py @@ -221,11 +221,11 @@ class LoadOcioLookNodes(load.LoaderPlugin): return group_node def update(self, container, context): - repre_doc = context["representation"] + repre_entity = context["representation"] group_node = container["node"] - filepath = get_representation_path(repre_doc) + filepath = get_representation_path(repre_entity) json_f = self._load_json_data(filepath) @@ -243,7 +243,7 @@ class LoadOcioLookNodes(load.LoaderPlugin): group_node["name"].value())) return update_container( - group_node, {"representation": str(repre_doc["_id"])}) + group_node, {"representation": repre_entity["id"]}) def _load_json_data(self, filepath): # getting data from json file with unicode conversion diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_script_precomp.py b/client/ayon_core/hosts/nuke/plugins/load/load_script_precomp.py index 6dc74a517e..9fb168f322 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_script_precomp.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_script_precomp.py @@ -117,15 +117,15 @@ class LinkAsGroup(load.LoaderPlugin): project_name = context["project"]["name"] version_entity = context["version"] - repre_doc = context["representation"] + repre_entity = context["representation"] - root = get_representation_path(repre_doc).replace("\\", "/") + root = get_representation_path(repre_entity).replace("\\", "/") # Get start frame from version data version_attributes = version_entity["attrib"] updated_dict = { - "representation": str(repre_doc["_id"]), + "representation": repre_entity["id"], "frameEnd": version_attributes.get("frameEnd"), "version": version_entity["version"], "colorspace": version_attributes.get("colorSpace"), From 301a40981cc617051e0735988c66832f44754fee Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 12:45:07 +0100 Subject: [PATCH 487/573] photoshop uses representation entity --- client/ayon_core/hosts/photoshop/api/README.md | 6 +++--- client/ayon_core/hosts/photoshop/api/pipeline.py | 2 +- client/ayon_core/hosts/photoshop/plugins/load/load_image.py | 6 +++--- .../hosts/photoshop/plugins/load/load_reference.py | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/client/ayon_core/hosts/photoshop/api/README.md b/client/ayon_core/hosts/photoshop/api/README.md index 9383bdaade..c936c1ec1f 100644 --- a/client/ayon_core/hosts/photoshop/api/README.md +++ b/client/ayon_core/hosts/photoshop/api/README.md @@ -226,14 +226,14 @@ class ImageLoader(load.LoaderPlugin): def update(self, container, context): layer = container.pop("layer") - repre_doc = context["representation"] + repre_entity = context["representation"] with photoshop.maintained_selection(): stub.replace_smart_object( - layer, get_representation_path(repre_doc) + layer, get_representation_path(repre_entity) ) stub.imprint( - layer, {"representation": str(repre_doc["_id"])} + layer, {"representation": repre_entity["id"]} ) def remove(self, container): diff --git a/client/ayon_core/hosts/photoshop/api/pipeline.py b/client/ayon_core/hosts/photoshop/api/pipeline.py index 4e9a861220..32f66cf7fb 100644 --- a/client/ayon_core/hosts/photoshop/api/pipeline.py +++ b/client/ayon_core/hosts/photoshop/api/pipeline.py @@ -260,7 +260,7 @@ def containerise( "name": name, "namespace": namespace, "loader": str(loader), - "representation": str(context["representation"]["_id"]), + "representation": context["representation"]["id"], "members": [str(layer.id)] } stub = lib.stub() diff --git a/client/ayon_core/hosts/photoshop/plugins/load/load_image.py b/client/ayon_core/hosts/photoshop/plugins/load/load_image.py index 674410195b..42cc2dbcbf 100644 --- a/client/ayon_core/hosts/photoshop/plugins/load/load_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/load/load_image.py @@ -42,7 +42,7 @@ class ImageLoader(photoshop.PhotoshopLoader): layer = container.pop("layer") - repre_doc = context["representation"] + repre_entity = context["representation"] folder_name = context["folder"]["name"] product_name = context["product"]["name"] @@ -57,14 +57,14 @@ class ImageLoader(photoshop.PhotoshopLoader): else: # switching version - keep same name layer_name = container["namespace"] - path = get_representation_path(repre_doc) + path = get_representation_path(repre_entity) with photoshop.maintained_selection(): stub.replace_smart_object( layer, path, layer_name ) stub.imprint( - layer.id, {"representation": str(repre_doc["_id"])} + layer.id, {"representation": repre_entity["id"]} ) def remove(self, container): diff --git a/client/ayon_core/hosts/photoshop/plugins/load/load_reference.py b/client/ayon_core/hosts/photoshop/plugins/load/load_reference.py index d468e296d0..f1897ad18a 100644 --- a/client/ayon_core/hosts/photoshop/plugins/load/load_reference.py +++ b/client/ayon_core/hosts/photoshop/plugins/load/load_reference.py @@ -44,7 +44,7 @@ class ReferenceLoader(photoshop.PhotoshopLoader): folder_name = context["folder"]["name"] product_name = context["product"]["name"] - repre_doc = context["representation"] + repre_entity = context["representation"] namespace_from_container = re.sub(r'_\d{3}$', '', container["namespace"]) @@ -57,14 +57,14 @@ class ReferenceLoader(photoshop.PhotoshopLoader): else: # switching version - keep same name layer_name = container["namespace"] - path = get_representation_path(repre_doc) + path = get_representation_path(repre_entity) with photoshop.maintained_selection(): stub.replace_smart_object( layer, path, layer_name ) stub.imprint( - layer.id, {"representation": str(repre_doc["_id"])} + layer.id, {"representation": repre_entity["id"]} ) def remove(self, container): From a9ebc2e8e16d420dbe6bf840774a2c589f6397ce Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 12:45:20 +0100 Subject: [PATCH 488/573] resolve uses representation entity --- client/ayon_core/hosts/resolve/api/pipeline.py | 2 +- client/ayon_core/hosts/resolve/api/plugin.py | 4 ++-- client/ayon_core/hosts/resolve/plugins/load/load_clip.py | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/hosts/resolve/api/pipeline.py b/client/ayon_core/hosts/resolve/api/pipeline.py index 19d33971dc..15e4f1203d 100644 --- a/client/ayon_core/hosts/resolve/api/pipeline.py +++ b/client/ayon_core/hosts/resolve/api/pipeline.py @@ -123,7 +123,7 @@ def containerise(timeline_item, "name": str(name), "namespace": str(namespace), "loader": str(loader), - "representation": str(context["representation"]["_id"]), + "representation": context["representation"]["id"], }) if data: diff --git a/client/ayon_core/hosts/resolve/api/plugin.py b/client/ayon_core/hosts/resolve/api/plugin.py index 7c4490a897..c205daee8c 100644 --- a/client/ayon_core/hosts/resolve/api/plugin.py +++ b/client/ayon_core/hosts/resolve/api/plugin.py @@ -357,11 +357,11 @@ class ClipLoader: # create name folder_entity = self.context["folder"] product_name = self.context["product"]["name"] - repre_doc = self.context["representation"] + repre_entity = self.context["representation"] folder_name = folder_entity["name"] folder_path = folder_entity["path"] - representation_name = repre_doc["name"] + representation_name = repre_entity["name"] self.data["clip_name"] = "_".join([ folder_name, diff --git a/client/ayon_core/hosts/resolve/plugins/load/load_clip.py b/client/ayon_core/hosts/resolve/plugins/load/load_clip.py index 6715135377..26ee35a90d 100644 --- a/client/ayon_core/hosts/resolve/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/resolve/plugins/load/load_clip.py @@ -67,14 +67,14 @@ class LoadClip(plugin.TimelineItemLoader): """ Updating previously loaded clips """ - repre_doc = context["representation"] + repre_entity = context["representation"] name = container['name'] namespace = container['namespace'] timeline_item = container["_timeline_item"] media_pool_item = timeline_item.GetMediaPoolItem() - files = plugin.get_representation_files(repre_doc) + files = plugin.get_representation_files(repre_entity) loader = plugin.ClipLoader(self, context) timeline_item = loader.update(timeline_item, files) @@ -97,7 +97,7 @@ class LoadClip(plugin.TimelineItemLoader): def get_tag_data(self, context, name, namespace): """Return data to be imprinted on the timeline item marker""" - repre_doc = context["representation"] + repre_entity = context["representation"] version_entity = context["version"] version_attributes = version_entity["attrib"] colorspace = version_attributes.get("colorSpace", None) @@ -116,7 +116,7 @@ class LoadClip(plugin.TimelineItemLoader): # add variables related to version context data.update({ - "representation": str(repre_doc["_id"]), + "representation": repre_entity["id"], "version": version_entity["version"], "colorspace": colorspace, "objectName": object_name From 323e6a509869176782e46b4a9adb4ece7f427240 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 12:45:36 +0100 Subject: [PATCH 489/573] substance painter uses representation entity --- client/ayon_core/hosts/substancepainter/api/pipeline.py | 2 +- .../hosts/substancepainter/plugins/load/load_mesh.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/substancepainter/api/pipeline.py b/client/ayon_core/hosts/substancepainter/api/pipeline.py index fdea26e6f9..c75cc3135a 100644 --- a/client/ayon_core/hosts/substancepainter/api/pipeline.py +++ b/client/ayon_core/hosts/substancepainter/api/pipeline.py @@ -337,7 +337,7 @@ def imprint_container(container, ("name", str(name)), ("namespace", str(namespace) if namespace else None), ("loader", str(loader.__class__.__name__)), - ("representation", str(context["representation"]["_id"])), + ("representation", context["representation"]["id"]), ] for key, value in data: container[key] = value diff --git a/client/ayon_core/hosts/substancepainter/plugins/load/load_mesh.py b/client/ayon_core/hosts/substancepainter/plugins/load/load_mesh.py index 810fecb8e5..fb2f1db726 100644 --- a/client/ayon_core/hosts/substancepainter/plugins/load/load_mesh.py +++ b/client/ayon_core/hosts/substancepainter/plugins/load/load_mesh.py @@ -101,9 +101,9 @@ class SubstanceLoadProjectMesh(load.LoaderPlugin): self.update(container, context) def update(self, container, context): - repre_doc = context["representation"] + repre_entity = context["representation"] - path = get_representation_path(repre_doc) + path = get_representation_path(repre_entity) # Reload the mesh container_options = container.get("options", {}) @@ -122,7 +122,7 @@ class SubstanceLoadProjectMesh(load.LoaderPlugin): # Update container representation object_name = container["objectName"] - update_data = {"representation": str(repre_doc["_id"])} + update_data = {"representation": repre_entity["id"]} set_container_metadata(object_name, update_data, update=True) def remove(self, container): From 45a4985997170ccd387074ea541c0d3823431da7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 12:45:42 +0100 Subject: [PATCH 490/573] uses representation entity --- client/ayon_core/hosts/tvpaint/api/pipeline.py | 2 +- .../hosts/tvpaint/plugins/load/load_reference_image.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/hosts/tvpaint/api/pipeline.py b/client/ayon_core/hosts/tvpaint/api/pipeline.py index 27f2981a2e..6f5c4d49d4 100644 --- a/client/ayon_core/hosts/tvpaint/api/pipeline.py +++ b/client/ayon_core/hosts/tvpaint/api/pipeline.py @@ -233,7 +233,7 @@ def containerise( "name": name, "namespace": namespace, "loader": str(loader), - "representation": str(context["representation"]["_id"]) + "representation": context["representation"]["id"] } if current_containers is None: current_containers = get_containers() diff --git a/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py b/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py index 995243bfea..b9276a8f7c 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py +++ b/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py @@ -218,9 +218,9 @@ class LoadImage(plugin.Loader): removed. """ - repre_doc = context["representation"] + repre_entity = context["representation"] # Create new containers first - context = get_representation_context(repre_doc) + context = get_representation_context(repre_entity["id"]) # Get layer ids from previous container old_layer_names = self.get_members_from_container(container) From 0a2b676713ec5a88a4e1dee82dea5a22f07a9bc9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 12:46:16 +0100 Subject: [PATCH 491/573] unreal uses representation entity and forgetten changes --- client/ayon_core/hosts/unreal/api/pipeline.py | 2 +- .../plugins/load/load_alembic_animation.py | 23 ++--- .../unreal/plugins/load/load_animation.py | 32 ++++--- .../hosts/unreal/plugins/load/load_camera.py | 37 +++++--- .../plugins/load/load_geometrycache_abc.py | 72 ++++++++++----- .../hosts/unreal/plugins/load/load_layout.py | 74 +++++++-------- .../plugins/load/load_layout_existing.py | 89 ++++++++++--------- .../plugins/load/load_skeletalmesh_abc.py | 56 ++++++++---- .../plugins/load/load_skeletalmesh_fbx.py | 56 ++++++++---- .../plugins/load/load_staticmesh_abc.py | 65 ++++++++++---- .../plugins/load/load_staticmesh_fbx.py | 55 ++++++++---- .../hosts/unreal/plugins/load/load_uasset.py | 27 +++--- .../unreal/plugins/load/load_yeticache.py | 27 +++--- .../unreal/plugins/publish/extract_layout.py | 8 +- 14 files changed, 393 insertions(+), 230 deletions(-) diff --git a/client/ayon_core/hosts/unreal/api/pipeline.py b/client/ayon_core/hosts/unreal/api/pipeline.py index eb093a2e6f..1f937437a4 100644 --- a/client/ayon_core/hosts/unreal/api/pipeline.py +++ b/client/ayon_core/hosts/unreal/api/pipeline.py @@ -262,7 +262,7 @@ def containerise(name, namespace, nodes, context, loader=None, suffix="_CON"): "name": new_name, "namespace": namespace, "loader": str(loader), - "representation": context["representation"]["_id"], + "representation": context["representation"]["id"], } # 3 - imprint data imprint(f"{path}/{container_name}", data) diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_alembic_animation.py b/client/ayon_core/hosts/unreal/plugins/load/load_alembic_animation.py index d75cf207b5..9b8d22fb5e 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_alembic_animation.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_alembic_animation.py @@ -78,12 +78,12 @@ class AnimationAlembicLoader(plugin.Loader): asset_name = "{}_{}".format(folder_name, name) else: asset_name = "{}".format(name) - version = context.get('version') + version = context["version"]["version"] # Check if version is hero version and use different name - if not version.get("name") and version.get('type') == "hero_version": + if version < 0: name_version = f"{name}_hero" else: - name_version = f"{name}_v{version.get('name'):03d}" + name_version = f"{name}_v{version:03d}" tools = unreal.AssetToolsHelpers().get_asset_tools() asset_dir, container_name = tools.create_unique_asset_name( @@ -107,16 +107,17 @@ class AnimationAlembicLoader(plugin.Loader): data = { "schema": "ayon:container-2.0", "id": AYON_CONTAINER_ID, - "asset": folder_path, "folder_path": folder_path, "namespace": asset_dir, "container_name": container_name, "asset_name": asset_name, "loader": str(self.__class__.__name__), - "representation": context["representation"]["_id"], - "parent": context["representation"]["parent"], - "family": product_type, + "representation": context["representation"]["id"], + "parent": context["representation"]["versionId"], "product_type": product_type, + # TODO these should be probably removed + "asset": folder_path, + "family": product_type, } unreal_pipeline.imprint( f"{asset_dir}/{container_name}", data) @@ -132,8 +133,8 @@ class AnimationAlembicLoader(plugin.Loader): def update(self, container, context): folder_name = container["asset_name"] - repre_doc = context["representation"] - source_path = get_representation_path(repre_doc) + repre_entity = context["representation"] + source_path = get_representation_path(repre_entity) destination_path = container["namespace"] task = self.get_task( @@ -150,8 +151,8 @@ class AnimationAlembicLoader(plugin.Loader): unreal_pipeline.imprint( container_path, { - "representation": str(repre_doc["_id"]), - "parent": str(repre_doc["parent"]) + "representation": repre_entity["id"], + "parent": repre_entity["versionId"], }) asset_content = unreal.EditorAssetLibrary.list_assets( diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_animation.py b/client/ayon_core/hosts/unreal/plugins/load/load_animation.py index 1832841ef0..b2c066c871 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_animation.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_animation.py @@ -140,14 +140,17 @@ class AnimationFBXLoader(plugin.Loader): list(str): list of container content """ # Create directory for asset and Ayon container - hierarchy = context.get('asset').get('data').get('parents') root = "/Game/Ayon" - asset = context.get('asset').get('name') + folder_path = context["folder"]["path"] + hierarchy = folder_path.lstrip("/").split("/") + folder_name = hierarchy.pop(-1) + product_type = context["product"]["productType"] + suffix = "_CON" - asset_name = f"{asset}_{name}" if asset else f"{name}" + asset_name = f"{folder_name}_{name}" if folder_name else f"{name}" tools = unreal.AssetToolsHelpers().get_asset_tools() asset_dir, container_name = tools.create_unique_asset_name( - f"{root}/Animations/{asset}/{name}", suffix="") + f"{root}/Animations/{folder_name}/{name}", suffix="") ar = unreal.AssetRegistryHelpers.get_asset_registry() @@ -161,7 +164,7 @@ class AnimationFBXLoader(plugin.Loader): hierarchy_dir = root for h in hierarchy: hierarchy_dir = f"{hierarchy_dir}/{h}" - hierarchy_dir = f"{hierarchy_dir}/{asset}" + hierarchy_dir = f"{hierarchy_dir}/{folder_name}" _filter = unreal.ARFilter( class_names=["World"], @@ -226,14 +229,17 @@ class AnimationFBXLoader(plugin.Loader): data = { "schema": "ayon:container-2.0", "id": AYON_CONTAINER_ID, - "asset": asset, "namespace": asset_dir, "container_name": container_name, "asset_name": asset_name, "loader": str(self.__class__.__name__), - "representation": context["representation"]["_id"], - "parent": context["representation"]["parent"], - "family": context["representation"]["context"]["family"] + "representation": context["representation"]["id"], + "parent": context["representation"]["versionId"], + "folder_path": folder_path, + "product_type": product_type, + # TODO these shold be probably removed + "asset": folder_path, + "family": product_type } unreal_pipeline.imprint(f"{asset_dir}/{container_name}", data) @@ -247,9 +253,9 @@ class AnimationFBXLoader(plugin.Loader): unreal.EditorLevelLibrary.load_level(master_level) def update(self, container, context): - repre_doc = context["representation"] + repre_entity = context["representation"] folder_name = container["asset_name"] - source_path = get_representation_path(repre_doc) + source_path = get_representation_path(repre_entity) folder_entity = get_current_project_folder(fields=["attrib.fps"]) destination_path = container["namespace"] @@ -306,8 +312,8 @@ class AnimationFBXLoader(plugin.Loader): unreal_pipeline.imprint( container_path, { - "representation": str(repre_doc["_id"]), - "parent": str(repre_doc["parent"]) + "representation": repre_entity["id"], + "parent": repre_entity["versionId"], }) asset_content = EditorAssetLibrary.list_assets( diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_camera.py b/client/ayon_core/hosts/unreal/plugins/load/load_camera.py index 26ec80c804..ed159d31bd 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_camera.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_camera.py @@ -14,6 +14,7 @@ from unreal import ( from ayon_core.pipeline import ( AYON_CONTAINER_ID, get_current_project_name, + get_representation_path, ) from ayon_core.hosts.unreal.api import plugin from ayon_core.hosts.unreal.api.pipeline import ( @@ -246,18 +247,21 @@ class CameraLoader(plugin.Loader): create_container( container=container_name, path=asset_dir) + product_type = context["product"]["productType"] data = { "schema": "ayon:container-2.0", "id": AYON_CONTAINER_ID, - "asset": folder_name, "folder_path": folder_path, "namespace": asset_dir, "container_name": container_name, "asset_name": asset_name, "loader": str(self.__class__.__name__), - "representation": context["representation"]["_id"], - "parent": context["representation"]["parent"], - "family": context["representation"]["context"]["family"] + "representation": context["representation"]["id"], + "parent": context["representation"]["versionId"], + "product_type": product_type, + # TODO these should be probably removed + "asset": folder_name, + "family": product_type, } imprint(f"{asset_dir}/{container_name}", data) @@ -393,28 +397,33 @@ class CameraLoader(plugin.Loader): sub_scene.set_sequence(new_sequence) - repre_doc = context["representation"] + repre_entity = context["representation"] + repre_path = get_representation_path(repre_entity) self._import_camera( EditorLevelLibrary.get_editor_world(), new_sequence, new_sequence.get_bindings(), settings, - str(repre_doc["data"]["path"]) + repre_path ) # Set range of all sections # Changing the range of the section is not enough. We need to change # the frame of all the keys in the section. project_name = get_current_project_name() - asset = container.get('asset') - data = get_asset_by_name(project_name, asset)["data"] + folder_path = container.get("folder_path") + if folder_path is None: + folder_path = container.get("asset") + folder_entity = ayon_api.get_folder_by_path(project_name, folder_path) + folder_attributes = folder_entity["attrib"] + clip_in = folder_attributes["clipIn"] + clip_out = folder_attributes["clipOut"] + frame_start = folder_attributes["frameStart"] for possessable in new_sequence.get_possessables(): for tracks in possessable.get_tracks(): for section in tracks.get_sections(): - section.set_range( - data.get('clipIn'), - data.get('clipOut') + 1) + section.set_range(clip_in, clip_out + 1) for channel in section.get_all_channels(): for key in channel.get_keys(): old_time = key.get_time().get_editor_property( @@ -422,13 +431,13 @@ class CameraLoader(plugin.Loader): old_time_value = old_time.get_editor_property( 'value') new_time = old_time_value + ( - data.get('clipIn') - data.get('frameStart') + clip_in - frame_start ) key.set_time(unreal.FrameNumber(value=new_time)) data = { - "representation": str(repre_doc["_id"]), - "parent": str(repre_doc["parent"]) + "representation": repre_entity["id"], + "parent": repre_entity["versionId"], } imprint(f"{asset_dir}/{container.get('container_name')}", data) diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py b/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py index c2f183480c..d0c936ed06 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_geometrycache_abc.py @@ -84,22 +84,32 @@ class PointCacheAlembicLoader(plugin.Loader): create_container(container=container_name, path=asset_dir) def imprint( - self, asset, asset_dir, container_name, asset_name, representation, - frame_start, frame_end + self, + folder_path, + asset_dir, + container_name, + asset_name, + representation, + frame_start, + frame_end, + product_type, ): data = { "schema": "ayon:container-2.0", "id": AYON_CONTAINER_ID, - "asset": asset, "namespace": asset_dir, "container_name": container_name, "asset_name": asset_name, "loader": str(self.__class__.__name__), - "representation": representation["_id"], - "parent": representation["parent"], - "family": representation["context"]["family"], + "representation": representation["id"], + "parent": representation["versionId"], "frame_start": frame_start, - "frame_end": frame_end + "frame_end": frame_end, + "product_type": product_type, + "folder_path": folder_path, + # TODO these should be probably removed + "family": product_type, + "asset": folder_path, } imprint(f"{asset_dir}/{container_name}", data) @@ -119,24 +129,28 @@ class PointCacheAlembicLoader(plugin.Loader): list(str): list of container content """ # Create directory for asset and Ayon container - asset = context.get('asset').get('name') + folder_entity = context["folder"] + folder_path = folder_entity["path"] + folder_name = folder_entity["name"] + folder_attributes = folder_entity["attrib"] + suffix = "_CON" - asset_name = f"{asset}_{name}" if asset else f"{name}" - version = context.get('version') + asset_name = f"{folder_name}_{name}" if folder_name else f"{name}" + version = context["version"]["version"] # Check if version is hero version and use different name - if not version.get("name") and version.get('type') == "hero_version": + if version < 0: name_version = f"{name}_hero" else: - name_version = f"{name}_v{version.get('name'):03d}" + name_version = f"{name}_v{version:03d}" tools = unreal.AssetToolsHelpers().get_asset_tools() asset_dir, container_name = tools.create_unique_asset_name( - f"{self.root}/{asset}/{name_version}", suffix="") + f"{self.root}/{folder_name}/{name_version}", suffix="") container_name += suffix - frame_start = context.get('asset').get('data').get('frameStart') - frame_end = context.get('asset').get('data').get('frameEnd') + frame_start = folder_attributes.get("frameStart") + frame_end = folder_attributes.get("frameEnd") # If frame start and end are the same, we increase the end frame by # one, otherwise Unreal will not import it @@ -151,8 +165,15 @@ class PointCacheAlembicLoader(plugin.Loader): frame_start, frame_end) self.imprint( - asset, asset_dir, container_name, asset_name, - context["representation"], frame_start, frame_end) + folder_path, + asset_dir, + container_name, + asset_name, + context["representation"], + frame_start, + frame_end, + context["product"]["productType"] + ) asset_content = unreal.EditorAssetLibrary.list_assets( asset_dir, recursive=True, include_folder=True @@ -165,10 +186,12 @@ class PointCacheAlembicLoader(plugin.Loader): def update(self, container, context): # Create directory for folder and Ayon container + folder_path = context["folder"]["path"] folder_name = context["folder"]["name"] product_name = context["product"]["name"] + product_type = context["product"]["productType"] version = context["version"]["version"] - repre_doc = context["representation"] + repre_entity = context["representation"] suffix = "_CON" asset_name = product_name @@ -190,15 +213,22 @@ class PointCacheAlembicLoader(plugin.Loader): frame_end = int(container.get("frame_end")) if not unreal.EditorAssetLibrary.does_directory_exist(asset_dir): - path = get_representation_path(repre_doc) + path = get_representation_path(repre_entity) self.import_and_containerize( path, asset_dir, asset_name, container_name, frame_start, frame_end) self.imprint( - folder_name, asset_dir, container_name, asset_name, repre_doc, - frame_start, frame_end) + folder_path, + asset_dir, + container_name, + asset_name, + repre_entity, + frame_start, + frame_end, + product_type + ) asset_content = unreal.EditorAssetLibrary.list_assets( asset_dir, recursive=True, include_folder=False diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_layout.py b/client/ayon_core/hosts/unreal/plugins/load/load_layout.py index 47e99b9534..beb94ada7b 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_layout.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_layout.py @@ -17,7 +17,6 @@ from unreal import ( ) import ayon_api -from ayon_core.client import get_representations from ayon_core.pipeline import ( discover_loader_plugins, loaders_from_representation, @@ -291,7 +290,7 @@ class LayoutLoader(plugin.Loader): sec_params = section.get_editor_property('params') sec_params.set_editor_property('animation', animation) - def _get_repre_docs_by_version_id(self, data): + def _get_repre_entities_by_version_id(self, data): version_ids = { element.get("version") for element in data @@ -304,15 +303,15 @@ class LayoutLoader(plugin.Loader): return output project_name = get_current_project_name() - repre_docs = get_representations( + repre_entities = ayon_api.get_representations( project_name, - representation_names=["fbx", "abc"], + representation_names={"fbx", "abc"}, version_ids=version_ids, - fields=["_id", "parent", "name"] + fields={"id", "versionId", "name"} ) - for repre_doc in repre_docs: - version_id = str(repre_doc["parent"]) - output[version_id].append(repre_doc) + for repre_entity in repre_entities: + version_id = repre_entity["versionId"] + output[version_id].append(repre_entity) return output def _process(self, lib_path, asset_dir, sequence, repr_loaded=None): @@ -334,47 +333,50 @@ class LayoutLoader(plugin.Loader): loaded_assets = [] - repre_docs_by_version_id = self._get_repre_docs_by_version_id(data) + repre_entities_by_version_id = self._get_repre_entities_by_version_id( + data + ) for element in data: - representation = None + repre_id = None repr_format = None if element.get('representation'): - repre_docs = repre_docs_by_version_id[element.get("version")] - if not repre_docs: + version_id = element.get("version") + repre_entities = repre_entities_by_version_id[version_id] + if not repre_entities: self.log.error( - f"No valid representation found for version " - f"{element.get('version')}") + f"No valid representation found for version" + f" {version_id}") continue - repre_doc = repre_docs[0] - representation = str(repre_doc["_id"]) - repr_format = repre_doc["name"] + repre_entity = repre_entities[0] + repre_id = repre_entity["id"] + repr_format = repre_entity["name"] # This is to keep compatibility with old versions of the # json format. elif element.get('reference_fbx'): - representation = element.get('reference_fbx') + repre_id = element.get('reference_fbx') repr_format = 'fbx' elif element.get('reference_abc'): - representation = element.get('reference_abc') + repre_id = element.get('reference_abc') repr_format = 'abc' # If reference is None, this element is skipped, as it cannot be # imported in Unreal - if not representation: + if not repre_id: continue instance_name = element.get('instance_name') skeleton = None - if representation not in repr_loaded: - repr_loaded.append(representation) + if repre_id not in repr_loaded: + repr_loaded.append(repre_id) product_type = element.get("product_type") if product_type is None: product_type = element.get("family") loaders = loaders_from_representation( - all_loaders, representation) + all_loaders, repre_id) loader = None @@ -385,7 +387,7 @@ class LayoutLoader(plugin.Loader): if not loader: self.log.error( - f"No valid loader found for {representation}") + f"No valid loader found for {repre_id}") continue options = { @@ -394,7 +396,7 @@ class LayoutLoader(plugin.Loader): assets = load_container( loader, - representation, + repre_id, namespace=instance_name, options=options ) @@ -414,8 +416,8 @@ class LayoutLoader(plugin.Loader): item for item in data if ((item.get('version') and item.get('version') == element.get('version')) or - item.get('reference_fbx') == representation or - item.get('reference_abc') == representation)] + item.get('reference_fbx') == repre_id or + item.get('reference_abc') == repre_id)] for instance in instances: # transform = instance.get('transform') @@ -439,9 +441,9 @@ class LayoutLoader(plugin.Loader): bindings_dict[inst] = bindings if skeleton: - skeleton_dict[representation] = skeleton + skeleton_dict[repre_id] = skeleton else: - skeleton = skeleton_dict.get(representation) + skeleton = skeleton_dict.get(repre_id) animation_file = element.get('animation') @@ -655,8 +657,8 @@ class LayoutLoader(plugin.Loader): "container_name": container_name, "asset_name": asset_name, "loader": str(self.__class__.__name__), - "representation": context["representation"]["_id"], - "parent": context["representation"]["parent"], + "representation": context["representation"]["id"], + "parent": context["representation"]["versionId"], "family": context["representation"]["context"]["family"], "loaded_assets": loaded_assets } @@ -694,7 +696,7 @@ class LayoutLoader(plugin.Loader): asset_dir = container.get('namespace') folder_entity = context["folder"] - repre_doc = context["representation"] + repre_entity = context["representation"] hierarchy = folder_entity["path"].lstrip("/").split("/") first_parent_name = hierarchy[0] @@ -746,14 +748,14 @@ class LayoutLoader(plugin.Loader): EditorAssetLibrary.delete_directory(f"{asset_dir}/animations/") - source_path = get_representation_path(repre_doc) + source_path = get_representation_path(repre_entity) loaded_assets = self._process(source_path, asset_dir, sequence) data = { - "representation": str(repre_doc["_id"]), - "parent": str(repre_doc["parent"]), - "loaded_assets": loaded_assets + "representation": repre_entity["id"], + "parent": repre_entity["versionId"], + "loaded_assets": loaded_assets, } imprint( "{}/{}".format(asset_dir, container.get('container_name')), data) diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py b/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py index c896e7688b..144df10fe0 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py @@ -5,7 +5,6 @@ import unreal from unreal import EditorLevelLibrary import ayon_api -from ayon_core.client import get_representations from ayon_core.pipeline import ( discover_loader_plugins, loaders_from_representation, @@ -65,7 +64,6 @@ class ExistingLayoutLoader(plugin.Loader): data = { "schema": "ayon:container-2.0", "id": AYON_CONTAINER_ID, - "asset": folder_path, "folder_path": folder_path, "namespace": asset_dir, "container_name": container_name, @@ -73,8 +71,10 @@ class ExistingLayoutLoader(plugin.Loader): # "loader": str(self.__class__.__name__), "representation": representation, "parent": version_id, - "family": product_type, "product_type": product_type, + # TODO these shold be probably removed + "asset": folder_path, + "family": product_type, } upipeline.imprint( @@ -201,19 +201,19 @@ class ExistingLayoutLoader(plugin.Loader): return assets - def _get_valid_repre_docs(self, project_name, version_ids): + def _get_valid_repre_entities(self, project_name, version_ids): valid_formats = ['fbx', 'abc'] - repre_docs = list(get_representations( + repre_entities = list(ayon_api.get_representations( project_name, representation_names=valid_formats, version_ids=version_ids )) - repre_doc_by_version_id = {} - for repre_doc in repre_docs: - version_id = str(repre_doc["parent"]) - repre_doc_by_version_id[version_id] = repre_doc - return repre_doc_by_version_id + repre_entities_by_version_id = {} + for repre_entity in repre_entities: + version_id = repre_entity["versionId"] + repre_entities_by_version_id[version_id] = repre_entity + return repre_entities_by_version_id def _process(self, lib_path, project_name): ar = unreal.AssetRegistryHelpers.get_asset_registry() @@ -232,44 +232,47 @@ class ExistingLayoutLoader(plugin.Loader): repre_ids.add(repre_id) elements.append(element) - repre_docs = get_representations( + repre_entities = ayon_api.get_representations( project_name, representation_ids=repre_ids ) - repre_docs_by_id = { - str(repre_doc["_id"]): repre_doc - for repre_doc in repre_docs + repre_entities_by_id = { + repre_entity["id"]: repre_entity + for repre_entity in repre_entities } layout_data = [] version_ids = set() for element in elements: repre_id = element.get("representation") - repre_doc = repre_docs_by_id.get(repre_id) - if not repre_doc: + repre_entity = repre_entities_by_id.get(repre_id) + if not repre_entity: raise AssertionError("Representation not found") - if not (repre_doc.get('data') or repre_doc['data'].get('path')): + if not ( + repre_entity.get("attrib") + or repre_entity["attrib"].get("path") + ): raise AssertionError("Representation does not have path") - if not repre_doc.get('context'): + if not repre_entity.get('context'): raise AssertionError("Representation does not have context") - layout_data.append((repre_doc, element)) - version_ids.add(repre_doc["parent"]) + layout_data.append((repre_entity, element)) + version_ids.add(repre_entity["versionId"]) repre_parents_by_id = ayon_api.get_representation_parents( - project_name, repre_docs_by_id.keys() + project_name, repre_entities_by_id.keys() ) # Prequery valid repre documents for all elements at once - valid_repre_doc_by_version_id = self._get_valid_repre_docs( + valid_repre_entities_by_version_id = self._get_valid_repre_entities( project_name, version_ids) containers = [] actors_matched = [] - for (repre_doc, lasset) in layout_data: + for (repre_entity, lasset) in layout_data: # For every actor in the scene, check if it has a representation in # those we got from the JSON. If so, create a container for it. # Otherwise, remove it from the scene. found = False - repre_id = repre_doc["_id"] + repre_id = repre_entity["id"] repre_parents = repre_parents_by_id[repre_id] folder_path = repre_parents.folder["path"] folder_name = repre_parents.folder["name"] @@ -291,7 +294,7 @@ class ExistingLayoutLoader(plugin.Loader): path = Path(filename) if (not path.name or - path.name not in repre_doc["data"]["path"]): + path.name not in repre_entity["attrib"]["path"]): continue actor.set_actor_label(lasset.get('instance_name')) @@ -303,8 +306,8 @@ class ExistingLayoutLoader(plugin.Loader): f"{folder_name}_{product_name}", mesh_path, folder_path, - repre_doc["_id"], - repre_doc["parent"], + repre_entity["id"], + repre_entity["versionId"], product_type ) containers.append(container) @@ -333,18 +336,18 @@ class ExistingLayoutLoader(plugin.Loader): loaded = False for container in all_containers: - repr = container.get('representation') + repre_id = container.get('representation') - if not repr == repre_doc["_id"]: + if not repre_id == repre_entity["id"]: continue asset_dir = container.get('namespace') - filter = unreal.ARFilter( + arfilter = unreal.ARFilter( class_names=["StaticMesh"], package_paths=[asset_dir], recursive_paths=False) - assets = ar.get_assets(filter) + assets = ar.get_assets(arfilter) for asset in assets: obj = asset.get_asset() @@ -357,8 +360,9 @@ class ExistingLayoutLoader(plugin.Loader): if loaded: continue + version_id = lasset.get('version') assets = self._load_asset( - valid_repre_doc_by_version_id.get(lasset.get('version')), + valid_repre_entities_by_version_id.get(version_id), lasset.get('representation'), lasset.get('instance_name'), lasset.get('family') @@ -414,17 +418,18 @@ class ExistingLayoutLoader(plugin.Loader): data = { "schema": "ayon:container-2.0", "id": AYON_CONTAINER_ID, - "asset": folder_path, "folder_path": folder_path, "namespace": curr_level_path, "container_name": container_name, "asset_name": asset_name, "loader": str(self.__class__.__name__), - "representation": context["representation"]["_id"], - "parent": context["representation"]["parent"], - "family": product_type, + "representation": context["representation"]["id"], + "parent": context["representation"]["versionId"], "product_type": product_type, - "loaded_assets": containers + "loaded_assets": containers, + # TODO these shold be probably removed + "asset": folder_path, + "family": product_type, } upipeline.imprint(f"{curr_level_path}/{container_name}", data) @@ -432,15 +437,15 @@ class ExistingLayoutLoader(plugin.Loader): asset_dir = container.get('namespace') project_name = context["project"]["name"] - repre_doc = context["representation"] + repre_entity = context["representation"] - source_path = get_representation_path(repre_doc) + source_path = get_representation_path(repre_entity) containers = self._process(source_path, project_name) data = { - "representation": str(repre_doc["_id"]), - "parent": str(repre_doc["parent"]), - "loaded_assets": containers + "representation": repre_entity["id"], + "loaded_assets": containers, + "parent": repre_entity["versionId"], } upipeline.imprint( "{}/{}".format(asset_dir, container.get('container_name')), data) diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py index 9879b0b967..4d9c0554d8 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_abc.py @@ -73,19 +73,28 @@ class SkeletalMeshAlembicLoader(plugin.Loader): create_container(container=container_name, path=asset_dir) def imprint( - self, asset, asset_dir, container_name, asset_name, representation + self, + folder_path, + asset_dir, + container_name, + asset_name, + representation, + product_type ): data = { "schema": "ayon:container-2.0", "id": AYON_CONTAINER_ID, - "asset": asset, + "folder_path": folder_path, "namespace": asset_dir, "container_name": container_name, "asset_name": asset_name, "loader": str(self.__class__.__name__), - "representation": representation["_id"], - "parent": representation["parent"], - "family": representation["context"]["family"] + "representation": representation["id"], + "parent": representation["versionId"], + "product_type": product_type, + # TODO these should be probably removed + "asset": folder_path, + "family": product_type, } imprint(f"{asset_dir}/{container_name}", data) @@ -105,15 +114,16 @@ class SkeletalMeshAlembicLoader(plugin.Loader): list(str): list of container content """ # Create directory for asset and ayon container - asset = context.get('asset').get('name') + folder_path = context["folder"]["path"] + folder_name = context["folder"]["name"] suffix = "_CON" - asset_name = f"{asset}_{name}" if asset else f"{name}" - version = context.get('version') + asset_name = f"{folder_name}_{name}" if folder_name else f"{name}" + version = context["version"]["version"] # Check if version is hero version and use different name - if not version.get("name") and version.get('type') == "hero_version": + if version < 0: name_version = f"{name}_hero" else: - name_version = f"{name}_v{version.get('name'):03d}" + name_version = f"{name}_v{version:03d}" default_conversion = False if options.get("default_conversion"): @@ -121,7 +131,7 @@ class SkeletalMeshAlembicLoader(plugin.Loader): tools = unreal.AssetToolsHelpers().get_asset_tools() asset_dir, container_name = tools.create_unique_asset_name( - f"{self.root}/{asset}/{name_version}", suffix="") + f"{self.root}/{folder_name}/{name_version}", suffix="") container_name += suffix @@ -131,9 +141,15 @@ class SkeletalMeshAlembicLoader(plugin.Loader): self.import_and_containerize(path, asset_dir, asset_name, container_name, default_conversion) + product_type = context["product"]["productType"] self.imprint( - asset, asset_dir, container_name, asset_name, - context["representation"]) + folder_path, + asset_dir, + container_name, + asset_name, + context["representation"], + product_type + ) asset_content = unreal.EditorAssetLibrary.list_assets( asset_dir, recursive=True, include_folder=True @@ -145,10 +161,12 @@ class SkeletalMeshAlembicLoader(plugin.Loader): return asset_content def update(self, container, context): + folder_path = context["folder"]["path"] folder_name = context["folder"]["name"] product_name = context["product"]["name"] + product_type = context["product"]["productType"] version = context["version"]["version"] - repre_doc = context["representation"] + repre_entity = context["representation"] # Create directory for folder and Ayon container suffix = "_CON" @@ -167,13 +185,19 @@ class SkeletalMeshAlembicLoader(plugin.Loader): container_name += suffix if not unreal.EditorAssetLibrary.does_directory_exist(asset_dir): - path = get_representation_path(repre_doc) + path = get_representation_path(repre_entity) self.import_and_containerize(path, asset_dir, asset_name, container_name) self.imprint( - folder_name, asset_dir, container_name, asset_name, repre_doc) + folder_path, + asset_dir, + container_name, + asset_name, + repre_entity, + product_type, + ) asset_content = unreal.EditorAssetLibrary.list_assets( asset_dir, recursive=True, include_folder=False diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py index f643e027d2..990ad4b977 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_skeletalmesh_fbx.py @@ -78,19 +78,28 @@ class SkeletalMeshFBXLoader(plugin.Loader): create_container(container=container_name, path=asset_dir) def imprint( - self, asset, asset_dir, container_name, asset_name, representation + self, + folder_path, + asset_dir, + container_name, + asset_name, + representation, + product_type ): data = { "schema": "ayon:container-2.0", "id": AYON_CONTAINER_ID, - "asset": asset, + "folder_path": folder_path, "namespace": asset_dir, "container_name": container_name, "asset_name": asset_name, "loader": str(self.__class__.__name__), - "representation": representation["_id"], - "parent": representation["parent"], - "family": representation["context"]["family"] + "representation": representation["id"], + "parent": representation["versionId"], + "product_type": product_type, + # TODO these should be probably removed + "asset": folder_path, + "family": product_type, } imprint(f"{asset_dir}/{container_name}", data) @@ -110,19 +119,21 @@ class SkeletalMeshFBXLoader(plugin.Loader): list(str): list of container content """ # Create directory for asset and Ayon container - asset = context.get('asset').get('name') + folder_name = context["folder"]["name"] + product_type = context["product"]["productType"] suffix = "_CON" - asset_name = f"{asset}_{name}" if asset else f"{name}" - version = context.get('version') + asset_name = f"{folder_name}_{name}" if folder_name else f"{name}" + version_entity = context["version"] # Check if version is hero version and use different name - if not version.get("name") and version.get('type') == "hero_version": + version = version_entity["version"] + if version < 0: name_version = f"{name}_hero" else: - name_version = f"{name}_v{version.get('name'):03d}" + name_version = f"{name}_v{version:03d}" tools = unreal.AssetToolsHelpers().get_asset_tools() asset_dir, container_name = tools.create_unique_asset_name( - f"{self.root}/{asset}/{name_version}", suffix="" + f"{self.root}/{folder_name}/{name_version}", suffix="" ) container_name += suffix @@ -134,8 +145,13 @@ class SkeletalMeshFBXLoader(plugin.Loader): path, asset_dir, asset_name, container_name) self.imprint( - asset, asset_dir, container_name, asset_name, - context["representation"]) + folder_name, + asset_dir, + container_name, + asset_name, + context["representation"], + product_type + ) asset_content = unreal.EditorAssetLibrary.list_assets( asset_dir, recursive=True, include_folder=True @@ -147,10 +163,12 @@ class SkeletalMeshFBXLoader(plugin.Loader): return asset_content def update(self, container, context): + folder_path = context["folder"]["path"] folder_name = context["folder"]["name"] product_name = context["product"]["name"] + product_type = context["product"]["productType"] version = context["version"]["version"] - repre_doc = context["representation"] + repre_entity = context["representation"] # Create directory for asset and Ayon container suffix = "_CON" @@ -169,13 +187,19 @@ class SkeletalMeshFBXLoader(plugin.Loader): container_name += suffix if not unreal.EditorAssetLibrary.does_directory_exist(asset_dir): - path = get_representation_path(repre_doc) + path = get_representation_path(repre_entity) self.import_and_containerize( path, asset_dir, asset_name, container_name) self.imprint( - folder_name, asset_dir, container_name, asset_name, repre_doc) + folder_path, + asset_dir, + container_name, + asset_name, + repre_entity, + product_type + ) asset_content = unreal.EditorAssetLibrary.list_assets( asset_dir, recursive=True, include_folder=False diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_abc.py b/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_abc.py index 9056a471d8..5e4a28a031 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_abc.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_abc.py @@ -74,19 +74,28 @@ class StaticMeshAlembicLoader(plugin.Loader): create_container(container=container_name, path=asset_dir) def imprint( - self, asset, asset_dir, container_name, asset_name, representation + self, + folder_path, + asset_dir, + container_name, + asset_name, + representation, + product_type, ): data = { "schema": "ayon:container-2.0", "id": AYON_CONTAINER_ID, - "asset": asset, + "folder_path": folder_path, "namespace": asset_dir, "container_name": container_name, "asset_name": asset_name, "loader": str(self.__class__.__name__), - "representation": representation["_id"], - "parent": representation["parent"], - "family": representation["context"]["family"] + "representation": representation["id"], + "parent": representation["versionId"], + "product_type": product_type, + # TODO these should be probably removed + "asset": folder_path, + "family": product_type } imprint(f"{asset_dir}/{container_name}", data) @@ -106,15 +115,17 @@ class StaticMeshAlembicLoader(plugin.Loader): list(str): list of container content """ # Create directory for asset and Ayon container - asset = context.get('asset').get('name') + folder_path = context["folder"]["path"] + folder_name = context["folder"]["path"] + suffix = "_CON" - asset_name = f"{asset}_{name}" if asset else f"{name}" - version = context.get('version') + asset_name = f"{folder_name}_{name}" if folder_name else f"{name}" + version = context["version"]["version"] # Check if version is hero version and use different name - if not version.get("name") and version.get('type') == "hero_version": + if version < 0: name_version = f"{name}_hero" else: - name_version = f"{name}_v{version.get('name'):03d}" + name_version = f"{name}_v{version:03d}" default_conversion = False if options.get("default_conversion"): @@ -122,7 +133,7 @@ class StaticMeshAlembicLoader(plugin.Loader): tools = unreal.AssetToolsHelpers().get_asset_tools() asset_dir, container_name = tools.create_unique_asset_name( - f"{self.root}/{asset}/{name_version}", suffix="") + f"{self.root}/{folder_name}/{name_version}", suffix="") container_name += suffix @@ -132,9 +143,15 @@ class StaticMeshAlembicLoader(plugin.Loader): self.import_and_containerize(path, asset_dir, asset_name, container_name, default_conversion) + product_type = context["product"]["productType"] self.imprint( - asset, asset_dir, container_name, asset_name, - context["representation"]) + folder_path, + asset_dir, + container_name, + asset_name, + context["representation"], + product_type + ) asset_content = unreal.EditorAssetLibrary.list_assets( asset_dir, recursive=True, include_folder=False @@ -146,18 +163,24 @@ class StaticMeshAlembicLoader(plugin.Loader): return asset_content def update(self, container, context): + folder_path = context["folder"]["path"] folder_name = context["folder"]["name"] product_name = context["product"]["name"] - repre_doc = context["representation"] + product_type = context["product"]["productType"] + repre_entity = context["representation"] # Create directory for asset and Ayon container suffix = "_CON" asset_name = product_name if folder_name: asset_name = f"{folder_name}_{product_name}" - version = context.get('version') + version = context["version"]["version"] # Check if version is hero version and use different name - name_version = f"{product_name}_v{version:03d}" if version else f"{product_name}_hero" + if version < 0: + name_version = f"{product_name}_hero" + else: + name_version = f"{product_name}_v{version:03d}" + tools = unreal.AssetToolsHelpers().get_asset_tools() asset_dir, container_name = tools.create_unique_asset_name( f"{self.root}/{folder_name}/{name_version}", suffix="") @@ -165,13 +188,19 @@ class StaticMeshAlembicLoader(plugin.Loader): container_name += suffix if not unreal.EditorAssetLibrary.does_directory_exist(asset_dir): - path = get_representation_path(repre_doc) + path = get_representation_path(repre_entity) self.import_and_containerize(path, asset_dir, asset_name, container_name) self.imprint( - folder_name, asset_dir, container_name, asset_name, repre_doc) + folder_path, + asset_dir, + container_name, + asset_name, + repre_entity, + product_type + ) asset_content = unreal.EditorAssetLibrary.list_assets( asset_dir, recursive=True, include_folder=False diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py b/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py index a23290997e..1db2dcf676 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_staticmesh_fbx.py @@ -66,19 +66,28 @@ class StaticMeshFBXLoader(plugin.Loader): create_container(container=container_name, path=asset_dir) def imprint( - self, asset, asset_dir, container_name, asset_name, representation + self, + folder_path, + asset_dir, + container_name, + asset_name, + repre_entity, + product_type ): data = { "schema": "ayon:container-2.0", "id": AYON_CONTAINER_ID, - "asset": asset, "namespace": asset_dir, + "folder_path": folder_path, "container_name": container_name, "asset_name": asset_name, "loader": str(self.__class__.__name__), - "representation": representation["_id"], - "parent": representation["parent"], - "family": representation["context"]["family"] + "representation": repre_entity["id"], + "parent": repre_entity["versionId"], + "product_type": product_type, + # TODO these shold be probably removed + "asset": folder_path, + "family": product_type, } imprint(f"{asset_dir}/{container_name}", data) @@ -98,19 +107,20 @@ class StaticMeshFBXLoader(plugin.Loader): list(str): list of container content """ # Create directory for asset and Ayon container - asset = context.get('asset').get('name') + folder_path = context["folder"]["path"] + folder_name = context["folder"]["name"] suffix = "_CON" - asset_name = f"{asset}_{name}" if asset else f"{name}" - version = context.get('version') + asset_name = f"{folder_name}_{name}" if folder_name else f"{name}" + version = context["version"]["version"] # Check if version is hero version and use different name - if not version.get("name") and version.get('type') == "hero_version": + if version < 0: name_version = f"{name}_hero" else: - name_version = f"{name}_v{version.get('name'):03d}" + name_version = f"{name}_v{version:03d}" tools = unreal.AssetToolsHelpers().get_asset_tools() asset_dir, container_name = tools.create_unique_asset_name( - f"{self.root}/{asset}/{name_version}", suffix="" + f"{self.root}/{folder_name}/{name_version}", suffix="" ) container_name += suffix @@ -122,8 +132,13 @@ class StaticMeshFBXLoader(plugin.Loader): path, asset_dir, asset_name, container_name) self.imprint( - asset, asset_dir, container_name, asset_name, - context["representation"]) + folder_path, + asset_dir, + container_name, + asset_name, + context["representation"], + context["product"]["productType"] + ) asset_content = unreal.EditorAssetLibrary.list_assets( asset_dir, recursive=True, include_folder=True @@ -135,10 +150,12 @@ class StaticMeshFBXLoader(plugin.Loader): return asset_content def update(self, container, context): + folder_path = context["folder"]["path"] folder_name = context["folder"]["name"] product_name = context["product"]["name"] + product_type = context["product"]["productType"] version = context["version"]["version"] - repre_doc = context["representation"] + repre_entity = context["representation"] # Create directory for asset and Ayon container suffix = "_CON" @@ -157,13 +174,19 @@ class StaticMeshFBXLoader(plugin.Loader): container_name += suffix if not unreal.EditorAssetLibrary.does_directory_exist(asset_dir): - path = get_representation_path(repre_doc) + path = get_representation_path(repre_entity) self.import_and_containerize( path, asset_dir, asset_name, container_name) self.imprint( - folder_name, asset_dir, container_name, asset_name, repre_doc) + folder_path, + asset_dir, + container_name, + asset_name, + repre_entity, + product_type, + ) asset_content = unreal.EditorAssetLibrary.list_assets( asset_dir, recursive=True, include_folder=False diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_uasset.py b/client/ayon_core/hosts/unreal/plugins/load/load_uasset.py index d7a79616b7..e0b1a69aac 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_uasset.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_uasset.py @@ -42,12 +42,13 @@ class UAssetLoader(plugin.Loader): # Create directory for asset and Ayon container root = unreal_pipeline.AYON_ASSET_DIR - asset = context.get('asset').get('name') + folder_path = context["folder"]["path"] + folder_name = context["folder"]["name"] suffix = "_CON" - asset_name = f"{asset}_{name}" if asset else f"{name}" + asset_name = f"{folder_name}_{name}" if folder_name else f"{name}" tools = unreal.AssetToolsHelpers().get_asset_tools() asset_dir, container_name = tools.create_unique_asset_name( - f"{root}/{asset}/{name}", suffix="" + f"{root}/{folder_name}/{name}", suffix="" ) unique_number = 1 @@ -73,17 +74,21 @@ class UAssetLoader(plugin.Loader): unreal_pipeline.create_container( container=container_name, path=asset_dir) + product_type = context["product"]["productType"] data = { "schema": "ayon:container-2.0", "id": AYON_CONTAINER_ID, - "asset": asset, "namespace": asset_dir, + "folder_path": folder_path, "container_name": container_name, "asset_name": asset_name, "loader": str(self.__class__.__name__), - "representation": context["representation"]["_id"], - "parent": context["representation"]["parent"], - "family": context["representation"]["context"]["family"], + "representation": context["representation"]["id"], + "parent": context["representation"]["versionId"], + "product_type": product_type, + # TODO these should be probably removed + "asset": folder_path, + "family": product_type, } unreal_pipeline.imprint(f"{asset_dir}/{container_name}", data) @@ -102,7 +107,7 @@ class UAssetLoader(plugin.Loader): asset_dir = container["namespace"] product_name = context["product"]["name"] - repre_doc = context["representation"] + repre_entity = context["representation"] unique_number = container["container_name"].split("_")[-2] @@ -118,7 +123,7 @@ class UAssetLoader(plugin.Loader): if obj.get_class().get_name() != "AyonAssetContainer": unreal.EditorAssetLibrary.delete_asset(asset) - update_filepath = get_representation_path(repre_doc) + update_filepath = get_representation_path(repre_entity) shutil.copy( update_filepath, @@ -130,8 +135,8 @@ class UAssetLoader(plugin.Loader): unreal_pipeline.imprint( container_path, { - "representation": str(repre_doc["_id"]), - "parent": str(repre_doc["parent"]), + "representation": repre_entity["id"], + "parent": repre_entity["versionId"], } ) diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_yeticache.py b/client/ayon_core/hosts/unreal/plugins/load/load_yeticache.py index c6e275c844..7a1767e52a 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_yeticache.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_yeticache.py @@ -87,13 +87,14 @@ class YetiLoader(plugin.Loader): # Create directory for asset and Ayon container root = unreal_pipeline.AYON_ASSET_DIR - asset = context.get('asset').get('name') + folder_path = context["folder"]["path"] + folder_name = context["folder"]["name"] suffix = "_CON" - asset_name = f"{asset}_{name}" if asset else f"{name}" + asset_name = f"{folder_name}_{name}" if folder_name else f"{name}" tools = unreal.AssetToolsHelpers().get_asset_tools() asset_dir, container_name = tools.create_unique_asset_name( - f"{root}/{asset}/{name}", suffix="") + f"{root}/{folder_name}/{name}", suffix="") unique_number = 1 while unreal.EditorAssetLibrary.does_directory_exist( @@ -116,17 +117,21 @@ class YetiLoader(plugin.Loader): unreal_pipeline.create_container( container=container_name, path=asset_dir) + product_type = context["product"]["productType"] data = { "schema": "ayon:container-2.0", "id": AYON_CONTAINER_ID, - "asset": asset, "namespace": asset_dir, "container_name": container_name, + "folder_path": folder_path, "asset_name": asset_name, "loader": str(self.__class__.__name__), - "representation": context["representation"]["_id"], - "parent": context["representation"]["parent"], - "family": context["representation"]["context"]["family"] + "representation": context["representation"]["id"], + "parent": context["representation"]["versionId"], + "product_type": product_type, + # TODO these shold be probably removed + "asset": folder_path, + "family": product_type, } unreal_pipeline.imprint(f"{asset_dir}/{container_name}", data) @@ -140,9 +145,9 @@ class YetiLoader(plugin.Loader): return asset_content def update(self, container, context): - repre_doc = context["representation"] + repre_entity = context["representation"] name = container["asset_name"] - source_path = get_representation_path(repre_doc) + source_path = get_representation_path(repre_entity) destination_path = container["namespace"] task = self.get_task(source_path, destination_path, name, True) @@ -155,8 +160,8 @@ class YetiLoader(plugin.Loader): unreal_pipeline.imprint( container_path, { - "representation": str(repre_doc["_id"]), - "parent": str(repre_doc["parent"]) + "representation": repre_entity["id"], + "parent": repre_entity["versionId"], }) asset_content = unreal.EditorAssetLibrary.list_assets( diff --git a/client/ayon_core/hosts/unreal/plugins/publish/extract_layout.py b/client/ayon_core/hosts/unreal/plugins/publish/extract_layout.py index de8cf0be2a..5489057021 100644 --- a/client/ayon_core/hosts/unreal/plugins/publish/extract_layout.py +++ b/client/ayon_core/hosts/unreal/plugins/publish/extract_layout.py @@ -6,8 +6,8 @@ import math import unreal from unreal import EditorLevelLibrary as ell from unreal import EditorAssetLibrary as eal +import ayon_api -from ayon_core.client import get_representation_by_name from ayon_core.pipeline import publish @@ -60,10 +60,10 @@ class ExtractLayout(publish.Extractor): family = eal.get_metadata_tag(asset_container, "family") self.log.info("Parent: {}".format(parent_id)) - blend = get_representation_by_name( - project_name, "blend", parent_id, fields=["_id"] + blend = ayon_api.get_representation_by_name( + project_name, "blend", parent_id, fields={"id"} ) - blend_id = blend["_id"] + blend_id = blend["id"] json_element = {} json_element["reference"] = str(blend_id) From f957b9bf41dffe306c5ef4df0a6fdc1904887652 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 8 Mar 2024 22:13:41 +0800 Subject: [PATCH 492/573] allow users to tweak material duplicate options in loader setting --- .../hosts/max/plugins/load/load_max_scene.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py index 39bb3b568d..915abc75a1 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py +++ b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py @@ -1,5 +1,6 @@ import os +from ayon_core.lib import EnumDef from ayon_core.hosts.max.api import lib from ayon_core.hosts.max.api.lib import ( unique_namespace, @@ -25,14 +26,28 @@ class MaxSceneLoader(load.LoaderPlugin): order = -8 icon = "code-fork" color = "green" + mtl_dup_default = "promptMtlDups" + mtl_dup_enum = ["promptMtlDups", "useMergedMtlDups", + "useSceneMtlDups", "renameMtlDups"] - def load(self, context, name=None, namespace=None, data=None): + @classmethod + def get_options(cls, contexts): + return [ + EnumDef("mtldup", + cls.mtl_dup_enum, + default=cls.mtl_dup_default, + label="Material Duplicate Options") + ] + + def load(self, context, name=None, namespace=None, options=None): from pymxs import runtime as rt + mat_dup_options = options.get("mtldup", self.mtl_dup_default) path = self.filepath_from_context(context) path = os.path.normpath(path) # import the max scene by using "merge file" path = path.replace('\\', '/') - rt.MergeMaxFile(path, quiet=True, includeFullGroup=True) + rt.MergeMaxFile(path, rt.Name(mat_dup_options), + quiet=True, includeFullGroup=True) max_objects = rt.getLastMergedNodes() max_object_names = [obj.name for obj in max_objects] # implement the OP/AYON custom attributes before load From 552c9c2147f2d952daaeafbc22b4fae1c577327d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 15:54:45 +0100 Subject: [PATCH 493/573] modified integration plugins to use AYON entity types and functions --- client/ayon_core/plugins/publish/integrate.py | 365 +++++++++++------- .../plugins/publish/integrate_hero_version.py | 293 +++++++------- .../plugins/publish/integrate_thumbnail.py | 13 +- .../publish/integrate_version_attrs.py | 7 +- 4 files changed, 362 insertions(+), 316 deletions(-) diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index b61ac457ad..fd66550141 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -2,27 +2,24 @@ import os import logging import sys import copy -import datetime import clique import six import pyblish.api - -from ayon_core.client.operations import ( - OperationsSession, - new_subset_document, - new_version_doc, - new_representation_doc, - prepare_subset_update_data, - prepare_version_update_data, - prepare_representation_update_data, -) - -from ayon_core.client import ( - get_representations, - get_subset_by_name, +from ayon_api import ( + get_attributes_for_type, + get_product_by_name, get_version_by_name, + get_representations, ) +from ayon_api.operations import ( + OperationsSession, + new_product_entity, + new_version_entity, + new_representation_entity, +) +from ayon_api.utils import create_entity_id + from ayon_core.lib import source_hash from ayon_core.lib.file_transaction import ( FileTransaction, @@ -36,6 +33,36 @@ from ayon_core.pipeline.publish import ( log = logging.getLogger(__name__) +def prepare_changes(old_entity, new_entity): + """Prepare changes for entity update. + + Args: + old_entity: Existing entity. + new_entity: New entity. + + Returns: + dict[str, Any]: Changes that have new entity. + + """ + changes = {} + for key in set(new_entity.keys()): + if key == "attrib": + continue + + if key in new_entity and new_entity[key] != old_entity.get(key): + changes[key] = new_entity[key] + continue + + attrib_changes = {} + if "attrib" in new_entity: + for key, value in new_entity["attrib"].items(): + if value != old_entity["attrib"].get(key): + attrib_changes[key] = value + if attrib_changes: + changes["attrib"] = attrib_changes + return changes + + def get_instance_families(instance): """Get all families of the instance""" # todo: move this to lib? @@ -164,7 +191,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): ] def process(self, instance): - # Instance should be integrated on a farm if instance.data.get("farm"): self.log.debug( @@ -256,23 +282,23 @@ class IntegrateAsset(pyblish.api.InstancePlugin): template_name = self.get_template_name(instance) op_session = OperationsSession() - subset = self.prepare_subset( + product_entity = self.prepare_product( instance, op_session, project_name ) - version = self.prepare_version( - instance, op_session, subset, project_name + version_entity = self.prepare_version( + instance, op_session, product_entity, project_name ) - instance.data["versionEntity"] = version + instance.data["versionEntity"] = version_entity anatomy = instance.context.data["anatomy"] # Get existing representations (if any) existing_repres_by_name = { - repre_doc["name"].lower(): repre_doc - for repre_doc in get_representations( + repre_entity["name"].lower(): repre_entity + for repre_entity in get_representations( project_name, - version_ids=[version["_id"]], - fields=["_id", "name"] + version_ids=[version_entity["id"]], + fields={"id", "name"} ) } @@ -284,7 +310,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): repre, template_name, existing_repres_by_name, - version, + version_entity, instance_stagingdir, instance) @@ -312,7 +338,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): resource_destinations.add(os.path.abspath(dst)) # Bulk write to the database - # We write the subset and version to the database before the File + # We write the product and version to the database before the File # Transaction to reduce the chances of another publish trying to # publish to the same version number since that chance can greatly # increase if the file transaction takes a long time. @@ -320,7 +346,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): self.log.info(( "Product '{}' version {} written to database.." - ).format(subset["name"], version["name"])) + ).format(product_entity["name"], version_entity["version"])) # Process all file transfers of all integrations now self.log.debug("Integrating source files to destination ...") @@ -331,58 +357,46 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "Transferred files: {}".format(file_transactions.transferred)) self.log.debug("Retrieving Representation Site Sync information ...") - # Get the accessible sites for Site Sync - addons_manager = instance.context.data["ayonAddonsManager"] - sync_server_addon = addons_manager.get("sync_server") - if sync_server_addon is None: - sites = [{ - "name": "studio", - "created_dt": datetime.datetime.now() - }] - else: - sites = sync_server_addon.compute_resource_sync_sites( - project_name=instance.data["projectEntity"]["name"] - ) - self.log.debug("Sync Server Sites: {}".format(sites)) - # Compute the resource file infos once (files belonging to the # version instance instead of an individual representation) so # we can re-use those file infos per representation - resource_file_infos = self.get_files_info(resource_destinations, - sites=sites, - anatomy=anatomy) + resource_file_infos = self.get_files_info( + resource_destinations, anatomy + ) # Finalize the representations now the published files are integrated # Get 'files' info for representations and its attached resources new_repre_names_low = set() for prepared in prepared_representations: - repre_doc = prepared["representation"] - repre_update_data = prepared["repre_doc_update_data"] + repre_entity = prepared["representation"] + repre_update_data = prepared["repre_update_data"] transfers = prepared["transfers"] destinations = [dst for src, dst in transfers] - repre_doc["files"] = self.get_files_info( - destinations, sites=sites, anatomy=anatomy + repre_files = self.get_files_info( + destinations, anatomy ) - # Add the version resource file infos to each representation - repre_doc["files"] += resource_file_infos + repre_files += resource_file_infos + repre_entity["files"] = repre_files # Set up representation for writing to the database. Since # we *might* be overwriting an existing entry if the version # already existed we'll use ReplaceOnce with `upsert=True` if repre_update_data is None: op_session.create_entity( - project_name, repre_doc["type"], repre_doc + project_name, "representation", repre_entity ) else: + # Add files to update data + repre_update_data["files"] = repre_files op_session.update_entity( project_name, - repre_doc["type"], - repre_doc["_id"], + "representation", + repre_entity["id"], repre_update_data ) - new_repre_names_low.add(repre_doc["name"].lower()) + new_repre_names_low.add(repre_entity["name"].lower()) # Delete any existing representations that didn't get any new data # if the instance is not set to append mode @@ -392,7 +406,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # We add the exact representation name because `name` is # lowercase for name matching only and not in the database op_session.delete_entity( - project_name, "representation", existing_repres["_id"] + project_name, "representation", existing_repres["id"] ) self.log.debug("{}".format(op_session.to_data())) @@ -401,7 +415,8 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # Backwards compatibility used in hero integration. # todo: can we avoid the need to store this? instance.data["published_representations"] = { - p["representation"]["_id"]: p for p in prepared_representations + p["representation"]["id"]: p + for p in prepared_representations } self.log.info( @@ -412,108 +427,131 @@ class IntegrateAsset(pyblish.api.InstancePlugin): ) ) - def prepare_subset(self, instance, op_session, project_name): + def prepare_product(self, instance, op_session, project_name): folder_entity = instance.data["folderEntity"] product_name = instance.data["productName"] product_type = instance.data["productType"] self.log.debug("Product: {}".format(product_name)) - # Get existing subset if it exists - existing_subset_doc = get_subset_by_name( + # Get existing product if it exists + existing_product_entity = get_product_by_name( project_name, product_name, folder_entity["id"] ) - # Define subset data + # Define product data data = { "families": get_instance_families(instance) } + attribibutes = {} - subset_group = instance.data.get("productGroup") - if subset_group: - data["productGroup"] = subset_group - elif existing_subset_doc: - # Preserve previous subset group if new version does not set it - if "productGroup" in existing_subset_doc.get("data", {}): - subset_group = existing_subset_doc["data"]["productGroup"] - data["productGroup"] = subset_group + product_group = instance.data.get("productGroup") + if product_group: + attribibutes["productGroup"] = product_group + elif existing_product_entity: + # Preserve previous product group if new version does not set it + product_group = existing_product_entity.get("attrib", {}).get( + "productGroup" + ) + if product_group is not None: + attribibutes["productGroup"] = product_group - subset_id = None - if existing_subset_doc: - subset_id = existing_subset_doc["_id"] - subset_doc = new_subset_document( - product_name, product_type, folder_entity["id"], data, subset_id + product_id = None + if existing_product_entity: + product_id = existing_product_entity["id"] + + product_entity = new_product_entity( + product_name, + product_type, + folder_entity["id"], + data=data, + attribs=attribibutes, + entity_id=product_id ) - if existing_subset_doc is None: - # Create a new subset + if existing_product_entity is None: + # Create a new product self.log.info( "Product '%s' not found, creating ..." % product_name ) op_session.create_entity( - project_name, subset_doc["type"], subset_doc + project_name, "product", product_entity ) else: - # Update existing subset data with new data and set in database. - # We also change the found subset in-place so we don't need to - # re-query the subset afterwards - subset_doc["data"].update(data) - update_data = prepare_subset_update_data( - existing_subset_doc, subset_doc + # Update existing product data with new data and set in database. + # We also change the found product in-place so we don't need to + # re-query the product afterwards + update_data = prepare_changes( + existing_product_entity, product_entity ) op_session.update_entity( project_name, - subset_doc["type"], - subset_doc["_id"], + "product", + product_entity["id"], update_data ) self.log.debug("Prepared product: {}".format(product_name)) - return subset_doc + return product_entity - def prepare_version(self, instance, op_session, subset_doc, project_name): + def prepare_version( + self, instance, op_session, product_entity, project_name + ): version_number = instance.data["version"] + task_id = None + task_entity = instance.data.get("taskEntity") + if task_entity: + task_id = task_entity["id"] existing_version = get_version_by_name( project_name, version_number, - subset_doc["_id"], - fields=["_id"] + product_entity["id"], + fields={"id"} ) version_id = None if existing_version: - version_id = existing_version["_id"] + version_id = existing_version["id"] - version_data = self.create_version_data(instance) - version_doc = new_version_doc( + all_version_data = self.create_version_data(instance) + version_data = {} + version_attributes = {} + attr_defs = self._get_attributes_for_type(instance.context, "version") + for key, value in all_version_data.items(): + if key in attr_defs: + version_attributes[key] = value + else: + version_data[key] = value + + version_entity = new_version_entity( version_number, - subset_doc["_id"], - version_data, - version_id + product_entity["id"], + task_id=task_id, + data=version_data, + attribs=version_attributes, + entity_id=version_id, ) if existing_version: self.log.debug("Updating existing version ...") - update_data = prepare_version_update_data( - existing_version, version_doc - ) + update_data = prepare_changes(existing_version, version_entity) op_session.update_entity( project_name, - version_doc["type"], - version_doc["_id"], + "version", + version_entity["id"], update_data ) else: self.log.debug("Creating new version ...") op_session.create_entity( - project_name, version_doc["type"], version_doc + project_name, "version", version_entity ) self.log.debug( - "Prepared version: v{0:03d}".format(version_doc["name"]) + "Prepared version: v{0:03d}".format(version_entity["version"]) ) - return version_doc + return version_entity def _validate_repre_files(self, files, is_sequence_representation): """Validate representation files before transfer preparation. @@ -552,13 +590,15 @@ class IntegrateAsset(pyblish.api.InstancePlugin): ", ".join([str(rem) for rem in remainders]) )) - def prepare_representation(self, repre, - template_name, - existing_repres_by_name, - version, - instance_stagingdir, - instance): - + def prepare_representation( + self, + repre, + template_name, + existing_repres_by_name, + version_entity, + instance_stagingdir, + instance + ): # pre-flight validations if repre["ext"].startswith("."): raise KnownPublishError(( @@ -581,7 +621,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): template_data["ext"] = repre["ext"] # allow overwriting existing version - template_data["version"] = version["name"] + template_data["version"] = version_entity["version"] # add template data for colorspaceData if repre.get("colorspaceData"): @@ -823,7 +863,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): existing = existing_repres_by_name.get(repre["name"].lower()) repre_id = None if existing: - repre_id = existing["_id"] + repre_id = existing["id"] # Store first transferred destination as published path data # - used primarily for reviews that are integrated to custom modules @@ -835,25 +875,37 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # todo: `repre` is not the actual `representation` entity # we should simplify/clarify difference between data above # and the actual representation entity for the database - data = repre.get("data", {}) - data.update({"path": published_path, "template": template}) + attr_defs = self._get_attributes_for_type( + instance.context, "representation" + ) + attributes = {"path": published_path, "template": template} + data = {"context": repre_context} + for key, value in repre.get("data", {}).items(): + if key in attr_defs: + attributes[key] = value + else: + data[key] = value # add colorspace data if any exists on representation if repre.get("colorspaceData"): data["colorspaceData"] = repre["colorspaceData"] - repre_doc = new_representation_doc( - repre["name"], version["_id"], repre_context, data, repre_id + repre_doc = new_representation_entity( + repre["name"], + version_entity["id"], + # files are filled afterwards + [], + data=data, + attribs=attributes, + entity_id=repre_id ) update_data = None if repre_id is not None: - update_data = prepare_representation_update_data( - existing, repre_doc - ) + update_data = prepare_changes(existing, repre_doc) return { "representation": repre_doc, - "repre_doc_update_data": update_data, + "repre_update_data": update_data, "anatomy_data": template_data, "transfers": transfers, # todo: avoid the need for 'published_files' used by Integrate Hero @@ -950,13 +1002,13 @@ class IntegrateAsset(pyblish.api.InstancePlugin): '{root}/MyProject1/Assets...' Args: - anatomy: anatomy part from instance - path: path (absolute) - Returns: - path: modified path if possible, or unmodified path - + warning logged - """ + anatomy (Anatomy): Project anatomy. + path (str): Absolute path. + Returns: + str: Path where root path is replaced by formatting string. + + """ success, rootless_path = anatomy.find_root_template_from_path(path) if success: path = rootless_path @@ -967,43 +1019,41 @@ class IntegrateAsset(pyblish.api.InstancePlugin): ).format(path)) return path - def get_files_info(self, destinations, sites, anatomy): + def get_files_info(self, filepaths, anatomy): """Prepare 'files' info portion for representations. Arguments: - destinations (list): List of transferred file destinations - sites (list): array of published locations - anatomy: anatomy part from instance - Returns: - output_resources: array of dictionaries to be added to 'files' key - in representation - """ + filepaths (Iterable[str]): List of transferred file paths. + anatomy (Anatomy): Project anatomy. + Returns: + list[dict[str, Any]]: Representation 'files' information. + + """ file_infos = [] - for file_path in destinations: - file_info = self.prepare_file_info(file_path, anatomy, sites=sites) + for filepath in filepaths: + file_info = self.prepare_file_info(filepath, anatomy) file_infos.append(file_info) return file_infos - def prepare_file_info(self, path, anatomy, sites): + def prepare_file_info(self, path, anatomy): """ Prepare information for one file (asset or resource) Arguments: - path: destination url of published file - anatomy: anatomy part from instance - sites: array of published locations, - [ {'name':'studio', 'created_dt':date} by default - keys expected ['studio', 'site1', 'gdrive1'] + path (str): Destination url of published file. + anatomy (Anatomy): Project anatomy part from instance. Returns: - dict: file info dictionary - """ + dict[str, Any]: Representation file info dictionary. + """ return { + "id": create_entity_id(), + "name": os.path.basename(path), "path": self.get_rootless_path(anatomy, path), "size": os.path.getsize(path), "hash": source_hash(path), - "sites": sites + "hash_type": "op3", } def _validate_path_in_project_roots(self, anatomy, file_path): @@ -1012,10 +1062,11 @@ class IntegrateAsset(pyblish.api.InstancePlugin): Used to check that published path belongs to project, eg. we are not trying to publish to local only folder. Args: - anatomy (Anatomy) - file_path (str) - Raises - (KnownPublishError) + anatomy (Anatomy): Project anatomy. + file_path (str): Filepath. + + Raises: + KnownPublishError: When failed to find root for the path. """ path = self.get_rootless_path(anatomy, file_path) if not path: @@ -1023,3 +1074,21 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "Destination path '{}' ".format(file_path) + "must be in project dir" )) + + def _get_attributes_for_type(self, context, entity_type): + return self._get_attributes_by_type(context)[entity_type] + + def _get_attributes_by_type(self, context): + attributes = context.data.get("ayonAttributes") + if attributes is None: + attributes = {} + for key in ( + "project", + "folder", + "product", + "version", + "representation", + ): + attributes[key] = get_attributes_for_type(key) + context.data["ayonAttributes"] = attributes + return attributes diff --git a/client/ayon_core/plugins/publish/integrate_hero_version.py b/client/ayon_core/plugins/publish/integrate_hero_version.py index c275f75118..9e4265f853 100644 --- a/client/ayon_core/plugins/publish/integrate_hero_version.py +++ b/client/ayon_core/plugins/publish/integrate_hero_version.py @@ -1,30 +1,53 @@ import os import copy -import clique import errno import shutil +import clique import pyblish.api - -from ayon_core.client import ( - get_version_by_id, - get_hero_version_by_subset_id, - get_archived_representations, - get_representations, -) -from ayon_core.client.operations import ( +import ayon_api +from ayon_api.operations import ( OperationsSession, + new_version_entity, +) + +from ayon_core.client.operations import ( new_hero_version_doc, - prepare_hero_version_update_data, - prepare_representation_update_data, ) from ayon_core.lib import create_hard_link -from ayon_core.pipeline import ( - schema -) from ayon_core.pipeline.publish import get_publish_template_name +def prepare_changes(old_entity, new_entity): + """Prepare changes for entity update. + + Args: + old_entity: Existing entity. + new_entity: New entity. + + Returns: + dict[str, Any]: Changes that have new entity. + + """ + changes = {} + for key in set(new_entity.keys()): + if key == "attrib": + continue + + if key in new_entity and new_entity[key] != old_entity.get(key): + changes[key] = new_entity[key] + continue + + attrib_changes = {} + if "attrib" in new_entity: + for key, value in new_entity["attrib"].items(): + if value != old_entity["attrib"].get(key): + attrib_changes[key] = value + if attrib_changes: + changes["attrib"] = attrib_changes + return changes + + class IntegrateHeroVersion(pyblish.api.InstancePlugin): label = "Integrate Hero Version" # Must happen after IntegrateNew @@ -150,7 +173,7 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): )) return - if src_version_entity["name"] == 0: + if src_version_entity["version"] == 0: self.log.debug( "Version 0 cannot have hero version. Skipping." ) @@ -200,39 +223,45 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): old_version, old_repres = self.current_hero_ents( project_name, src_version_entity ) - - old_repres_by_name = { - repre["name"].lower(): repre for repre in old_repres - } + inactive_old_repres_by_name = {} + old_repres_by_name = {} + for repre in old_repres: + low_name = repre["name"].lower() + if repre["active"]: + old_repres_by_name[low_name] = repre + else: + inactive_old_repres_by_name[low_name] = repre op_session = OperationsSession() entity_id = None if old_version: - entity_id = old_version["_id"] + entity_id = old_version["id"] - new_hero_version = new_hero_version_doc( - src_version_entity["parent"], - copy.deepcopy(src_version_entity["data"]), - src_version_entity["name"], - entity_id=entity_id + new_hero_version = new_version_entity( + src_version_entity["version"], + src_version_entity["productId"], + task_id=src_version_entity["taskId"], + data=copy.deepcopy(src_version_entity["data"]), + attribs=copy.deepcopy(src_version_entity["attrib"]), + entity_id=entity_id, ) if old_version: self.log.debug("Replacing old hero version.") - update_data = prepare_hero_version_update_data( + update_data = prepare_changes( old_version, new_hero_version ) op_session.update_entity( project_name, - new_hero_version["type"], - old_version["_id"], + "version", + old_version["id"], update_data ) else: self.log.debug("Creating first hero version.") op_session.create_entity( - project_name, new_hero_version["type"], new_hero_version + project_name, "version", new_hero_version ) # Separate old representations into `to replace` and `to delete` @@ -249,16 +278,6 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): if old_repres_by_name: old_repres_to_delete = old_repres_by_name - archived_repres = list(get_archived_representations( - project_name, - # Check what is type of archived representation - version_ids=[new_hero_version["_id"]] - )) - archived_repres_by_name = {} - for repre in archived_repres: - repre_name_low = repre["name"].lower() - archived_repres_by_name[repre_name_low] = repre - backup_hero_publish_dir = None if os.path.exists(hero_publish_dir): backup_hero_publish_dir = hero_publish_dir + ".BACKUP" @@ -322,7 +341,7 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): # Get filled path to repre context template_filled = path_template_obj.format_strict(anatomy_data) - repre_data = { + repre_attributes = { "path": str(template_filled), "template": hero_template } @@ -333,11 +352,11 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): repre_context[key] = value # Prepare new repre - repre = copy.deepcopy(repre_info["representation"]) - repre["parent"] = new_hero_version["_id"] - repre["context"] = repre_context - repre["data"] = repre_data - repre.pop("_id", None) + repre_entity = copy.deepcopy(repre_info["representation"]) + repre_entity.pop("id", None) + repre_entity["versionId"] = new_hero_version["id"] + repre_entity["context"] = repre_context + repre_entity["attrib"] = repre_attributes # Prepare paths of source and destination files if len(published_files) == 1: @@ -378,82 +397,48 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): ) # replace original file name with hero name in repre doc - for index in range(len(repre.get("files"))): - file = repre.get("files")[index] - file_name = os.path.basename(file.get('path')) - for src_file, dst_file in src_to_dst_file_paths: - src_file_name = os.path.basename(src_file) - if src_file_name == file_name: - repre["files"][index]["path"] = self._update_path( - anatomy, repre["files"][index]["path"], - src_file, dst_file) + dst_paths = [] + for _, dst_path in src_to_dst_file_paths: + dst_paths.append(dst_path) + repre_files = self.get_files_info(dst_paths, anatomy) + repre_entity["files"] = repre_files - repre["files"][index]["hash"] = self._update_hash( - repre["files"][index]["hash"], - src_file_name, dst_file - ) - - schema.validate(repre) - - repre_name_low = repre["name"].lower() + repre_name_low = repre_entity["name"].lower() # Replace current representation if repre_name_low in old_repres_to_replace: old_repre = old_repres_to_replace.pop(repre_name_low) - repre["_id"] = old_repre["_id"] - update_data = prepare_representation_update_data( - old_repre, repre) - - # Keep previously synchronized sites up-to-date - # by comparing old and new sites and adding old sites - # if missing in new ones - # Prepare all sites from all files in old representation - old_site_names = set() - for file_info in old_repre.get("files", []): - old_site_names |= { - site["name"] - for site in file_info["sites"] - } - - for file_info in update_data.get("files", []): - file_info.setdefault("sites", []) - file_info_site_names = { - site["name"] - for site in file_info["sites"] - } - for site_name in old_site_names: - if site_name not in file_info_site_names: - file_info["sites"].append({ - "name": site_name - }) + repre_entity["id"] = old_repre["id"] + update_data = prepare_changes(old_repre, repre_entity) op_session.update_entity( project_name, - old_repre["type"], - old_repre["_id"], + "representation", + old_repre["id"], update_data ) # Unarchive representation - elif repre_name_low in archived_repres_by_name: - archived_repre = archived_repres_by_name.pop( + elif repre_name_low in inactive_old_repres_by_name: + inactive_repre = inactive_old_repres_by_name.pop( repre_name_low ) - repre["_id"] = archived_repre["old_id"] - update_data = prepare_representation_update_data( - archived_repre, repre) + repre_entity["id"] = inactive_repre["id"] + update_data = prepare_changes(inactive_repre, repre_entity) op_session.update_entity( project_name, - old_repre["type"], - archived_repre["_id"], + "representation", + inactive_repre["id"], update_data ) # Create representation else: - repre.pop("_id", None) - op_session.create_entity(project_name, "representation", - repre) + op_session.create_entity( + project_name, + "representation", + repre_entity + ) self.path_checks = [] @@ -467,27 +452,13 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): self.copy_file(src_path, dst_path) # Archive not replaced old representations - for repre_name_low, repre in old_repres_to_delete.items(): - # Replace archived representation (This is backup) - # - should not happen to have both repre and archived repre - if repre_name_low in archived_repres_by_name: - archived_repre = archived_repres_by_name.pop( - repre_name_low - ) - - changes = {"old_id": repre["_id"], - "_id": archived_repre["_id"], - "type": archived_repre["type"]} - op_session.update_entity(project_name, - archived_repre["type"], - archived_repre["_id"], - changes) - else: - repre["old_id"] = repre.pop("_id") - repre["type"] = "archived_representation" - op_session.create_entity(project_name, - "archived_representation", - repre) + for repre in old_repres_to_delete.values(): + op_session.update_entity( + project_name, + "representation", + repre["id"], + {"active": False} + ) op_session.commit() @@ -519,13 +490,42 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): instance.data["productName"] )) - def get_all_files_from_path(self, path): - files = [] - for (dir_path, dir_names, file_names) in os.walk(path): - for file_name in file_names: - _path = os.path.join(dir_path, file_name) - files.append(_path) - return files + def get_files_info(self, filepaths, anatomy): + """Prepare 'files' info portion for representations. + + Arguments: + filepaths (Iterable[str]): List of transferred file paths. + anatomy (Anatomy): Project anatomy. + + Returns: + list[dict[str, Any]]: Representation 'files' information. + + """ + file_infos = [] + for filepath in filepaths: + file_info = self.prepare_file_info(filepath, anatomy) + file_infos.append(file_info) + return file_infos + + def prepare_file_info(self, path, anatomy): + """ Prepare information for one file (asset or resource) + + Arguments: + path (str): Destination url of published file. + anatomy (Anatomy): Project anatomy part from instance. + + Returns: + dict[str, Any]: Representation file info dictionary. + + """ + return { + "id": create_entity_id(), + "name": os.path.basename(path), + "path": self.get_rootless_path(anatomy, path), + "size": os.path.getsize(path), + "hash": source_hash(path), + "hash_type": "op3", + } def get_publish_dir(self, instance, template_key): anatomy = instance.context.data["anatomy"] @@ -617,48 +617,25 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): def version_from_representations(self, project_name, repres): for repre in repres: - version = get_version_by_id(project_name, repre["parent"]) + version = ayon_api.get_version_by_id( + project_name, repre["versionId"] + ) if version: return version def current_hero_ents(self, project_name, version): - hero_version = get_hero_version_by_subset_id( - project_name, version["parent"] + hero_version = ayon_api.get_hero_version_by_product_id( + project_name, version["productId"] ) if not hero_version: return (None, []) - hero_repres = list(get_representations( - project_name, version_ids=[hero_version["_id"]] + hero_repres = list(ayon_api.get_representations( + project_name, version_ids={hero_version["id"]} )) return (hero_version, hero_repres) - def _update_path(self, anatomy, path, src_file, dst_file): - """ - Replaces source path with new hero path - - 'path' contains original path with version, must be replaced with - 'hero' path (with 'hero' label and without version) - - Args: - anatomy (Anatomy) - to get rootless style of path - path (string) - path from DB - src_file (string) - original file path - dst_file (string) - hero file path - """ - _, rootless = anatomy.find_root_template_from_path(dst_file) - _, rtls_src = anatomy.find_root_template_from_path(src_file) - return path.replace(rtls_src, rootless) - - def _update_hash(self, hash, src_file_name, dst_file): - """ - Updates hash value with proper hero name - """ - src_file_name = self._get_name_without_ext(src_file_name) - hero_file_name = self._get_name_without_ext(dst_file) - return hash.replace(src_file_name, hero_file_name) - def _get_name_without_ext(self, value): file_name = os.path.basename(value) file_name, _ = os.path.splitext(file_name) diff --git a/client/ayon_core/plugins/publish/integrate_thumbnail.py b/client/ayon_core/plugins/publish/integrate_thumbnail.py index 2770dd04cf..31a7f801fa 100644 --- a/client/ayon_core/plugins/publish/integrate_thumbnail.py +++ b/client/ayon_core/plugins/publish/integrate_thumbnail.py @@ -27,8 +27,7 @@ import collections import pyblish.api import ayon_api - -from ayon_core.client.operations import OperationsSession +from ayon_api.operations import OperationsSession InstanceFilterResult = collections.namedtuple( "InstanceFilterResult", @@ -162,8 +161,6 @@ class IntegrateThumbnailsAYON(pyblish.api.ContextPlugin): version_entities_by_id, project_name ): - from ayon_core.client.operations import create_thumbnail - # Make sure each entity id has defined only one thumbnail id thumbnail_info_by_entity_id = {} for instance_item in filtered_instance_items: @@ -176,7 +173,9 @@ class IntegrateThumbnailsAYON(pyblish.api.ContextPlugin): ).format(instance_label)) continue - thumbnail_id = create_thumbnail(project_name, thumbnail_path) + thumbnail_id = ayon_api.create_thumbnail( + project_name, thumbnail_path + ) # Set thumbnail id for version thumbnail_info_by_entity_id[version_id] = { @@ -194,7 +193,7 @@ class IntegrateThumbnailsAYON(pyblish.api.ContextPlugin): folder_path = instance.data["folderPath"] thumbnail_info_by_entity_id[folder_id] = { "thumbnail_id": thumbnail_id, - "entity_type": "asset", + "entity_type": "folder", } self.log.debug("Setting thumbnail for folder \"{}\" <{}>".format( folder_path, version_id @@ -207,7 +206,7 @@ class IntegrateThumbnailsAYON(pyblish.api.ContextPlugin): project_name, thumbnail_info["entity_type"], entity_id, - {"data.thumbnail_id": thumbnail_id} + {"thumbnailId": thumbnail_id} ) op_session.commit() diff --git a/client/ayon_core/plugins/publish/integrate_version_attrs.py b/client/ayon_core/plugins/publish/integrate_version_attrs.py index 0380d33137..eadf4a194c 100644 --- a/client/ayon_core/plugins/publish/integrate_version_attrs.py +++ b/client/ayon_core/plugins/publish/integrate_version_attrs.py @@ -1,7 +1,6 @@ import pyblish.api import ayon_api - -from ayon_core.client.operations import OperationsSession +from ayon_api.operations import OperationsSession class IntegrateVersionAttributes(pyblish.api.ContextPlugin): @@ -33,6 +32,8 @@ class IntegrateVersionAttributes(pyblish.api.ContextPlugin): version_entity = instance.data.get("versionEntity") if not version_entity: continue + + current_attributes = version_entity["attrib"] attributes = instance.data.get("versionAttributes") if not attributes: self.log.debug(( @@ -45,7 +46,7 @@ class IntegrateVersionAttributes(pyblish.api.ContextPlugin): for attr, value in attributes.items(): if attr not in available_attributes: skipped_attributes.add(attr) - else: + elif current_attributes.get(attr) != value: filtered_attributes[attr] = value if not filtered_attributes: From f38e15dad209ee35d4f711f7e4ca99aa2a97e853 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 15:55:08 +0100 Subject: [PATCH 494/573] moved thumbnails cache to thumbnails model --- client/ayon_core/client/thumbnails.py | 229 ------------------ .../tools/ayon_utils/models/thumbnails.py | 223 ++++++++++++++++- 2 files changed, 220 insertions(+), 232 deletions(-) delete mode 100644 client/ayon_core/client/thumbnails.py diff --git a/client/ayon_core/client/thumbnails.py b/client/ayon_core/client/thumbnails.py deleted file mode 100644 index dc649b9651..0000000000 --- a/client/ayon_core/client/thumbnails.py +++ /dev/null @@ -1,229 +0,0 @@ -"""Cache of thumbnails downloaded from AYON server. - -Thumbnails are cached to appdirs to predefined directory. - -This should be moved to thumbnails logic in pipeline but because it would -overflow OpenPype logic it's here for now. -""" - -import os -import time -import collections - -import appdirs - -FileInfo = collections.namedtuple( - "FileInfo", - ("path", "size", "modification_time") -) - - -class AYONThumbnailCache: - """Cache of thumbnails on local storage. - - Thumbnails are cached to appdirs to predefined directory. Each project has - own subfolder with thumbnails -> that's because each project has own - thumbnail id validation and file names are thumbnail ids with matching - extension. Extensions are predefined (.png and .jpeg). - - Cache has cleanup mechanism which is triggered on initialized by default. - - The cleanup has 2 levels: - 1. soft cleanup which remove all files that are older then 'days_alive' - 2. max size cleanup which remove all files until the thumbnails folder - contains less then 'max_filesize' - - this is time consuming so it's not triggered automatically - - Args: - cleanup (bool): Trigger soft cleanup (Cleanup expired thumbnails). - """ - - # Lifetime of thumbnails (in seconds) - # - default 3 days - days_alive = 3 - # Max size of thumbnail directory (in bytes) - # - default 2 Gb - max_filesize = 2 * 1024 * 1024 * 1024 - - def __init__(self, cleanup=True): - self._thumbnails_dir = None - self._days_alive_secs = self.days_alive * 24 * 60 * 60 - if cleanup: - self.cleanup() - - def get_thumbnails_dir(self): - """Root directory where thumbnails are stored. - - Returns: - str: Path to thumbnails root. - """ - - if self._thumbnails_dir is None: - # TODO use generic function - directory = appdirs.user_data_dir("AYON", "Ynput") - self._thumbnails_dir = os.path.join(directory, "thumbnails") - return self._thumbnails_dir - - thumbnails_dir = property(get_thumbnails_dir) - - def get_thumbnails_dir_file_info(self): - """Get information about all files in thumbnails directory. - - Returns: - List[FileInfo]: List of file information about all files. - """ - - thumbnails_dir = self.thumbnails_dir - files_info = [] - if not os.path.exists(thumbnails_dir): - return files_info - - for root, _, filenames in os.walk(thumbnails_dir): - for filename in filenames: - path = os.path.join(root, filename) - files_info.append(FileInfo( - path, os.path.getsize(path), os.path.getmtime(path) - )) - return files_info - - def get_thumbnails_dir_size(self, files_info=None): - """Got full size of thumbnail directory. - - Args: - files_info (List[FileInfo]): Prepared file information about - files in thumbnail directory. - - Returns: - int: File size of all files in thumbnail directory. - """ - - if files_info is None: - files_info = self.get_thumbnails_dir_file_info() - - if not files_info: - return 0 - - return sum( - file_info.size - for file_info in files_info - ) - - def cleanup(self, check_max_size=False): - """Cleanup thumbnails directory. - - Args: - check_max_size (bool): Also cleanup files to match max size of - thumbnails directory. - """ - - thumbnails_dir = self.get_thumbnails_dir() - # Skip if thumbnails dir does not exists yet - if not os.path.exists(thumbnails_dir): - return - - self._soft_cleanup(thumbnails_dir) - if check_max_size: - self._max_size_cleanup(thumbnails_dir) - - def _soft_cleanup(self, thumbnails_dir): - current_time = time.time() - for root, _, filenames in os.walk(thumbnails_dir): - for filename in filenames: - path = os.path.join(root, filename) - modification_time = os.path.getmtime(path) - if current_time - modification_time > self._days_alive_secs: - os.remove(path) - - def _max_size_cleanup(self, thumbnails_dir): - files_info = self.get_thumbnails_dir_file_info() - size = self.get_thumbnails_dir_size(files_info) - if size < self.max_filesize: - return - - sorted_file_info = collections.deque( - sorted(files_info, key=lambda item: item.modification_time) - ) - diff = size - self.max_filesize - while diff > 0: - if not sorted_file_info: - break - - file_info = sorted_file_info.popleft() - diff -= file_info.size - os.remove(file_info.path) - - def get_thumbnail_filepath(self, project_name, thumbnail_id): - """Get thumbnail by thumbnail id. - - Args: - project_name (str): Name of project. - thumbnail_id (str): Thumbnail id. - - Returns: - Union[str, None]: Path to thumbnail image or None if thumbnail - is not cached yet. - """ - - if not thumbnail_id: - return None - - for ext in ( - ".png", - ".jpeg", - ): - filepath = os.path.join( - self.thumbnails_dir, project_name, thumbnail_id + ext - ) - if os.path.exists(filepath): - return filepath - return None - - def get_project_dir(self, project_name): - """Path to root directory for specific project. - - Args: - project_name (str): Name of project for which root directory path - should be returned. - - Returns: - str: Path to root of project's thumbnails. - """ - - return os.path.join(self.thumbnails_dir, project_name) - - def make_sure_project_dir_exists(self, project_name): - project_dir = self.get_project_dir(project_name) - if not os.path.exists(project_dir): - os.makedirs(project_dir) - return project_dir - - def store_thumbnail(self, project_name, thumbnail_id, content, mime_type): - """Store thumbnail to cache folder. - - Args: - project_name (str): Project where the thumbnail belong to. - thumbnail_id (str): Id of thumbnail. - content (bytes): Byte content of thumbnail file. - mime_data (str): Type of content. - - Returns: - str: Path to cached thumbnail image file. - """ - - if mime_type == "image/png": - ext = ".png" - elif mime_type == "image/jpeg": - ext = ".jpeg" - else: - raise ValueError( - "Unknown mime type for thumbnail \"{}\"".format(mime_type)) - - project_dir = self.make_sure_project_dir_exists(project_name) - thumbnail_path = os.path.join(project_dir, thumbnail_id + ext) - with open(thumbnail_path, "wb") as stream: - stream.write(content) - - current_time = time.time() - os.utime(thumbnail_path, (current_time, current_time)) - - return thumbnail_path diff --git a/client/ayon_core/tools/ayon_utils/models/thumbnails.py b/client/ayon_core/tools/ayon_utils/models/thumbnails.py index 86d6f3cba3..138cee4ea2 100644 --- a/client/ayon_core/tools/ayon_utils/models/thumbnails.py +++ b/client/ayon_core/tools/ayon_utils/models/thumbnails.py @@ -1,17 +1,234 @@ +import os +import time import collections import ayon_api - -from ayon_core.client.thumbnails import AYONThumbnailCache +import appdirs from .cache import NestedCacheItem +FileInfo = collections.namedtuple( + "FileInfo", + ("path", "size", "modification_time") +) + + +class ThumbnailsCache: + """Cache of thumbnails on local storage. + + Thumbnails are cached to appdirs to predefined directory. Each project has + own subfolder with thumbnails -> that's because each project has own + thumbnail id validation and file names are thumbnail ids with matching + extension. Extensions are predefined (.png and .jpeg). + + Cache has cleanup mechanism which is triggered on initialized by default. + + The cleanup has 2 levels: + 1. soft cleanup which remove all files that are older then 'days_alive' + 2. max size cleanup which remove all files until the thumbnails folder + contains less then 'max_filesize' + - this is time consuming so it's not triggered automatically + + Args: + cleanup (bool): Trigger soft cleanup (Cleanup expired thumbnails). + """ + + # Lifetime of thumbnails (in seconds) + # - default 3 days + days_alive = 3 + # Max size of thumbnail directory (in bytes) + # - default 2 Gb + max_filesize = 2 * 1024 * 1024 * 1024 + + def __init__(self, cleanup=True): + self._thumbnails_dir = None + self._days_alive_secs = self.days_alive * 24 * 60 * 60 + if cleanup: + self.cleanup() + + def get_thumbnails_dir(self): + """Root directory where thumbnails are stored. + + Returns: + str: Path to thumbnails root. + """ + + if self._thumbnails_dir is None: + # TODO use generic function + directory = appdirs.user_data_dir("AYON", "Ynput") + self._thumbnails_dir = os.path.join(directory, "thumbnails") + return self._thumbnails_dir + + thumbnails_dir = property(get_thumbnails_dir) + + def get_thumbnails_dir_file_info(self): + """Get information about all files in thumbnails directory. + + Returns: + List[FileInfo]: List of file information about all files. + """ + + thumbnails_dir = self.thumbnails_dir + files_info = [] + if not os.path.exists(thumbnails_dir): + return files_info + + for root, _, filenames in os.walk(thumbnails_dir): + for filename in filenames: + path = os.path.join(root, filename) + files_info.append(FileInfo( + path, os.path.getsize(path), os.path.getmtime(path) + )) + return files_info + + def get_thumbnails_dir_size(self, files_info=None): + """Got full size of thumbnail directory. + + Args: + files_info (List[FileInfo]): Prepared file information about + files in thumbnail directory. + + Returns: + int: File size of all files in thumbnail directory. + """ + + if files_info is None: + files_info = self.get_thumbnails_dir_file_info() + + if not files_info: + return 0 + + return sum( + file_info.size + for file_info in files_info + ) + + def cleanup(self, check_max_size=False): + """Cleanup thumbnails directory. + + Args: + check_max_size (bool): Also cleanup files to match max size of + thumbnails directory. + """ + + thumbnails_dir = self.get_thumbnails_dir() + # Skip if thumbnails dir does not exists yet + if not os.path.exists(thumbnails_dir): + return + + self._soft_cleanup(thumbnails_dir) + if check_max_size: + self._max_size_cleanup(thumbnails_dir) + + def _soft_cleanup(self, thumbnails_dir): + current_time = time.time() + for root, _, filenames in os.walk(thumbnails_dir): + for filename in filenames: + path = os.path.join(root, filename) + modification_time = os.path.getmtime(path) + if current_time - modification_time > self._days_alive_secs: + os.remove(path) + + def _max_size_cleanup(self, thumbnails_dir): + files_info = self.get_thumbnails_dir_file_info() + size = self.get_thumbnails_dir_size(files_info) + if size < self.max_filesize: + return + + sorted_file_info = collections.deque( + sorted(files_info, key=lambda item: item.modification_time) + ) + diff = size - self.max_filesize + while diff > 0: + if not sorted_file_info: + break + + file_info = sorted_file_info.popleft() + diff -= file_info.size + os.remove(file_info.path) + + def get_thumbnail_filepath(self, project_name, thumbnail_id): + """Get thumbnail by thumbnail id. + + Args: + project_name (str): Name of project. + thumbnail_id (str): Thumbnail id. + + Returns: + Union[str, None]: Path to thumbnail image or None if thumbnail + is not cached yet. + """ + + if not thumbnail_id: + return None + + for ext in ( + ".png", + ".jpeg", + ): + filepath = os.path.join( + self.thumbnails_dir, project_name, thumbnail_id + ext + ) + if os.path.exists(filepath): + return filepath + return None + + def get_project_dir(self, project_name): + """Path to root directory for specific project. + + Args: + project_name (str): Name of project for which root directory path + should be returned. + + Returns: + str: Path to root of project's thumbnails. + """ + + return os.path.join(self.thumbnails_dir, project_name) + + def make_sure_project_dir_exists(self, project_name): + project_dir = self.get_project_dir(project_name) + if not os.path.exists(project_dir): + os.makedirs(project_dir) + return project_dir + + def store_thumbnail(self, project_name, thumbnail_id, content, mime_type): + """Store thumbnail to cache folder. + + Args: + project_name (str): Project where the thumbnail belong to. + thumbnail_id (str): Id of thumbnail. + content (bytes): Byte content of thumbnail file. + mime_data (str): Type of content. + + Returns: + str: Path to cached thumbnail image file. + """ + + if mime_type == "image/png": + ext = ".png" + elif mime_type == "image/jpeg": + ext = ".jpeg" + else: + raise ValueError( + "Unknown mime type for thumbnail \"{}\"".format(mime_type)) + + project_dir = self.make_sure_project_dir_exists(project_name) + thumbnail_path = os.path.join(project_dir, thumbnail_id + ext) + with open(thumbnail_path, "wb") as stream: + stream.write(content) + + current_time = time.time() + os.utime(thumbnail_path, (current_time, current_time)) + + return thumbnail_path + class ThumbnailsModel: entity_cache_lifetime = 240 # In seconds def __init__(self): - self._thumbnail_cache = AYONThumbnailCache() + self._thumbnail_cache = ThumbnailsCache() self._paths_cache = collections.defaultdict(dict) self._folders_cache = NestedCacheItem( levels=2, lifetime=self.entity_cache_lifetime) From d578a43be23756509731161e7022e109e1ab86eb Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 15:55:39 +0100 Subject: [PATCH 495/573] modified push to project tool to use AYON entities --- .../tools/push_to_project/control.py | 17 +- .../tools/push_to_project/models/integrate.py | 245 ++++++++---------- 2 files changed, 116 insertions(+), 146 deletions(-) diff --git a/client/ayon_core/tools/push_to_project/control.py b/client/ayon_core/tools/push_to_project/control.py index e74bc8c210..d5acaadc2a 100644 --- a/client/ayon_core/tools/push_to_project/control.py +++ b/client/ayon_core/tools/push_to_project/control.py @@ -2,7 +2,6 @@ import threading import ayon_api -from ayon_core.client import get_representations from ayon_core.settings import get_project_settings from ayon_core.lib import prepare_template_data from ayon_core.lib.events import QueuedEventSystem @@ -225,10 +224,12 @@ class PushToContextController: version_entity["version"] ) - def _get_task_info_from_repre_docs(self, task_entities, repre_docs): + def _get_task_info_from_repre_entities( + self, task_entities, repre_entities + ): found_comb = [] - for repre_doc in repre_docs: - context = repre_doc["context"] + for repre_entity in repre_entities: + context = repre_entity["context"] repre_task_name = context.get("task") if repre_task_name is None: continue @@ -257,11 +258,11 @@ class PushToContextController: project_name = self._src_project_name version_entity = self._src_version_entity task_entities = self._src_folder_task_entities - repre_docs = get_representations( - project_name, version_ids=[version_entity["id"]] + repre_entities = ayon_api.get_representations( + project_name, version_ids={version_entity["id"]} ) - task_name, task_type = self._get_task_info_from_repre_docs( - task_entities, repre_docs + task_name, task_type = self._get_task_info_from_repre_entities( + task_entities, repre_entities ) project_settings = get_project_settings(project_name) diff --git a/client/ayon_core/tools/push_to_project/models/integrate.py b/client/ayon_core/tools/push_to_project/models/integrate.py index 91b9a65a8c..30bccc46a3 100644 --- a/client/ayon_core/tools/push_to_project/models/integrate.py +++ b/client/ayon_core/tools/push_to_project/models/integrate.py @@ -2,25 +2,24 @@ import os import re import copy import itertools -import datetime import sys import traceback import uuid import ayon_api - -from ayon_core.client import get_representations -from ayon_core.client.operations import ( +from ayon_api.utils import create_entity_id +from ayon_api.operations import ( OperationsSession, - new_representation_doc, - prepare_representation_update_data, + new_folder_entity, + new_product_entity, + new_version_entity, + new_representation_entity, ) -from ayon_core.addon import AddonsManager + from ayon_core.lib import ( StringTemplate, source_hash, ) - from ayon_core.lib.file_transaction import FileTransaction from ayon_core.settings import get_project_settings from ayon_core.pipeline import Anatomy @@ -220,20 +219,20 @@ class ProjectPushRepreItem: but filenames are not template based. Args: - repre_doc (Dict[str, Ant]): Representation document. + repre_entity (Dict[str, Ant]): Representation entity. roots (Dict[str, str]): Project roots (based on project anatomy). """ - def __init__(self, repre_doc, roots): - self._repre_doc = repre_doc + def __init__(self, repre_entity, roots): + self._repre_entity = repre_entity self._roots = roots self._src_files = None self._resource_files = None self._frame = UNKNOWN @property - def repre_doc(self): - return self._repre_doc + def repre_entity(self): + return self._repre_entity @property def src_files(self): @@ -312,7 +311,7 @@ class ProjectPushRepreItem: if self._src_files is not None: return self._src_files, self._resource_files - repre_context = self._repre_doc["context"] + repre_context = self._repre_entity["context"] if "frame" in repre_context or "udim" in repre_context: src_files, resource_files = self._get_source_files_with_frames() else: @@ -329,7 +328,7 @@ class ProjectPushRepreItem: udim_placeholder = "__udim__" src_files = [] resource_files = [] - template = self._repre_doc["data"]["template"] + template = self._repre_entity["attrib"]["template"] # Remove padding from 'udim' and 'frame' formatting keys # - "{frame:0>4}" -> "{frame}" for key in ("udim", "frame"): @@ -337,7 +336,7 @@ class ProjectPushRepreItem: replacement = "{{{}}}".format(key) template = re.sub(sub_part, replacement, template) - repre_context = self._repre_doc["context"] + repre_context = self._repre_entity["context"] fill_repre_context = copy.deepcopy(repre_context) if "frame" in fill_repre_context: fill_repre_context["frame"] = frame_placeholder @@ -358,7 +357,7 @@ class ProjectPushRepreItem: .replace(udim_placeholder, "(?P[0-9]+)") ) src_basename_regex = re.compile("^{}$".format(src_basename)) - for file_info in self._repre_doc["files"]: + for file_info in self._repre_entity["files"]: filepath_template = self._clean_path(file_info["path"]) filepath = self._clean_path( filepath_template.format(root=self._roots) @@ -390,8 +389,8 @@ class ProjectPushRepreItem: def _get_source_files(self): src_files = [] resource_files = [] - template = self._repre_doc["data"]["template"] - repre_context = self._repre_doc["context"] + template = self._repre_entity["attrib"]["template"] + repre_context = self._repre_entity["context"] fill_repre_context = copy.deepcopy(repre_context) fill_roots = fill_repre_context["root"] for root_name in tuple(fill_roots.keys()): @@ -400,7 +399,7 @@ class ProjectPushRepreItem: fill_repre_context) repre_path = self._clean_path(repre_path) src_dirpath = os.path.dirname(repre_path) - for file_info in self._repre_doc["files"]: + for file_info in self._repre_entity["files"]: filepath_template = self._clean_path(file_info["path"]) filepath = self._clean_path( filepath_template.format(root=self._roots)) @@ -594,13 +593,13 @@ class ProjectPushItemProcess: anatomy = Anatomy(src_project_name) - repre_docs = get_representations( + repre_entities = ayon_api.get_representations( src_project_name, - version_ids=[src_version_id] + version_ids={src_version_id} ) repre_items = [ - ProjectPushRepreItem(repre_doc, anatomy.roots) - for repre_doc in repre_docs + ProjectPushRepreItem(repre_entity, anatomy.roots) + for repre_entity in repre_entities ] self._log_debug(( f"Found {len(repre_items)} representations on" @@ -694,38 +693,31 @@ class ProjectPushItemProcess: folder_label = None if new_folder_name != folder_name: folder_label = folder_name - fodler_create_data = { - "name": folder_name, - # TODO use different folder type? - "folderType": "Folder", - } - if parent_id: - fodler_create_data["parentId"] = parent_id + # TODO find out how to define folder type + folder_entity = new_folder_entity( + folder_name, + "Folder", + parent_id=parent_id, + attribs=new_folder_attrib + ) if folder_label: - fodler_create_data["label"] = folder_label + folder_entity["label"] = folder_label - if new_folder_attrib: - fodler_create_data["attrib"] = new_folder_attrib - - project_name = project_entity["name"] - response = ayon_api.post( - f"projects/{project_name}/folders", - **fodler_create_data + self._operations.create_entity( + project_entity["name"], + "folder", + folder_entity ) - new_folder_entity = ayon_api.get_folder_by_id( - project_name, response.data["id"] - ) - # TODO use operations - # self._operations.create_entity( - # project_entity["name"], - # "folder", - # fodler_create_data - # ) self._log_info( f"Creating new folder with name \"{folder_name}\"" ) - return new_folder_entity + # Calculate path for usage in rest of logic + parent_path = "" + if parent_folder_entity: + parent_path = parent_folder_entity["path"] + folder_entity["path"] = "/".join([parent_path, folder_name]) + return folder_entity def _fill_or_create_destination_folder(self): dst_project_name = self._item.dst_project_name @@ -826,12 +818,16 @@ class ProjectPushItemProcess: def _determine_product_name(self): product_type = self._product_type - folder_entity = self._folder_entity task_info = self._task_info + new_task_info = None + if task_info: + new_task_info = { + "name": task_info["name"], + "taskType": task_info["type"] + } product_name = get_product_name( self._item.dst_project_name, - folder_entity, - task_info, + new_task_info, self.host_name, product_type, self._item.variant, @@ -854,21 +850,14 @@ class ProjectPushItemProcess: self._product_entity = product_entity return product_entity - create_data = { - "name": product_name, - "productType": product_type, - "folderId": folder_id, - } - response = ayon_api.post( - "projects/{}/products".format(project_name), - **create_data + product_entity = new_product_entity( + product_name, + product_type, + folder_id, ) - product_entity = ayon_api.get_product_by_id( - project_name, response.data["id"] + self._operations.create_entity( + project_name, "product", product_entity ) - # self._operations.create_entity( - # project_name, "product", data - # ) self._product_entity = product_entity def _make_sure_version_exists(self): @@ -927,44 +916,24 @@ class ProjectPushItemProcess: ) # Update existing version if existing_version_entity: - ayon_api.patch( - "projects/{}/versions/{}".format( - project_name, existing_version_entity["id"] - ), - **{"attrib": dst_attrib} + self._operations.update_entity( + project_name, + "version", + existing_version_entity["id"], + {"attrib": dst_attrib} ) - version_entity = ayon_api.get_version_by_id( - project_name, existing_version_entity["id"] - ) - # TODO use operations - # self._operations.update_entity( - # project_name, - # "version", - # existing_version_entity["id"], - # update_data - # ) - self._version_entity = version_entity - + existing_version_entity["attrib"].update(dst_attrib) + self._version_entity = existing_version_entity return - response = ayon_api.post( - "projects/{}/versions/{}".format( - project_name, existing_version_entity["id"] - ), - **{ - "version": version, - "productId": product_id, - "attrib": dst_attrib, - } + version_entity = new_version_entity( + version, + product_id, + attribs=dst_attrib, ) - version_entity = ayon_api.get_version_by_id( - project_name, response.data["id"] + self._operations.create_entity( + project_name, "version", version_entity ) - # TODO use operations - # self._operations.create_entity( - # project_name, "version", version_entity - # ) - self._version_entity = version_entity def _integrate_representations(self): @@ -978,13 +947,13 @@ class ProjectPushItemProcess: def _real_integrate_representations(self): version_entity = self._version_entity version_id = version_entity["id"] - existing_repres = get_representations( + existing_repres = ayon_api.get_representations( self._item.dst_project_name, - version_ids=[version_id] + version_ids={version_id} ) existing_repres_by_low_name = { - repre_doc["name"].lower(): repre_doc - for repre_doc in existing_repres + repre_entity["name"].lower(): repre_entity + for repre_entity in existing_repres } template_name = self._template_name anatomy = self._anatomy @@ -1031,8 +1000,8 @@ class ProjectPushItemProcess: ): processed_repre_items = [] for repre_item in self._src_repre_items: - repre_doc = repre_item.repre_doc - repre_name = repre_doc["name"] + repre_entity = repre_item.repre_entity + repre_name = repre_entity["name"] repre_format_data = copy.deepcopy(formatting_data) repre_format_data["representation"] = repre_name for src_file in repre_item.src_files: @@ -1041,7 +1010,7 @@ class ProjectPushItemProcess: break # Re-use 'output' from source representation - repre_output_name = repre_doc["context"].get("output") + repre_output_name = repre_entity["context"].get("output") if repre_output_name is not None: repre_format_data["output"] = repre_output_name @@ -1098,34 +1067,24 @@ class ProjectPushItemProcess: path_template, existing_repres_by_low_name ): - addons_manager = AddonsManager() - sync_server_module = addons_manager.get("sync_server") - if sync_server_module is None or not sync_server_module.enabled: - sites = [{ - "name": "studio", - "created_dt": datetime.datetime.now() - }] - else: - sites = sync_server_module.compute_resource_sync_sites( - project_name=self._item.dst_project_name - ) - added_repre_names = set() for item in processed_repre_items: (repre_item, repre_filepaths, repre_context, published_path) = item - repre_name = repre_item.repre_doc["name"] + repre_name = repre_item.repre_entity["name"] added_repre_names.add(repre_name.lower()) - new_repre_data = { + new_repre_attributes = { "path": published_path, "template": path_template } new_repre_files = [] for (path, rootless_path) in repre_filepaths: new_repre_files.append({ + "id": create_entity_id(), + "name": os.path.basename(rootless_path), "path": rootless_path, "size": os.path.getsize(path), "hash": source_hash(path), - "sites": sites + "hash_type": "op3", }) existing_repre = existing_repres_by_low_name.get( @@ -1133,41 +1092,51 @@ class ProjectPushItemProcess: ) entity_id = None if existing_repre: - entity_id = existing_repre["_id"] - new_repre_doc = new_representation_doc( + entity_id = existing_repre["id"] + repre_entity = new_representation_entity( repre_name, version_id, - repre_context, - data=new_repre_data, + new_repre_files, + data={"context": repre_context}, + attribs=new_repre_attributes, entity_id=entity_id ) - new_repre_doc["files"] = new_repre_files if not existing_repre: self._operations.create_entity( self._item.dst_project_name, - new_repre_doc["type"], - new_repre_doc + "representation", + repre_entity ) else: - update_data = prepare_representation_update_data( - existing_repre, new_repre_doc - ) - if update_data: + changes = {} + for key, value in repre_entity.items(): + if key == "attrib": + continue + if value != existing_repre.get(key): + changes[key] = value + attrib_changes = {} + for key, value in repre_entity["attrib"].items(): + if value != existing_repre["attrib"].get(key): + attrib_changes[key] = value + if attrib_changes: + changes["attrib"] = attrib_changes + + if changes: self._operations.update_entity( self._item.dst_project_name, - new_repre_doc["type"], - new_repre_doc["_id"], - update_data + "representation", + entity_id, + changes ) existing_repre_names = set(existing_repres_by_low_name.keys()) for repre_name in (existing_repre_names - added_repre_names): - repre_doc = existing_repres_by_low_name[repre_name] + repre_entity = existing_repres_by_low_name[repre_name] self._operations.update_entity( self._item.dst_project_name, - repre_doc["type"], - repre_doc["_id"], - {"type": "archived_representation"} + "representation", + repre_entity["id"], + {"active": False} ) From 66ad6e824232e79647cc020babcc33caab0b2118 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 15:55:56 +0100 Subject: [PATCH 496/573] workfiles tool is using ayon api operations --- .../ayon_core/tools/workfiles/models/workfiles.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/tools/workfiles/models/workfiles.py b/client/ayon_core/tools/workfiles/models/workfiles.py index ae8efaf23a..35a49a908f 100644 --- a/client/ayon_core/tools/workfiles/models/workfiles.py +++ b/client/ayon_core/tools/workfiles/models/workfiles.py @@ -6,9 +6,6 @@ import arrow import ayon_api from ayon_api.operations import OperationsSession -from ayon_core.client.operations import ( - prepare_workfile_info_update_data, -) from ayon_core.pipeline.template_data import ( get_template_data, get_task_template_data, @@ -474,22 +471,24 @@ class WorkfileEntitiesModel: if note is None: return + old_note = workfile_info.get("attrib", {}).get("note") + new_workfile_info = copy.deepcopy(workfile_info) attrib = new_workfile_info.setdefault("attrib", {}) attrib["description"] = note - update_data = prepare_workfile_info_update_data( - workfile_info, new_workfile_info - ) self._cache[identifier] = new_workfile_info self._items.pop(identifier, None) - if not update_data: + if old_note == note: return project_name = self._controller.get_current_project_name() session = OperationsSession() session.update_entity( - project_name, "workfile", workfile_info["id"], update_data + project_name, + "workfile", + workfile_info["id"], + {"attrib": {"description": note}}, ) session.commit() From fedf09c364fe4230cf953df2681ffcafb18b1fa2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 15:56:06 +0100 Subject: [PATCH 497/573] formatting changes --- .../ayon_core/hosts/maya/plugins/publish/extract_look.py | 9 +++++---- client/ayon_core/pipeline/create/product_name.py | 2 +- client/ayon_core/tools/loader/models/actions.py | 4 ---- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_look.py b/client/ayon_core/hosts/maya/plugins/publish/extract_look.py index 469608100d..fa74dd18d3 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_look.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_look.py @@ -1,16 +1,17 @@ # -*- coding: utf-8 -*- """Maya look extractor.""" +import os import sys -from abc import ABCMeta, abstractmethod -from collections import OrderedDict import contextlib import json import logging -import os import tempfile +import platform +from abc import ABCMeta, abstractmethod +from collections import OrderedDict + import six import attr - import pyblish.api from maya import cmds # noqa diff --git a/client/ayon_core/pipeline/create/product_name.py b/client/ayon_core/pipeline/create/product_name.py index 8976fb2ff0..6da357b5aa 100644 --- a/client/ayon_core/pipeline/create/product_name.py +++ b/client/ayon_core/pipeline/create/product_name.py @@ -106,7 +106,7 @@ def get_product_name( Args: project_name (str): Project name. - task_entity (Optional[Dict[str, Any]]): Task entity. + task_entity (Dict[str, Any]): Task entity. host_name (str): Host name. product_type (str): Product type. variant (str): In most of the cases it is user input during creation. diff --git a/client/ayon_core/tools/loader/models/actions.py b/client/ayon_core/tools/loader/models/actions.py index d8ae30b49f..39c0ca5693 100644 --- a/client/ayon_core/tools/loader/models/actions.py +++ b/client/ayon_core/tools/loader/models/actions.py @@ -27,10 +27,6 @@ NOT_SET = object() class LoaderActionsModel: """Model for loader actions. - This is probably only part of models that requires to use codebase from - 'ayon_core.client' because of backwards compatibility with loaders logic - which are expecting entities. - TODOs: Deprecate 'qargparse' usage in loaders and implement conversion of 'ActionItem' to data (and 'from_data'). From c359d446de219b81a6df6238e205a648ee5955a7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 15:56:35 +0100 Subject: [PATCH 498/573] removed unused import --- client/ayon_core/plugins/publish/integrate_hero_version.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/ayon_core/plugins/publish/integrate_hero_version.py b/client/ayon_core/plugins/publish/integrate_hero_version.py index 9e4265f853..75fc444a55 100644 --- a/client/ayon_core/plugins/publish/integrate_hero_version.py +++ b/client/ayon_core/plugins/publish/integrate_hero_version.py @@ -11,9 +11,6 @@ from ayon_api.operations import ( new_version_entity, ) -from ayon_core.client.operations import ( - new_hero_version_doc, -) from ayon_core.lib import create_hard_link from ayon_core.pipeline.publish import get_publish_template_name From 0dc27825ce93fb4e3afb83bea22d13ed22581cfd Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 16:00:26 +0100 Subject: [PATCH 499/573] removed functions from 'ayon_core.client' --- client/ayon_core/client/__init__.py | 96 -- client/ayon_core/client/constants.py | 26 - client/ayon_core/client/conversion_utils.py | 1136 ------------------- client/ayon_core/client/entities.py | 702 ------------ client/ayon_core/client/entity_links.py | 157 --- client/ayon_core/client/notes.md | 39 - client/ayon_core/client/openpype_comp.py | 159 --- client/ayon_core/client/operations.py | 838 -------------- client/ayon_core/client/operations_base.py | 289 ----- client/ayon_core/client/utils.py | 127 +-- 10 files changed, 1 insertion(+), 3568 deletions(-) delete mode 100644 client/ayon_core/client/constants.py delete mode 100644 client/ayon_core/client/conversion_utils.py delete mode 100644 client/ayon_core/client/entities.py delete mode 100644 client/ayon_core/client/entity_links.py delete mode 100644 client/ayon_core/client/notes.md delete mode 100644 client/ayon_core/client/openpype_comp.py delete mode 100644 client/ayon_core/client/operations.py delete mode 100644 client/ayon_core/client/operations_base.py diff --git a/client/ayon_core/client/__init__.py b/client/ayon_core/client/__init__.py index c2ed774392..ea651ba8db 100644 --- a/client/ayon_core/client/__init__.py +++ b/client/ayon_core/client/__init__.py @@ -1,102 +1,6 @@ from .utils import get_ayon_server_api_connection -from .entities import ( - get_asset_by_id, - get_asset_by_name, - get_assets, - get_archived_assets, - get_asset_ids_with_subsets, - - get_subset_by_id, - get_subset_by_name, - get_subsets, - get_subset_families, - - get_version_by_id, - get_version_by_name, - get_versions, - get_hero_version_by_id, - get_hero_version_by_subset_id, - get_hero_versions, - get_last_versions, - get_last_version_by_subset_id, - get_last_version_by_subset_name, - get_output_link_versions, - - version_is_latest, - - get_representation_by_id, - get_representation_by_name, - get_representations, - get_representation_parents, - get_representations_parents, - get_archived_representations, - - get_thumbnail, - get_thumbnails, - get_thumbnail_id_from_source, - - get_workfile_info, - - get_asset_name_identifier, -) - -from .entity_links import ( - get_linked_asset_ids, - get_linked_assets, - get_linked_representation_id, -) - -from .operations import ( - create_project, -) - __all__ = ( "get_ayon_server_api_connection", - - "get_asset_by_id", - "get_asset_by_name", - "get_assets", - "get_archived_assets", - "get_asset_ids_with_subsets", - - "get_subset_by_id", - "get_subset_by_name", - "get_subsets", - "get_subset_families", - - "get_version_by_id", - "get_version_by_name", - "get_versions", - "get_hero_version_by_id", - "get_hero_version_by_subset_id", - "get_hero_versions", - "get_last_versions", - "get_last_version_by_subset_id", - "get_last_version_by_subset_name", - "get_output_link_versions", - - "version_is_latest", - - "get_representation_by_id", - "get_representation_by_name", - "get_representations", - "get_representation_parents", - "get_representations_parents", - "get_archived_representations", - - "get_thumbnail", - "get_thumbnails", - "get_thumbnail_id_from_source", - - "get_workfile_info", - - "get_linked_asset_ids", - "get_linked_assets", - "get_linked_representation_id", - - "create_project", - - "get_asset_name_identifier", ) diff --git a/client/ayon_core/client/constants.py b/client/ayon_core/client/constants.py deleted file mode 100644 index 0d1db74d85..0000000000 --- a/client/ayon_core/client/constants.py +++ /dev/null @@ -1,26 +0,0 @@ -# --- Folders --- -DEFAULT_FOLDER_FIELDS = { - "id", - "name", - "path", - "parentId", - "active", - "parents", - "thumbnailId" -} - -REPRESENTATION_FILES_FIELDS = { - "files.name", - "files.hash", - "files.id", - "files.path", - "files.size", -} - -CURRENT_ASSET_DOC_SCHEMA = "openpype:asset-3.0" -CURRENT_SUBSET_SCHEMA = "openpype:subset-3.0" -CURRENT_VERSION_SCHEMA = "openpype:version-3.0" -CURRENT_HERO_VERSION_SCHEMA = "openpype:hero_version-1.0" -CURRENT_REPRESENTATION_SCHEMA = "openpype:representation-2.0" -CURRENT_WORKFILE_INFO_SCHEMA = "openpype:workfile-1.0" -CURRENT_THUMBNAIL_SCHEMA = "openpype:thumbnail-1.0" diff --git a/client/ayon_core/client/conversion_utils.py b/client/ayon_core/client/conversion_utils.py deleted file mode 100644 index 039384a81b..0000000000 --- a/client/ayon_core/client/conversion_utils.py +++ /dev/null @@ -1,1136 +0,0 @@ -import os -import arrow -import collections -import json - -import six - -from ayon_core.client.operations_base import REMOVED_VALUE -from .constants import ( - CURRENT_ASSET_DOC_SCHEMA, - CURRENT_SUBSET_SCHEMA, - CURRENT_VERSION_SCHEMA, - CURRENT_HERO_VERSION_SCHEMA, - CURRENT_REPRESENTATION_SCHEMA, - CURRENT_WORKFILE_INFO_SCHEMA, - REPRESENTATION_FILES_FIELDS, -) -from .utils import create_entity_id, prepare_entity_changes - -# TODO this should not be hardcoded but received from server!!! -# --- Folder entity --- -FOLDER_FIELDS_MAPPING_V3_V4 = { - "_id": {"id"}, - "name": {"name"}, - "label": {"label"}, - "data": { - "parentId", "parents", "active", "tasks", "thumbnailId" - }, - "data.visualParent": {"parentId"}, - "data.parents": {"parents"}, - "data.active": {"active"}, - "data.thumbnail_id": {"thumbnailId"}, - "data.entityType": {"folderType"} -} - -# --- Subset entity --- -SUBSET_FIELDS_MAPPING_V3_V4 = { - "_id": {"id"}, - "name": {"name"}, - "data.active": {"active"}, - "parent": {"folderId"} -} - -# --- Version entity --- -VERSION_FIELDS_MAPPING_V3_V4 = { - "_id": {"id"}, - "name": {"version"}, - "parent": {"productId"} -} - -# --- Representation entity --- -REPRESENTATION_FIELDS_MAPPING_V3_V4 = { - "_id": {"id"}, - "name": {"name"}, - "parent": {"versionId"}, - "context": {"context"}, - "files": {"files"}, -} - - -def folder_fields_v3_to_v4(fields, con): - """Convert folder fields from v3 to v4 structure. - - Args: - fields (Union[Iterable(str), None]): fields to be converted. - - Returns: - Union[Set(str), None]: Converted fields to v4 fields. - """ - - if not fields: - return None - - folder_attributes = con.get_attributes_for_type("folder") - output = set() - for field in fields: - if field in ("schema", "type", "parent"): - continue - - if field in FOLDER_FIELDS_MAPPING_V3_V4: - output |= FOLDER_FIELDS_MAPPING_V3_V4[field] - if field == "data": - output |= { - "attrib.{}".format(attr) - for attr in folder_attributes - } - - elif field.startswith("data"): - field_parts = field.split(".") - field_parts.pop(0) - data_key = ".".join(field_parts) - if data_key == "label": - output.add("name") - - elif data_key in ("icon", "color"): - continue - - elif data_key.startswith("tasks"): - output.add("tasks") - - elif data_key in folder_attributes: - output.add("attrib.{}".format(data_key)) - - else: - output.add("data") - print("Requested specific key from data {}".format(data_key)) - - else: - raise ValueError("Unknown field mapping for {}".format(field)) - - if "id" not in output: - output.add("id") - return output - - -def convert_v4_tasks_to_v3(tasks): - """Convert v4 task item to v3 task. - - Args: - tasks (List[Dict[str, Any]]): Task entites. - - Returns: - Dict[str, Dict[str, Any]]: Tasks in v3 variant ready for v3 asset. - """ - - output = {} - for task in tasks: - task_name = task["name"] - new_task = { - "type": task["taskType"] - } - output[task_name] = new_task - return output - - -def convert_v4_folder_to_v3(folder, project_name): - """Convert v4 folder to v3 asset. - - Args: - folder (Dict[str, Any]): Folder entity data. - project_name (str): Project name from which folder was queried. - - Returns: - Dict[str, Any]: Converted v4 folder to v3 asset. - """ - - output = { - "_id": folder["id"], - "parent": project_name, - "type": "asset", - "schema": CURRENT_ASSET_DOC_SCHEMA - } - - output_data = folder.get("data") or {} - - if "name" in folder: - output["name"] = folder["name"] - output_data["label"] = folder["name"] - - if "folderType" in folder: - output_data["entityType"] = folder["folderType"] - - for src_key, dst_key in ( - ("parentId", "visualParent"), - ("active", "active"), - ("thumbnailId", "thumbnail_id"), - ("parents", "parents"), - ): - if src_key in folder: - output_data[dst_key] = folder[src_key] - - if "attrib" in folder: - output_data.update(folder["attrib"]) - - if "tools" in output_data: - output_data["tools_env"] = output_data.pop("tools") - - if "tasks" in folder: - output_data["tasks"] = convert_v4_tasks_to_v3(folder["tasks"]) - - output["data"] = output_data - - return output - - -def subset_fields_v3_to_v4(fields, con): - """Convert subset fields from v3 to v4 structure. - - Args: - fields (Union[Iterable(str), None]): fields to be converted. - - Returns: - Union[Set(str), None]: Converted fields to v4 fields. - """ - - if not fields: - return None - - product_attributes = con.get_attributes_for_type("product") - - output = set() - for field in fields: - if field in ("schema", "type"): - continue - - if field in SUBSET_FIELDS_MAPPING_V3_V4: - output |= SUBSET_FIELDS_MAPPING_V3_V4[field] - - elif field == "data": - output.add("productType") - output.add("active") - output |= { - "attrib.{}".format(attr) - for attr in product_attributes - } - - elif field.startswith("data"): - field_parts = field.split(".") - field_parts.pop(0) - data_key = ".".join(field_parts) - if data_key in ("family", "families"): - output.add("productType") - - elif data_key in product_attributes: - output.add("attrib.{}".format(data_key)) - - else: - output.add("data") - print("Requested specific key from data {}".format(data_key)) - - else: - raise ValueError("Unknown field mapping for {}".format(field)) - - if "id" not in output: - output.add("id") - return output - - -def convert_v4_subset_to_v3(subset): - output = { - "_id": subset["id"], - "type": "subset", - "schema": CURRENT_SUBSET_SCHEMA - } - if "folderId" in subset: - output["parent"] = subset["folderId"] - - output_data = subset.get("data") or {} - - if "name" in subset: - output["name"] = subset["name"] - - if "active" in subset: - output_data["active"] = subset["active"] - - if "attrib" in subset: - attrib = subset["attrib"] - if "productGroup" in attrib: - attrib["subsetGroup"] = attrib.pop("productGroup") - output_data.update(attrib) - - family = subset.get("productType") - if family: - output_data["family"] = family - output_data["families"] = [family] - - output["data"] = output_data - - return output - - -def version_fields_v3_to_v4(fields, con): - """Convert version fields from v3 to v4 structure. - - Args: - fields (Union[Iterable(str), None]): fields to be converted. - - Returns: - Union[Set(str), None]: Converted fields to v4 fields. - """ - - if not fields: - return None - - version_attributes = con.get_attributes_for_type("version") - - output = set() - for field in fields: - if field in ("type", "schema", "version_id"): - continue - - if field in VERSION_FIELDS_MAPPING_V3_V4: - output |= VERSION_FIELDS_MAPPING_V3_V4[field] - - elif field == "data": - output |= { - "attrib.{}".format(attr) - for attr in version_attributes - } - output |= { - "author", - "createdAt", - "thumbnailId", - } - - elif field.startswith("data"): - field_parts = field.split(".") - field_parts.pop(0) - data_key = ".".join(field_parts) - if data_key in version_attributes: - output.add("attrib.{}".format(data_key)) - - elif data_key == "thumbnail_id": - output.add("thumbnailId") - - elif data_key == "time": - output.add("createdAt") - - elif data_key == "author": - output.add("author") - - elif data_key in ("tags", ): - continue - - else: - output.add("data") - print("Requested specific key from data {}".format(data_key)) - - else: - raise ValueError("Unknown field mapping for {}".format(field)) - - if "id" not in output: - output.add("id") - return output - - -def convert_v4_version_to_v3(version): - """Convert v4 version entity to v4 version. - - Args: - version (Dict[str, Any]): Queried v4 version entity. - - Returns: - Dict[str, Any]: Conveted version entity to v3 structure. - """ - - version_num = version["version"] - if version_num < 0: - output = { - "_id": version["id"], - "type": "hero_version", - "schema": CURRENT_HERO_VERSION_SCHEMA, - } - if "productId" in version: - output["parent"] = version["productId"] - - if "data" in version: - output["data"] = version["data"] - return output - - output = { - "_id": version["id"], - "type": "version", - "name": version_num, - "schema": CURRENT_VERSION_SCHEMA - } - if "productId" in version: - output["parent"] = version["productId"] - - output_data = version.get("data") or {} - if "attrib" in version: - output_data.update(version["attrib"]) - - for src_key, dst_key in ( - ("active", "active"), - ("thumbnailId", "thumbnail_id"), - ("author", "author") - ): - if src_key in version: - output_data[dst_key] = version[src_key] - - if "createdAt" in version: - created_at = arrow.get(version["createdAt"]).to("local") - output_data["time"] = created_at.strftime("%Y%m%dT%H%M%SZ") - - output["data"] = output_data - - return output - - -def representation_fields_v3_to_v4(fields, con): - """Convert representation fields from v3 to v4 structure. - - Args: - fields (Union[Iterable(str), None]): fields to be converted. - - Returns: - Union[Set(str), None]: Converted fields to v4 fields. - """ - - if not fields: - return None - - representation_attributes = con.get_attributes_for_type("representation") - - output = set() - for field in fields: - if field in ("type", "schema"): - continue - - if field in REPRESENTATION_FIELDS_MAPPING_V3_V4: - output |= REPRESENTATION_FIELDS_MAPPING_V3_V4[field] - - elif field.startswith("context"): - output.add("context") - - # TODO: 'files' can have specific attributes but the keys in v3 and v4 - # are not the same (content is not the same) - elif field.startswith("files"): - output |= REPRESENTATION_FILES_FIELDS - - elif field.startswith("data"): - output |= { - "attrib.{}".format(attr) - for attr in representation_attributes - } - - else: - raise ValueError("Unknown field mapping for {}".format(field)) - - if "id" not in output: - output.add("id") - return output - - -def convert_v4_representation_to_v3(representation): - """Convert v4 representation to v3 representation. - - Args: - representation (Dict[str, Any]): Queried representation from v4 server. - - Returns: - Dict[str, Any]: Converted representation to v3 structure. - """ - - output = { - "type": "representation", - "schema": CURRENT_REPRESENTATION_SCHEMA, - } - if "id" in representation: - output["_id"] = representation["id"] - - for v3_key, v4_key in ( - ("name", "name"), - ("parent", "versionId") - ): - if v4_key in representation: - output[v3_key] = representation[v4_key] - - if "context" in representation: - context = representation["context"] - if isinstance(context, six.string_types): - context = json.loads(context) - - if "asset" not in context and "folder" in context: - _c_folder = context["folder"] - context["asset"] = _c_folder["name"] - - elif "asset" in context and "folder" not in context: - context["folder"] = {"name": context["asset"]} - - if "product" in context: - _c_product = context.pop("product") - context["family"] = _c_product["type"] - context["subset"] = _c_product["name"] - - output["context"] = context - - if "files" in representation: - files = representation["files"] - new_files = [] - # From GraphQl is list - if isinstance(files, list): - for file_info in files: - file_info["_id"] = file_info["id"] - new_files.append(file_info) - - # From RestPoint is dictionary - elif isinstance(files, dict): - for file_id, file_info in files: - file_info["_id"] = file_id - new_files.append(file_info) - - for file_info in new_files: - if not file_info.get("sites"): - file_info["sites"] = [{ - "name": "studio" - }] - - output["files"] = new_files - - if representation.get("active") is False: - output["type"] = "archived_representation" - output["old_id"] = output["_id"] - - output_data = representation.get("data") or {} - if "attrib" in representation: - output_data.update(representation["attrib"]) - - for key, data_key in ( - ("active", "active"), - ): - if key in representation: - output_data[data_key] = representation[key] - - if "template" in output_data: - output_data["template"] = ( - output_data["template"] - .replace("{product[name]}", "{subset}") - .replace("{product[type]}", "{family}") - ) - - output["data"] = output_data - - return output - - -def workfile_info_fields_v3_to_v4(fields): - if not fields: - return None - - new_fields = set() - fields = set(fields) - for v3_key, v4_key in ( - ("_id", "id"), - ("files", "path"), - ("filename", "name"), - ("data", "data"), - ): - if v3_key in fields: - new_fields.add(v4_key) - - if "parent" in fields or "task_name" in fields: - new_fields.add("taskId") - - return new_fields - - -def convert_v4_workfile_info_to_v3(workfile_info, task): - output = { - "type": "workfile", - "schema": CURRENT_WORKFILE_INFO_SCHEMA, - } - if "id" in workfile_info: - output["_id"] = workfile_info["id"] - - if "path" in workfile_info: - output["files"] = [workfile_info["path"]] - - if "name" in workfile_info: - output["filename"] = workfile_info["name"] - - if "taskId" in workfile_info: - output["task_name"] = task["name"] - output["parent"] = task["folderId"] - - return output - - -def convert_create_asset_to_v4(asset, project, con): - folder_attributes = con.get_attributes_for_type("folder") - - asset_data = asset["data"] - parent_id = asset_data["visualParent"] - - folder = { - "name": asset["name"], - "parentId": parent_id, - } - entity_id = asset.get("_id") - if entity_id: - folder["id"] = entity_id - - attribs = {} - data = {} - for key, value in asset_data.items(): - if key in ( - "visualParent", - "thumbnail_id", - "parents", - "inputLinks", - "avalon_mongo_id", - ): - continue - - if key not in folder_attributes: - data[key] = value - elif value is not None: - attribs[key] = value - - if attribs: - folder["attrib"] = attribs - - if data: - folder["data"] = data - return folder - - -def convert_create_task_to_v4(task, project, con): - if not project["taskTypes"]: - raise ValueError( - "Project \"{}\" does not have any task types".format( - project["name"])) - - task_type = task["type"] - if task_type not in project["taskTypes"]: - task_type = tuple(project["taskTypes"].keys())[0] - - return { - "name": task["name"], - "taskType": task_type, - "folderId": task["folderId"] - } - - -def convert_create_subset_to_v4(subset, con): - product_attributes = con.get_attributes_for_type("product") - - subset_data = subset["data"] - product_type = subset_data.get("family") - if not product_type: - product_type = subset_data["families"][0] - - converted_product = { - "name": subset["name"], - "productType": product_type, - "folderId": subset["parent"], - } - entity_id = subset.get("_id") - if entity_id: - converted_product["id"] = entity_id - - attribs = {} - data = {} - if "subsetGroup" in subset_data: - subset_data["productGroup"] = subset_data.pop("subsetGroup") - for key, value in subset_data.items(): - if key not in product_attributes: - data[key] = value - elif value is not None: - attribs[key] = value - - if attribs: - converted_product["attrib"] = attribs - - if data: - converted_product["data"] = data - - return converted_product - - -def convert_create_version_to_v4(version, con): - version_attributes = con.get_attributes_for_type("version") - converted_version = { - "version": version["name"], - "productId": version["parent"], - } - entity_id = version.get("_id") - if entity_id: - converted_version["id"] = entity_id - - version_data = version["data"] - attribs = {} - data = {} - for key, value in version_data.items(): - if key not in version_attributes: - data[key] = value - elif value is not None: - attribs[key] = value - - if attribs: - converted_version["attrib"] = attribs - - if data: - converted_version["data"] = attribs - - return converted_version - - -def convert_create_hero_version_to_v4(hero_version, project_name, con): - if "version_id" in hero_version: - version_id = hero_version["version_id"] - version = con.get_version_by_id(project_name, version_id) - version["version"] = - version["version"] - - for auto_key in ( - "name", - "createdAt", - "updatedAt", - "author", - ): - version.pop(auto_key, None) - - return version - - version_attributes = con.get_attributes_for_type("version") - converted_version = { - "version": hero_version["version"], - "productId": hero_version["parent"], - } - entity_id = hero_version.get("_id") - if entity_id: - converted_version["id"] = entity_id - - version_data = hero_version["data"] - attribs = {} - data = {} - for key, value in version_data.items(): - if key not in version_attributes: - data[key] = value - elif value is not None: - attribs[key] = value - - if attribs: - converted_version["attrib"] = attribs - - if data: - converted_version["data"] = attribs - - return converted_version - - -def convert_create_representation_to_v4(representation, con): - representation_attributes = con.get_attributes_for_type("representation") - - converted_representation = { - "name": representation["name"], - "versionId": representation["parent"], - } - entity_id = representation.get("_id") - if entity_id: - converted_representation["id"] = entity_id - - if representation.get("type") == "archived_representation": - converted_representation["active"] = False - - new_files = [] - for file_item in representation["files"]: - new_file_item = { - key: value - for key, value in file_item.items() - if key in ("hash", "path", "size") - } - new_file_item.update({ - "id": create_entity_id(), - "hash_type": "op3", - "name": os.path.basename(new_file_item["path"]) - }) - new_files.append(new_file_item) - - converted_representation["files"] = new_files - - context = representation["context"] - if "folder" not in context: - context["folder"] = { - "name": context.get("asset") - } - - context["product"] = { - "type": context.pop("family", None), - "name": context.pop("subset", None), - } - - attribs = {} - data = { - "context": context, - } - - representation_data = representation["data"] - representation_data["template"] = ( - representation_data["template"] - .replace("{subset}", "{product[name]}") - .replace("{family}", "{product[type]}") - ) - - for key, value in representation_data.items(): - if key not in representation_attributes: - data[key] = value - elif value is not None: - attribs[key] = value - - if attribs: - converted_representation["attrib"] = attribs - - if data: - converted_representation["data"] = data - - return converted_representation - - -def convert_create_workfile_info_to_v4(data, project_name, con): - folder_id = data["parent"] - task_name = data["task_name"] - task = con.get_task_by_name(project_name, folder_id, task_name) - if not task: - return None - - workfile_attributes = con.get_attributes_for_type("workfile") - filename = data["filename"] - possible_attribs = { - "extension": os.path.splitext(filename)[-1] - } - attribs = {} - for attr in workfile_attributes: - if attr in possible_attribs: - attribs[attr] = possible_attribs[attr] - - output = { - "path": data["files"][0], - "name": filename, - "taskId": task["id"] - } - if "_id" in data: - output["id"] = data["_id"] - - if attribs: - output["attrib"] = attribs - - output_data = data.get("data") - if output_data: - output["data"] = output_data - return output - - -def _from_flat_dict(data): - output = {} - for key, value in data.items(): - output_value = output - subkeys = key.split(".") - last_key = subkeys.pop(-1) - for subkey in subkeys: - if subkey not in output_value: - output_value[subkey] = {} - output_value = output_value[subkey] - - output_value[last_key] = value - return output - - -def _to_flat_dict(data): - output = {} - flat_queue = collections.deque() - flat_queue.append(([], data)) - while flat_queue: - item = flat_queue.popleft() - parent_keys, data = item - for key, value in data.items(): - keys = list(parent_keys) - keys.append(key) - if isinstance(value, dict): - flat_queue.append((keys, value)) - else: - full_key = ".".join(keys) - output[full_key] = value - - return output - - -def convert_update_folder_to_v4(project_name, asset_id, update_data, con): - new_update_data = {} - - folder_attributes = con.get_attributes_for_type("folder") - full_update_data = _from_flat_dict(update_data) - data = full_update_data.get("data") - - has_new_parent = False - has_task_changes = False - parent_id = None - tasks = None - new_data = {} - attribs = full_update_data.pop("attrib", {}) - if "type" in update_data: - new_update_data["active"] = update_data["type"] == "asset" - - if data: - if "thumbnail_id" in data: - new_update_data["thumbnailId"] = data.pop("thumbnail_id") - - if "tasks" in data: - tasks = data.pop("tasks") - has_task_changes = True - - if "visualParent" in data: - has_new_parent = True - parent_id = data.pop("visualParent") - - for key, value in data.items(): - if key in folder_attributes: - attribs[key] = value - else: - new_data[key] = value - - if "name" in update_data: - new_update_data["name"] = update_data["name"] - - if "type" in update_data: - new_type = update_data["type"] - if new_type == "asset": - new_update_data["active"] = True - elif new_type == "archived_asset": - new_update_data["active"] = False - - if has_new_parent: - new_update_data["parentId"] = parent_id - - if new_data: - print("Folder has new data: {}".format(new_data)) - new_update_data["data"] = new_data - - if attribs: - new_update_data["attrib"] = attribs - - if has_task_changes: - raise ValueError("Task changes of folder are not implemented") - - return _to_flat_dict(new_update_data) - - -def convert_update_subset_to_v4(project_name, subset_id, update_data, con): - new_update_data = {} - - product_attributes = con.get_attributes_for_type("product") - full_update_data = _from_flat_dict(update_data) - data = full_update_data.get("data") - new_data = {} - attribs = full_update_data.pop("attrib", {}) - if data: - if "family" in data: - family = data.pop("family") - new_update_data["productType"] = family - - if "families" in data: - families = data.pop("families") - if "productType" not in new_update_data: - new_update_data["productType"] = families[0] - - if "subsetGroup" in data: - data["productGroup"] = data.pop("subsetGroup") - for key, value in data.items(): - if key in product_attributes: - if value is REMOVED_VALUE: - value = None - attribs[key] = value - - elif value is not REMOVED_VALUE: - new_data[key] = value - - if "name" in update_data: - new_update_data["name"] = update_data["name"] - - if "type" in update_data: - new_type = update_data["type"] - if new_type == "subset": - new_update_data["active"] = True - elif new_type == "archived_subset": - new_update_data["active"] = False - - if "parent" in update_data: - new_update_data["folderId"] = update_data["parent"] - - flat_data = _to_flat_dict(new_update_data) - if attribs: - flat_data["attrib"] = attribs - - if new_data: - print("Subset has new data: {}".format(new_data)) - flat_data["data"] = new_data - - return flat_data - - -def convert_update_version_to_v4(project_name, version_id, update_data, con): - new_update_data = {} - - version_attributes = con.get_attributes_for_type("version") - full_update_data = _from_flat_dict(update_data) - data = full_update_data.get("data") - new_data = {} - attribs = full_update_data.pop("attrib", {}) - if data: - if "author" in data: - new_update_data["author"] = data.pop("author") - - if "thumbnail_id" in data: - new_update_data["thumbnailId"] = data.pop("thumbnail_id") - - for key, value in data.items(): - if key in version_attributes: - if value is REMOVED_VALUE: - value = None - attribs[key] = value - - elif value is not REMOVED_VALUE: - new_data[key] = value - - if "name" in update_data: - new_update_data["version"] = update_data["name"] - - if "type" in update_data: - new_type = update_data["type"] - if new_type == "version": - new_update_data["active"] = True - elif new_type == "archived_version": - new_update_data["active"] = False - - if "parent" in update_data: - new_update_data["productId"] = update_data["parent"] - - flat_data = _to_flat_dict(new_update_data) - if attribs: - flat_data["attrib"] = attribs - - if new_data: - print("Version has new data: {}".format(new_data)) - flat_data["data"] = new_data - return flat_data - - -def convert_update_hero_version_to_v4( - project_name, hero_version_id, update_data, con -): - if "version_id" not in update_data: - return None - - version_id = update_data["version_id"] - hero_version = con.get_hero_version_by_id(project_name, hero_version_id) - version = con.get_version_by_id(project_name, version_id) - version["version"] = - version["version"] - version["id"] = hero_version_id - - for auto_key in ( - "name", - "createdAt", - "updatedAt", - "author", - ): - version.pop(auto_key, None) - - return prepare_entity_changes(hero_version, version) - - -def convert_update_representation_to_v4( - project_name, repre_id, update_data, con -): - new_update_data = {} - - folder_attributes = con.get_attributes_for_type("folder") - full_update_data = _from_flat_dict(update_data) - data = full_update_data.get("data") - - new_data = {} - attribs = full_update_data.pop("attrib", {}) - if data: - for key, value in data.items(): - if key in folder_attributes: - attribs[key] = value - else: - new_data[key] = value - - if "template" in attribs: - attribs["template"] = ( - attribs["template"] - .replace("{family}", "{product[type]}") - .replace("{subset}", "{product[name]}") - ) - - if "name" in update_data: - new_update_data["name"] = update_data["name"] - - if "type" in update_data: - new_type = update_data["type"] - if new_type == "representation": - new_update_data["active"] = True - elif new_type == "archived_representation": - new_update_data["active"] = False - - if "parent" in update_data: - new_update_data["versionId"] = update_data["parent"] - - if "context" in update_data: - context = update_data["context"] - if "folder" not in context and "asset" in context: - context["folder"] = {"name": context.pop("asset")} - - if "family" in context or "subset" in context: - context["product"] = { - "name": context.pop("subset"), - "type": context.pop("family"), - } - new_data["context"] = context - - if "files" in update_data: - new_files = update_data["files"] - if isinstance(new_files, dict): - new_files = list(new_files.values()) - - for item in new_files: - for key in tuple(item.keys()): - if key not in ("hash", "path", "size"): - item.pop(key) - item.update({ - "id": create_entity_id(), - "name": os.path.basename(item["path"]), - "hash_type": "op3", - }) - new_update_data["files"] = new_files - - flat_data = _to_flat_dict(new_update_data) - if attribs: - flat_data["attrib"] = attribs - - if new_data: - print("Representation has new data: {}".format(new_data)) - flat_data["data"] = new_data - - return flat_data - - -def convert_update_workfile_info_to_v4( - project_name, workfile_id, update_data, con -): - return { - key: value - for key, value in update_data.items() - if key.startswith("data") - } diff --git a/client/ayon_core/client/entities.py b/client/ayon_core/client/entities.py deleted file mode 100644 index d0acf535e8..0000000000 --- a/client/ayon_core/client/entities.py +++ /dev/null @@ -1,702 +0,0 @@ -import collections - -from .constants import CURRENT_THUMBNAIL_SCHEMA -from .utils import get_ayon_server_api_connection -from .openpype_comp import get_folders_with_tasks -from .conversion_utils import ( - folder_fields_v3_to_v4, - convert_v4_folder_to_v3, - - subset_fields_v3_to_v4, - convert_v4_subset_to_v3, - - version_fields_v3_to_v4, - convert_v4_version_to_v3, - - representation_fields_v3_to_v4, - convert_v4_representation_to_v3, - - workfile_info_fields_v3_to_v4, - convert_v4_workfile_info_to_v3, -) - - -def get_asset_name_identifier(asset_doc): - """Get asset name identifier by asset document. - - This function is added because of AYON implementation where name - identifier is not just a name but full path. - - Asset document must have "name" key, and "data.parents" when in AYON mode. - - Args: - asset_doc (dict[str, Any]): Asset document. - """ - - parents = list(asset_doc["data"]["parents"]) - parents.append(asset_doc["name"]) - return "/" + "/".join(parents) - - -def _get_subsets( - project_name, - subset_ids=None, - subset_names=None, - folder_ids=None, - names_by_folder_ids=None, - archived=False, - fields=None -): - # Convert fields and add minimum required fields - con = get_ayon_server_api_connection() - fields = subset_fields_v3_to_v4(fields, con) - if fields is not None: - for key in ( - "id", - "active" - ): - fields.add(key) - - active = True - if archived: - active = None - - for subset in con.get_products( - project_name, - product_ids=subset_ids, - product_names=subset_names, - folder_ids=folder_ids, - names_by_folder_ids=names_by_folder_ids, - active=active, - fields=fields, - ): - yield convert_v4_subset_to_v3(subset) - - -def _get_versions( - project_name, - version_ids=None, - subset_ids=None, - versions=None, - hero=True, - standard=True, - latest=None, - active=None, - fields=None -): - con = get_ayon_server_api_connection() - - fields = version_fields_v3_to_v4(fields, con) - - # Make sure 'productId' and 'version' are available when hero versions - # are queried - if fields and hero: - fields = set(fields) - fields |= {"productId", "version"} - - queried_versions = con.get_versions( - project_name, - version_ids=version_ids, - product_ids=subset_ids, - versions=versions, - hero=hero, - standard=standard, - latest=latest, - active=active, - fields=fields - ) - - version_entities = [] - hero_versions = [] - for version in queried_versions: - if version["version"] < 0: - hero_versions.append(version) - else: - version_entities.append(convert_v4_version_to_v3(version)) - - if hero_versions: - subset_ids = set() - versions_nums = set() - for hero_version in hero_versions: - versions_nums.add(abs(hero_version["version"])) - subset_ids.add(hero_version["productId"]) - - hero_eq_versions = con.get_versions( - project_name, - product_ids=subset_ids, - versions=versions_nums, - hero=False, - fields=["id", "version", "productId"] - ) - hero_eq_by_subset_id = collections.defaultdict(list) - for version in hero_eq_versions: - hero_eq_by_subset_id[version["productId"]].append(version) - - for hero_version in hero_versions: - abs_version = abs(hero_version["version"]) - subset_id = hero_version["productId"] - version_id = None - for version in hero_eq_by_subset_id.get(subset_id, []): - if version["version"] == abs_version: - version_id = version["id"] - break - conv_hero = convert_v4_version_to_v3(hero_version) - conv_hero["version_id"] = version_id - version_entities.append(conv_hero) - - return version_entities - - -def get_asset_by_id(project_name, asset_id, fields=None): - assets = get_assets( - project_name, asset_ids=[asset_id], fields=fields - ) - for asset in assets: - return asset - return None - - -def get_asset_by_name(project_name, asset_name, fields=None): - assets = get_assets( - project_name, asset_names=[asset_name], fields=fields - ) - for asset in assets: - return asset - return None - - -def _folders_query(project_name, con, fields, **kwargs): - if fields is None or "tasks" in fields: - folders = get_folders_with_tasks( - con, project_name, fields=fields, **kwargs - ) - - else: - folders = con.get_folders(project_name, fields=fields, **kwargs) - - for folder in folders: - yield folder - - -def get_assets( - project_name, - asset_ids=None, - asset_names=None, - parent_ids=None, - archived=False, - fields=None -): - if not project_name: - return - - active = True - if archived: - active = None - - con = get_ayon_server_api_connection() - fields = folder_fields_v3_to_v4(fields, con) - kwargs = dict( - folder_ids=asset_ids, - parent_ids=parent_ids, - active=active, - ) - if not asset_names: - for folder in _folders_query(project_name, con, fields, **kwargs): - yield convert_v4_folder_to_v3(folder, project_name) - return - - new_asset_names = set() - folder_paths = set() - for name in asset_names: - if "/" in name: - folder_paths.add(name) - else: - new_asset_names.add(name) - - yielded_ids = set() - if folder_paths: - for folder in _folders_query( - project_name, con, fields, folder_paths=folder_paths, **kwargs - ): - yielded_ids.add(folder["id"]) - yield convert_v4_folder_to_v3(folder, project_name) - - if not new_asset_names: - return - - for folder in _folders_query( - project_name, con, fields, folder_names=new_asset_names, **kwargs - ): - if folder["id"] not in yielded_ids: - yielded_ids.add(folder["id"]) - yield convert_v4_folder_to_v3(folder, project_name) - - -def get_archived_assets( - project_name, - asset_ids=None, - asset_names=None, - parent_ids=None, - fields=None -): - return get_assets( - project_name, - asset_ids, - asset_names, - parent_ids, - True, - fields - ) - - -def get_asset_ids_with_subsets(project_name, asset_ids=None): - con = get_ayon_server_api_connection() - return con.get_folder_ids_with_products(project_name, asset_ids) - - -def get_subset_by_id(project_name, subset_id, fields=None): - subsets = get_subsets( - project_name, subset_ids=[subset_id], fields=fields - ) - for subset in subsets: - return subset - return None - - -def get_subset_by_name(project_name, subset_name, asset_id, fields=None): - subsets = get_subsets( - project_name, - subset_names=[subset_name], - asset_ids=[asset_id], - fields=fields - ) - for subset in subsets: - return subset - return None - - -def get_subsets( - project_name, - subset_ids=None, - subset_names=None, - asset_ids=None, - names_by_asset_ids=None, - archived=False, - fields=None -): - return _get_subsets( - project_name, - subset_ids, - subset_names, - asset_ids, - names_by_asset_ids, - archived, - fields=fields - ) - - -def get_subset_families(project_name, subset_ids=None): - con = get_ayon_server_api_connection() - return con.get_product_type_names(project_name, subset_ids) - - -def get_version_by_id(project_name, version_id, fields=None): - versions = get_versions( - project_name, - version_ids=[version_id], - fields=fields, - hero=True - ) - for version in versions: - return version - return None - - -def get_version_by_name(project_name, version, subset_id, fields=None): - versions = get_versions( - project_name, - subset_ids=[subset_id], - versions=[version], - fields=fields - ) - for version in versions: - return version - return None - - -def get_versions( - project_name, - version_ids=None, - subset_ids=None, - versions=None, - hero=False, - fields=None -): - return _get_versions( - project_name, - version_ids, - subset_ids, - versions, - hero=hero, - standard=True, - fields=fields - ) - - -def get_hero_version_by_id(project_name, version_id, fields=None): - versions = get_hero_versions( - project_name, - version_ids=[version_id], - fields=fields - ) - for version in versions: - return version - return None - - -def get_hero_version_by_subset_id( - project_name, subset_id, fields=None -): - versions = get_hero_versions( - project_name, - subset_ids=[subset_id], - fields=fields - ) - for version in versions: - return version - return None - - -def get_hero_versions( - project_name, subset_ids=None, version_ids=None, fields=None -): - return _get_versions( - project_name, - version_ids=version_ids, - subset_ids=subset_ids, - hero=True, - standard=False, - fields=fields - ) - - -def get_last_versions(project_name, subset_ids, active=None, fields=None): - if fields: - fields = set(fields) - fields.add("parent") - - versions = _get_versions( - project_name, - subset_ids=subset_ids, - latest=True, - hero=False, - active=active, - fields=fields - ) - return { - version["parent"]: version - for version in versions - } - - -def get_last_version_by_subset_id(project_name, subset_id, fields=None): - versions = _get_versions( - project_name, - subset_ids=[subset_id], - latest=True, - hero=False, - fields=fields - ) - if not versions: - return None - return versions[0] - - -def get_last_version_by_subset_name( - project_name, - subset_name, - asset_id=None, - asset_name=None, - fields=None -): - if not asset_id and not asset_name: - return None - - if not asset_id: - asset = get_asset_by_name( - project_name, asset_name, fields=["_id"] - ) - if not asset: - return None - asset_id = asset["_id"] - - subset = get_subset_by_name( - project_name, subset_name, asset_id, fields=["_id"] - ) - if not subset: - return None - return get_last_version_by_subset_id( - project_name, subset["_id"], fields=fields - ) - - -def get_output_link_versions(project_name, version_id, fields=None): - if not version_id: - return [] - - con = get_ayon_server_api_connection() - version_links = con.get_version_links( - project_name, version_id, link_direction="out") - - version_ids = { - link["entityId"] - for link in version_links - if link["entityType"] == "version" - } - if not version_ids: - return [] - - return get_versions(project_name, version_ids=version_ids, fields=fields) - - -def version_is_latest(project_name, version_id): - con = get_ayon_server_api_connection() - return con.version_is_latest(project_name, version_id) - - -def get_representation_by_id(project_name, representation_id, fields=None): - representations = get_representations( - project_name, - representation_ids=[representation_id], - fields=fields - ) - for representation in representations: - return representation - return None - - -def get_representation_by_name( - project_name, representation_name, version_id, fields=None -): - representations = get_representations( - project_name, - representation_names=[representation_name], - version_ids=[version_id], - fields=fields - ) - for representation in representations: - return representation - return None - - -def get_representations( - project_name, - representation_ids=None, - representation_names=None, - version_ids=None, - context_filters=None, - names_by_version_ids=None, - archived=False, - standard=True, - fields=None -): - if context_filters is not None: - # TODO should we add the support? - # - there was ability to fitler using regex - raise ValueError("OP v4 can't filter by representation context.") - - if not archived and not standard: - return - - if archived and not standard: - active = False - elif not archived and standard: - active = True - else: - active = None - - con = get_ayon_server_api_connection() - fields = representation_fields_v3_to_v4(fields, con) - if fields and active is not None: - fields.add("active") - - representations = con.get_representations( - project_name, - representation_ids=representation_ids, - representation_names=representation_names, - version_ids=version_ids, - names_by_version_ids=names_by_version_ids, - active=active, - fields=fields - ) - for representation in representations: - yield convert_v4_representation_to_v3(representation) - - -def get_representation_parents(project_name, representation): - if not representation: - return None - - repre_id = representation["_id"] - parents_by_repre_id = get_representations_parents( - project_name, [representation] - ) - return parents_by_repre_id[repre_id] - - -def get_representations_parents(project_name, representations): - repre_ids = { - repre["_id"] - for repre in representations - } - con = get_ayon_server_api_connection() - parents_by_repre_id = con.get_representations_parents( - project_name, repre_ids - ) - - new_parents = {} - for repre_id, parents in parents_by_repre_id.items(): - version, subset, folder, project = parents - new_parents[repre_id] = ( - version, - subset, - folder, - project - ) - return new_parents - - -def get_archived_representations( - project_name, - representation_ids=None, - representation_names=None, - version_ids=None, - context_filters=None, - names_by_version_ids=None, - fields=None -): - return get_representations( - project_name, - representation_ids=representation_ids, - representation_names=representation_names, - version_ids=version_ids, - context_filters=context_filters, - names_by_version_ids=names_by_version_ids, - archived=True, - standard=False, - fields=fields - ) - - -def get_thumbnail( - project_name, thumbnail_id, entity_type, entity_id, fields=None -): - """Receive thumbnail entity data. - - Args: - project_name (str): Name of project where to look for queried entities. - thumbnail_id (Union[str, ObjectId]): Id of thumbnail entity. - entity_type (str): Type of entity for which the thumbnail should be - received. - entity_id (str): Id of entity for which the thumbnail should be - received. - fields (Iterable[str]): Fields that should be returned. All fields are - returned if 'None' is passed. - - Returns: - None: If thumbnail with specified id was not found. - Dict: Thumbnail entity data which can be reduced to specified 'fields'. - """ - - if not thumbnail_id or not entity_type or not entity_id: - return None - - if entity_type == "asset": - entity_type = "folder" - - elif entity_type == "hero_version": - entity_type = "version" - - return { - "_id": thumbnail_id, - "type": "thumbnail", - "schema": CURRENT_THUMBNAIL_SCHEMA, - "data": { - "entity_type": entity_type, - "entity_id": entity_id - } - } - - -def get_thumbnails(project_name, thumbnail_contexts, fields=None): - """Get thumbnail entities. - - Warning: - This function is not OpenPype compatible. There is none usage of this - function in codebase so there is nothing to convert. The previous - implementation cannot be AYON compatible without entity types. - """ - - thumbnail_items = set() - for thumbnail_context in thumbnail_contexts: - thumbnail_id, entity_type, entity_id = thumbnail_context - thumbnail_item = get_thumbnail( - project_name, thumbnail_id, entity_type, entity_id - ) - if thumbnail_item: - thumbnail_items.add(thumbnail_item) - return list(thumbnail_items) - - -def get_thumbnail_id_from_source(project_name, src_type, src_id): - """Receive thumbnail id from source entity. - - Args: - project_name (str): Name of project where to look for queried entities. - src_type (str): Type of source entity ('asset', 'version'). - src_id (Union[str, ObjectId]): Id of source entity. - - Returns: - ObjectId: Thumbnail id assigned to entity. - None: If Source entity does not have any thumbnail id assigned. - """ - - if not src_type or not src_id: - return None - - if src_type == "version": - version = get_version_by_id( - project_name, src_id, fields=["data.thumbnail_id"] - ) or {} - return version.get("data", {}).get("thumbnail_id") - - if src_type == "asset": - asset = get_asset_by_id( - project_name, src_id, fields=["data.thumbnail_id"] - ) or {} - return asset.get("data", {}).get("thumbnail_id") - - return None - - -def get_workfile_info( - project_name, asset_id, task_name, filename, fields=None -): - if not asset_id or not task_name or not filename: - return None - - con = get_ayon_server_api_connection() - task = con.get_task_by_name( - project_name, asset_id, task_name, fields=["id", "name", "folderId"] - ) - if not task: - return None - - fields = workfile_info_fields_v3_to_v4(fields) - - for workfile_info in con.get_workfiles_info( - project_name, task_ids=[task["id"]], fields=fields - ): - if workfile_info["name"] == filename: - return convert_v4_workfile_info_to_v3(workfile_info, task) - return None diff --git a/client/ayon_core/client/entity_links.py b/client/ayon_core/client/entity_links.py deleted file mode 100644 index 7fb9fbde6f..0000000000 --- a/client/ayon_core/client/entity_links.py +++ /dev/null @@ -1,157 +0,0 @@ -from .utils import get_ayon_server_api_connection -from .entities import get_assets, get_representation_by_id - - -def get_linked_asset_ids(project_name, asset_doc=None, asset_id=None): - """Extract linked asset ids from asset document. - - One of asset document or asset id must be passed. - - Note: - Asset links now works only from asset to assets. - - Args: - project_name (str): Project where to look for asset. - asset_doc (dict): Asset document from DB. - asset_id (str): Asset id to find its document. - - Returns: - List[Union[ObjectId, str]]: Asset ids of input links. - """ - - output = [] - if not asset_doc and not asset_id: - return output - - if not asset_id: - asset_id = asset_doc["_id"] - - con = get_ayon_server_api_connection() - links = con.get_folder_links(project_name, asset_id, link_direction="in") - return [ - link["entityId"] - for link in links - if link["entityType"] == "folder" - ] - - -def get_linked_assets( - project_name, asset_doc=None, asset_id=None, fields=None -): - """Return linked assets based on passed asset document. - - One of asset document or asset id must be passed. - - Args: - project_name (str): Name of project where to look for queried entities. - asset_doc (Dict[str, Any]): Asset document from database. - asset_id (Union[ObjectId, str]): Asset id. Can be used instead of - asset document. - fields (Iterable[str]): Fields that should be returned. All fields are - returned if 'None' is passed. - - Returns: - List[Dict[str, Any]]: Asset documents of input links for passed - asset doc. - """ - - link_ids = get_linked_asset_ids(project_name, asset_doc, asset_id) - if not link_ids: - return [] - return list(get_assets(project_name, asset_ids=link_ids, fields=fields)) - - - -def get_linked_representation_id( - project_name, repre_doc=None, repre_id=None, link_type=None, max_depth=None -): - """Returns list of linked ids of particular type (if provided). - - One of representation document or representation id must be passed. - Note: - Representation links now works only from representation through version - back to representations. - - Todos: - Missing depth query. Not sure how it did find more representations in - depth, probably links to version? - - Args: - project_name (str): Name of project where look for links. - repre_doc (Dict[str, Any]): Representation document. - repre_id (Union[ObjectId, str]): Representation id. - link_type (str): Type of link (e.g. 'reference', ...). - max_depth (int): Limit recursion level. Default: 0 - - Returns: - List[ObjectId] Linked representation ids. - """ - - if repre_doc: - repre_id = repre_doc["_id"] - - if not repre_id and not repre_doc: - return [] - - version_id = None - if repre_doc: - version_id = repre_doc.get("parent") - - if not version_id: - repre_doc = get_representation_by_id( - project_name, repre_id, fields=["parent"] - ) - if repre_doc: - version_id = repre_doc["parent"] - - if not version_id: - return [] - - if max_depth is None or max_depth == 0: - max_depth = 1 - - link_types = None - if link_type: - link_types = [link_type] - - con = get_ayon_server_api_connection() - # Store already found version ids to avoid recursion, and also to store - # output -> Don't forget to remove 'version_id' at the end!!! - linked_version_ids = {version_id} - # Each loop of depth will reset this variable - versions_to_check = {version_id} - for _ in range(max_depth): - if not versions_to_check: - break - - versions_links = con.get_versions_links( - project_name, - versions_to_check, - link_types=link_types, - link_direction="out") - - versions_to_check = set() - for links in versions_links.values(): - for link in links: - # Care only about version links - if link["entityType"] != "version": - continue - entity_id = link["entityId"] - # Skip already found linked version ids - if entity_id in linked_version_ids: - continue - linked_version_ids.add(entity_id) - versions_to_check.add(entity_id) - - linked_version_ids.remove(version_id) - if not linked_version_ids: - return [] - con = get_ayon_server_api_connection() - representations = con.get_representations( - project_name, - version_ids=linked_version_ids, - fields=["id"]) - return [ - repre["id"] - for repre in representations - ] diff --git a/client/ayon_core/client/notes.md b/client/ayon_core/client/notes.md deleted file mode 100644 index 59743892eb..0000000000 --- a/client/ayon_core/client/notes.md +++ /dev/null @@ -1,39 +0,0 @@ -# Client functionality -## Reason -Preparation for OpenPype v4 server. Goal is to remove direct mongo calls in code to prepare a little bit for different source of data for code before. To start think about database calls less as mongo calls but more universally. To do so was implemented simple wrapper around database calls to not use pymongo specific code. - -Current goal is not to make universal database model which can be easily replaced with any different source of data but to make it close as possible. Current implementation of OpenPype is too tightly connected to pymongo and it's abilities so we're trying to get closer with long term changes that can be used even in current state. - -## Queries -Query functions don't use full potential of mongo queries like very specific queries based on subdictionaries or unknown structures. We try to avoid these calls as much as possible because they'll probably won't be available in future. If it's really necessary a new function can be added but only if it's reasonable for overall logic. All query functions were moved to `~/client/entities.py`. Each function has arguments with available filters and possible reduce of returned keys for each entity. - -## Changes -Changes are a little bit complicated. Mongo has many options how update can happen which had to be reduced also it would be at this stage complicated to validate values which are created or updated thus automation is at this point almost none. Changes can be made using operations available in `~/client/operations.py`. Each operation require project name and entity type, but may require operation specific data. - -### Create -Create operations expect already prepared document data, for that are prepared functions creating skeletal structures of documents (do not fill all required data), except `_id` all data should be right. Existence of entity is not validated so if the same creation operation is send n times it will create the entity n times which can cause issues. - -### Update -Update operation require entity id and keys that should be changed, update dictionary must have {"key": value}. If value should be set in nested dictionary the key must have also all subkeys joined with dot `.` (e.g. `{"data": {"fps": 25}}` -> `{"data.fps": 25}`). To simplify update dictionaries were prepared functions which does that for you, their name has template `prepare__update_data` - they work on comparison of previous document and new document. If there is missing function for requested entity type it is because we didn't need it yet and require implementation. - -### Delete -Delete operation need entity id. Entity will be deleted from mongo. - - -## What (probably) won't be replaced -Some parts of code are still using direct mongo calls. In most of cases it is for very specific calls that are module specific or their usage will completely change in future. -- Mongo calls that are not project specific (out of `avalon` collection) will be removed or will have to use different mechanism how the data are stored. At this moment it is related to OpenPype settings and logs, ftrack server events, some other data. -- Sync server queries. They're complex and very specific for sync server module. Their replacement will require specific calls to OpenPype server in v4 thus their abstraction with wrapper is irrelevant and would complicate production in v3. -- Project managers (ftrack, kitsu, shotgrid, embedded Project Manager, etc.). Project managers are creating, updating or removing assets in v3, but in v4 will create folders with different structure. Wrapping creation of assets would not help to prepare for v4 because of new data structures. The same can be said about editorial Extract Hierarchy Avalon plugin which create project structure. -- Code parts that is marked as deprecated in v3 or will be deprecated in v4. - - integrate asset legacy publish plugin - already is legacy kept for safety - - integrate thumbnail - thumbnails will be stored in different way in v4 - - input links - link will be stored in different way and will have different mechanism of linking. In v3 are links limited to same entity type "asset <-> asset" or "representation <-> representation". - -## Known missing replacements -- change subset group in loader tool -- integrate subset group -- query input links in openpype lib -- create project in openpype lib -- save/create workfile doc in openpype lib -- integrate hero version diff --git a/client/ayon_core/client/openpype_comp.py b/client/ayon_core/client/openpype_comp.py deleted file mode 100644 index 71a141e913..0000000000 --- a/client/ayon_core/client/openpype_comp.py +++ /dev/null @@ -1,159 +0,0 @@ -import collections -import json - -import six -from ayon_api.graphql import GraphQlQuery, FIELD_VALUE, fields_to_dict - -from .constants import DEFAULT_FOLDER_FIELDS - - -def folders_tasks_graphql_query(fields): - query = GraphQlQuery("FoldersQuery") - project_name_var = query.add_variable("projectName", "String!") - folder_ids_var = query.add_variable("folderIds", "[String!]") - parent_folder_ids_var = query.add_variable("parentFolderIds", "[String!]") - folder_paths_var = query.add_variable("folderPaths", "[String!]") - folder_names_var = query.add_variable("folderNames", "[String!]") - has_products_var = query.add_variable("folderHasProducts", "Boolean!") - - project_field = query.add_field("project") - project_field.set_filter("name", project_name_var) - - folders_field = project_field.add_field_with_edges("folders") - folders_field.set_filter("ids", folder_ids_var) - folders_field.set_filter("parentIds", parent_folder_ids_var) - folders_field.set_filter("names", folder_names_var) - folders_field.set_filter("paths", folder_paths_var) - folders_field.set_filter("hasProducts", has_products_var) - - fields = set(fields) - fields.discard("tasks") - tasks_field = folders_field.add_field_with_edges("tasks") - tasks_field.add_field("name") - tasks_field.add_field("taskType") - - nested_fields = fields_to_dict(fields) - - query_queue = collections.deque() - for key, value in nested_fields.items(): - query_queue.append((key, value, folders_field)) - - while query_queue: - item = query_queue.popleft() - key, value, parent = item - field = parent.add_field(key) - if value is FIELD_VALUE: - continue - - for k, v in value.items(): - query_queue.append((k, v, field)) - return query - - -def get_folders_with_tasks( - con, - project_name, - folder_ids=None, - folder_paths=None, - folder_names=None, - parent_ids=None, - active=True, - fields=None -): - """Query folders with tasks from server. - - This is for v4 compatibility where tasks were stored on assets. This is - an inefficient way how folders and tasks are queried so it was added only - as compatibility function. - - Todos: - Folder name won't be unique identifier, so we should add folder path - filtering. - - Notes: - Filter 'active' don't have direct filter in GraphQl. - - Args: - con (ServerAPI): Connection to server. - project_name (str): Name of project where folders are. - folder_ids (Iterable[str]): Folder ids to filter. - folder_paths (Iterable[str]): Folder paths used for filtering. - folder_names (Iterable[str]): Folder names used for filtering. - parent_ids (Iterable[str]): Ids of folder parents. Use 'None' - if folder is direct child of project. - active (Union[bool, None]): Filter active/inactive folders. Both - are returned if is set to None. - fields (Union[Iterable(str), None]): Fields to be queried - for folder. All possible folder fields are returned if 'None' - is passed. - - Yields: - Dict[str, Any]: Queried folder entities. - """ - - if not project_name: - return - - filters = { - "projectName": project_name - } - if folder_ids is not None: - folder_ids = set(folder_ids) - if not folder_ids: - return - filters["folderIds"] = list(folder_ids) - - if folder_paths is not None: - folder_paths = set(folder_paths) - if not folder_paths: - return - filters["folderPaths"] = list(folder_paths) - - if folder_names is not None: - folder_names = set(folder_names) - if not folder_names: - return - filters["folderNames"] = list(folder_names) - - if parent_ids is not None: - parent_ids = set(parent_ids) - if not parent_ids: - return - if None in parent_ids: - # Replace 'None' with '"root"' which is used during GraphQl - # query for parent ids filter for folders without folder - # parent - parent_ids.remove(None) - parent_ids.add("root") - - if project_name in parent_ids: - # Replace project name with '"root"' which is used during - # GraphQl query for parent ids filter for folders without - # folder parent - parent_ids.remove(project_name) - parent_ids.add("root") - - filters["parentFolderIds"] = list(parent_ids) - - if fields: - fields = set(fields) - else: - fields = con.get_default_fields_for_type("folder") - fields |= DEFAULT_FOLDER_FIELDS - - if active is not None: - fields.add("active") - - query = folders_tasks_graphql_query(fields) - for attr, filter_value in filters.items(): - query.set_variable_value(attr, filter_value) - - parsed_data = query.query(con) - folders = parsed_data["project"]["folders"] - for folder in folders: - if active is not None and folder["active"] is not active: - continue - folder_data = folder.get("data") - if isinstance(folder_data, six.string_types): - folder["data"] = json.loads(folder_data) - yield folder diff --git a/client/ayon_core/client/operations.py b/client/ayon_core/client/operations.py deleted file mode 100644 index f05652fb50..0000000000 --- a/client/ayon_core/client/operations.py +++ /dev/null @@ -1,838 +0,0 @@ -import copy -import json -import collections -import uuid -import datetime - -from .constants import ( - CURRENT_ASSET_DOC_SCHEMA, - CURRENT_SUBSET_SCHEMA, - CURRENT_VERSION_SCHEMA, - CURRENT_HERO_VERSION_SCHEMA, - CURRENT_REPRESENTATION_SCHEMA, - CURRENT_WORKFILE_INFO_SCHEMA, - CURRENT_THUMBNAIL_SCHEMA, -) -from .operations_base import ( - REMOVED_VALUE, - CreateOperation, - UpdateOperation, - DeleteOperation, - BaseOperationsSession -) -from .conversion_utils import ( - convert_create_asset_to_v4, - convert_create_task_to_v4, - convert_create_subset_to_v4, - convert_create_version_to_v4, - convert_create_hero_version_to_v4, - convert_create_representation_to_v4, - convert_create_workfile_info_to_v4, - - convert_update_folder_to_v4, - convert_update_subset_to_v4, - convert_update_version_to_v4, - convert_update_hero_version_to_v4, - convert_update_representation_to_v4, - convert_update_workfile_info_to_v4, -) -from .utils import create_entity_id, get_ayon_server_api_connection - - -def _create_or_convert_to_id(entity_id=None): - if entity_id is None: - return create_entity_id() - - # Validate if can be converted to uuid - uuid.UUID(entity_id) - return entity_id - - -def new_asset_document( - name, project_id, parent_id, parents, data=None, entity_id=None -): - """Create skeleton data of asset document. - - Args: - name (str): Is considered as unique identifier of asset in project. - project_id (Union[str, ObjectId]): Id of project doument. - parent_id (Union[str, ObjectId]): Id of parent asset. - parents (List[str]): List of parent assets names. - data (Dict[str, Any]): Asset document data. Empty dictionary is used - if not passed. Value of 'parent_id' is used to fill 'visualParent'. - entity_id (Union[str, ObjectId]): Predefined id of document. New id is - created if not passed. - - Returns: - Dict[str, Any]: Skeleton of asset document. - """ - - if data is None: - data = {} - if parent_id is not None: - parent_id = _create_or_convert_to_id(parent_id) - data["visualParent"] = parent_id - data["parents"] = parents - - return { - "_id": _create_or_convert_to_id(entity_id), - "type": "asset", - "name": name, - # This will be ignored - "parent": project_id, - "data": data, - "schema": CURRENT_ASSET_DOC_SCHEMA - } - - -def new_subset_document(name, family, asset_id, data=None, entity_id=None): - """Create skeleton data of subset document. - - Args: - name (str): Is considered as unique identifier of subset under asset. - family (str): Subset's family. - asset_id (Union[str, ObjectId]): Id of parent asset. - data (Dict[str, Any]): Subset document data. Empty dictionary is used - if not passed. Value of 'family' is used to fill 'family'. - entity_id (Union[str, ObjectId]): Predefined id of document. New id is - created if not passed. - - Returns: - Dict[str, Any]: Skeleton of subset document. - """ - - if data is None: - data = {} - data["family"] = family - return { - "_id": _create_or_convert_to_id(entity_id), - "schema": CURRENT_SUBSET_SCHEMA, - "type": "subset", - "name": name, - "data": data, - "parent": _create_or_convert_to_id(asset_id) - } - - -def new_version_doc(version, subset_id, data=None, entity_id=None): - """Create skeleton data of version document. - - Args: - version (int): Is considered as unique identifier of version - under subset. - subset_id (Union[str, ObjectId]): Id of parent subset. - data (Dict[str, Any]): Version document data. - entity_id (Union[str, ObjectId]): Predefined id of document. New id is - created if not passed. - - Returns: - Dict[str, Any]: Skeleton of version document. - """ - - if data is None: - data = {} - - return { - "_id": _create_or_convert_to_id(entity_id), - "schema": CURRENT_VERSION_SCHEMA, - "type": "version", - "name": int(version), - "parent": _create_or_convert_to_id(subset_id), - "data": data - } - - -def new_hero_version_doc(subset_id, data, version=None, entity_id=None): - """Create skeleton data of hero version document. - - Args: - subset_id (Union[str, ObjectId]): Id of parent subset. - data (Dict[str, Any]): Version document data. - version (int): Version of source version. - entity_id (Union[str, ObjectId]): Predefined id of document. New id is - created if not passed. - - Returns: - Dict[str, Any]: Skeleton of version document. - """ - - if version is None: - version = -1 - elif version > 0: - version = -version - - return { - "_id": _create_or_convert_to_id(entity_id), - "schema": CURRENT_HERO_VERSION_SCHEMA, - "type": "hero_version", - "version": version, - "parent": _create_or_convert_to_id(subset_id), - "data": data - } - - -def new_representation_doc( - name, version_id, context, data=None, entity_id=None -): - """Create skeleton data of representation document. - - Args: - name (str): Representation name considered as unique identifier - of representation under version. - version_id (Union[str, ObjectId]): Id of parent version. - context (Dict[str, Any]): Representation context used for fill template - of to query. - data (Dict[str, Any]): Representation document data. - entity_id (Union[str, ObjectId]): Predefined id of document. New id is - created if not passed. - - Returns: - Dict[str, Any]: Skeleton of version document. - """ - - if data is None: - data = {} - - return { - "_id": _create_or_convert_to_id(entity_id), - "schema": CURRENT_REPRESENTATION_SCHEMA, - "type": "representation", - "parent": _create_or_convert_to_id(version_id), - "name": name, - "data": data, - - # Imprint shortcut to context for performance reasons. - "context": context - } - - -def new_thumbnail_doc(data=None, entity_id=None): - """Create skeleton data of thumbnail document. - - Args: - data (Dict[str, Any]): Thumbnail document data. - entity_id (Union[str, ObjectId]): Predefined id of document. New id is - created if not passed. - - Returns: - Dict[str, Any]: Skeleton of thumbnail document. - """ - - if data is None: - data = {} - - return { - "_id": _create_or_convert_to_id(entity_id), - "type": "thumbnail", - "schema": CURRENT_THUMBNAIL_SCHEMA, - "data": data - } - - -def new_workfile_info_doc( - filename, asset_id, task_name, files, data=None, entity_id=None -): - """Create skeleton data of workfile info document. - - Workfile document is at this moment used primarily for artist notes. - - Args: - filename (str): Filename of workfile. - asset_id (Union[str, ObjectId]): Id of asset under which workfile live. - task_name (str): Task under which was workfile created. - files (List[str]): List of rootless filepaths related to workfile. - data (Dict[str, Any]): Additional metadata. - - Returns: - Dict[str, Any]: Skeleton of workfile info document. - """ - - if not data: - data = {} - - return { - "_id": _create_or_convert_to_id(entity_id), - "type": "workfile", - "parent": _create_or_convert_to_id(asset_id), - "task_name": task_name, - "filename": filename, - "data": data, - "files": files - } - - -def _prepare_update_data(old_doc, new_doc, replace): - changes = {} - for key, value in new_doc.items(): - if key not in old_doc or value != old_doc[key]: - changes[key] = value - - if replace: - for key in old_doc.keys(): - if key not in new_doc: - changes[key] = REMOVED_VALUE - return changes - - -def prepare_subset_update_data(old_doc, new_doc, replace=True): - """Compare two subset documents and prepare update data. - - Based on compared values will create update data for - 'MongoUpdateOperation'. - - Empty output means that documents are identical. - - Returns: - Dict[str, Any]: Changes between old and new document. - """ - - return _prepare_update_data(old_doc, new_doc, replace) - - -def prepare_version_update_data(old_doc, new_doc, replace=True): - """Compare two version documents and prepare update data. - - Based on compared values will create update data for - 'MongoUpdateOperation'. - - Empty output means that documents are identical. - - Returns: - Dict[str, Any]: Changes between old and new document. - """ - - return _prepare_update_data(old_doc, new_doc, replace) - - -def prepare_hero_version_update_data(old_doc, new_doc, replace=True): - """Compare two hero version documents and prepare update data. - - Based on compared values will create update data for 'UpdateOperation'. - - Empty output means that documents are identical. - - Returns: - Dict[str, Any]: Changes between old and new document. - """ - - changes = _prepare_update_data(old_doc, new_doc, replace) - changes.pop("version_id", None) - return changes - - -def prepare_representation_update_data(old_doc, new_doc, replace=True): - """Compare two representation documents and prepare update data. - - Based on compared values will create update data for - 'MongoUpdateOperation'. - - Empty output means that documents are identical. - - Returns: - Dict[str, Any]: Changes between old and new document. - """ - - changes = _prepare_update_data(old_doc, new_doc, replace) - context = changes.get("data", {}).get("context") - # Make sure that both 'family' and 'subset' are in changes if - # one of them changed (they'll both become 'product'). - if ( - context - and ("family" in context or "subset" in context) - ): - context["family"] = new_doc["data"]["context"]["family"] - context["subset"] = new_doc["data"]["context"]["subset"] - - return changes - - -def prepare_workfile_info_update_data(old_doc, new_doc, replace=True): - """Compare two workfile info documents and prepare update data. - - Based on compared values will create update data for - 'MongoUpdateOperation'. - - Empty output means that documents are identical. - - Returns: - Dict[str, Any]: Changes between old and new document. - """ - - return _prepare_update_data(old_doc, new_doc, replace) - - -class FailedOperations(Exception): - pass - - -def entity_data_json_default(value): - if isinstance(value, datetime.datetime): - return int(value.timestamp()) - - raise TypeError( - "Object of type {} is not JSON serializable".format(str(type(value))) - ) - - -def failed_json_default(value): - return "< Failed value {} > {}".format(type(value), str(value)) - - -class ServerCreateOperation(CreateOperation): - """Operation to create an entity. - - Args: - project_name (str): On which project operation will happen. - entity_type (str): Type of entity on which change happens. - e.g. 'asset', 'representation' etc. - data (Dict[str, Any]): Data of entity that will be created. - """ - - def __init__(self, project_name, entity_type, data, session): - self._session = session - - if not data: - data = {} - data = copy.deepcopy(data) - if entity_type == "project": - raise ValueError("Project cannot be created using operations") - - tasks = None - if entity_type in "asset": - # TODO handle tasks - entity_type = "folder" - if "data" in data: - tasks = data["data"].get("tasks") - - project = self._session.get_project(project_name) - new_data = convert_create_asset_to_v4(data, project, self.con) - - elif entity_type == "task": - project = self._session.get_project(project_name) - new_data = convert_create_task_to_v4(data, project, self.con) - - elif entity_type == "subset": - new_data = convert_create_subset_to_v4(data, self.con) - entity_type = "product" - - elif entity_type == "version": - new_data = convert_create_version_to_v4(data, self.con) - - elif entity_type == "hero_version": - new_data = convert_create_hero_version_to_v4( - data, project_name, self.con - ) - entity_type = "version" - - elif entity_type in ("representation", "archived_representation"): - new_data = convert_create_representation_to_v4(data, self.con) - entity_type = "representation" - - elif entity_type == "workfile": - new_data = convert_create_workfile_info_to_v4( - data, project_name, self.con - ) - - else: - raise ValueError( - "Unhandled entity type \"{}\"".format(entity_type) - ) - - # Simple check if data can be dumped into json - # - should raise error on 'ObjectId' object - try: - new_data = json.loads( - json.dumps(new_data, default=entity_data_json_default) - ) - - except: - raise ValueError("Couldn't json parse body: {}".format( - json.dumps(new_data, default=failed_json_default) - )) - - super(ServerCreateOperation, self).__init__( - project_name, entity_type, new_data - ) - - if "id" not in self._data: - self._data["id"] = create_entity_id() - - if tasks: - copied_tasks = copy.deepcopy(tasks) - for task_name, task in copied_tasks.items(): - task["name"] = task_name - task["folderId"] = self._data["id"] - self.session.create_entity( - project_name, "task", task, nested_id=self.id - ) - - @property - def con(self): - return self.session.con - - @property - def session(self): - return self._session - - @property - def entity_id(self): - return self._data["id"] - - def to_server_operation(self): - return { - "id": self.id, - "type": "create", - "entityType": self.entity_type, - "entityId": self.entity_id, - "data": self._data - } - - -class ServerUpdateOperation(UpdateOperation): - """Operation to update an entity. - - Args: - project_name (str): On which project operation will happen. - entity_type (str): Type of entity on which change happens. - e.g. 'asset', 'representation' etc. - entity_id (Union[str, ObjectId]): Identifier of an entity. - update_data (Dict[str, Any]): Key -> value changes that will be set in - database. If value is set to 'REMOVED_VALUE' the key will be - removed. Only first level of dictionary is checked (on purpose). - """ - - def __init__( - self, project_name, entity_type, entity_id, update_data, session - ): - self._session = session - - update_data = copy.deepcopy(update_data) - if entity_type == "project": - raise ValueError("Project cannot be created using operations") - - if entity_type in ("asset", "archived_asset"): - new_update_data = convert_update_folder_to_v4( - project_name, entity_id, update_data, self.con - ) - entity_type = "folder" - - elif entity_type == "subset": - new_update_data = convert_update_subset_to_v4( - project_name, entity_id, update_data, self.con - ) - entity_type = "product" - - elif entity_type == "version": - new_update_data = convert_update_version_to_v4( - project_name, entity_id, update_data, self.con - ) - - elif entity_type == "hero_version": - new_update_data = convert_update_hero_version_to_v4( - project_name, entity_id, update_data, self.con - ) - entity_type = "version" - - elif entity_type in ("representation", "archived_representation"): - new_update_data = convert_update_representation_to_v4( - project_name, entity_id, update_data, self.con - ) - entity_type = "representation" - - elif entity_type == "workfile": - new_update_data = convert_update_workfile_info_to_v4( - project_name, entity_id, update_data, self.con - ) - - else: - raise ValueError( - "Unhandled entity type \"{}\"".format(entity_type) - ) - - try: - new_update_data = json.loads( - json.dumps(new_update_data, default=entity_data_json_default) - ) - - except: - raise ValueError("Couldn't json parse body: {}".format( - json.dumps(new_update_data, default=failed_json_default) - )) - - super(ServerUpdateOperation, self).__init__( - project_name, entity_type, entity_id, new_update_data - ) - - @property - def con(self): - return self.session.con - - @property - def session(self): - return self._session - - def to_server_operation(self): - if not self._update_data: - return None - - update_data = {} - for key, value in self._update_data.items(): - if value is REMOVED_VALUE: - value = None - update_data[key] = value - - return { - "id": self.id, - "type": "update", - "entityType": self.entity_type, - "entityId": self.entity_id, - "data": update_data - } - - -class ServerDeleteOperation(DeleteOperation): - """Operation to delete an entity. - - Args: - project_name (str): On which project operation will happen. - entity_type (str): Type of entity on which change happens. - e.g. 'asset', 'representation' etc. - entity_id (Union[str, ObjectId]): Entity id that will be removed. - """ - - def __init__(self, project_name, entity_type, entity_id, session): - self._session = session - - if entity_type == "asset": - entity_type = "folder" - - elif entity_type == "hero_version": - entity_type = "version" - - elif entity_type == "subset": - entity_type = "product" - - super(ServerDeleteOperation, self).__init__( - project_name, entity_type, entity_id - ) - - @property - def con(self): - return self.session.con - - @property - def session(self): - return self._session - - def to_server_operation(self): - return { - "id": self.id, - "type": self.operation_name, - "entityId": self.entity_id, - "entityType": self.entity_type, - } - - -class OperationsSession(BaseOperationsSession): - def __init__(self, con=None, *args, **kwargs): - super(OperationsSession, self).__init__(*args, **kwargs) - if con is None: - con = get_ayon_server_api_connection() - self._con = con - self._project_cache = {} - self._nested_operations = collections.defaultdict(list) - - @property - def con(self): - return self._con - - def get_project(self, project_name): - if project_name not in self._project_cache: - self._project_cache[project_name] = self.con.get_project( - project_name) - return copy.deepcopy(self._project_cache[project_name]) - - def commit(self): - """Commit session operations.""" - - operations, self._operations = self._operations, [] - if not operations: - return - - operations_by_project = collections.defaultdict(list) - for operation in operations: - operations_by_project[operation.project_name].append(operation) - - body_by_id = {} - results = [] - for project_name, operations in operations_by_project.items(): - operations_body = [] - for operation in operations: - body = operation.to_server_operation() - if body is not None: - try: - json.dumps(body) - except: - raise ValueError("Couldn't json parse body: {}".format( - json.dumps( - body, indent=4, default=failed_json_default - ) - )) - - body_by_id[operation.id] = body - operations_body.append(body) - - if operations_body: - result = self._con.post( - "projects/{}/operations".format(project_name), - operations=operations_body, - canFail=False - ) - results.append(result.data) - - for result in results: - if result.get("success"): - continue - - if "operations" not in result: - raise FailedOperations( - "Operation failed. Content: {}".format(str(result)) - ) - - for op_result in result["operations"]: - if not op_result["success"]: - operation_id = op_result["id"] - raise FailedOperations(( - "Operation \"{}\" failed with data:\n{}\nError: {}." - ).format( - operation_id, - json.dumps(body_by_id[operation_id], indent=4), - op_result.get("error", "unknown"), - )) - - def create_entity(self, project_name, entity_type, data, nested_id=None): - """Fast access to 'ServerCreateOperation'. - - Args: - project_name (str): On which project the creation happens. - entity_type (str): Which entity type will be created. - data (Dicst[str, Any]): Entity data. - nested_id (str): Id of other operation from which is triggered - operation -> Operations can trigger suboperations but they - must be added to operations list after it's parent is added. - - Returns: - ServerCreateOperation: Object of update operation. - """ - - operation = ServerCreateOperation( - project_name, entity_type, data, self - ) - - if nested_id: - self._nested_operations[nested_id].append(operation) - else: - self.add(operation) - if operation.id in self._nested_operations: - self.extend(self._nested_operations.pop(operation.id)) - - return operation - - def update_entity( - self, project_name, entity_type, entity_id, update_data, nested_id=None - ): - """Fast access to 'ServerUpdateOperation'. - - Returns: - ServerUpdateOperation: Object of update operation. - """ - - operation = ServerUpdateOperation( - project_name, entity_type, entity_id, update_data, self - ) - if nested_id: - self._nested_operations[nested_id].append(operation) - else: - self.add(operation) - if operation.id in self._nested_operations: - self.extend(self._nested_operations.pop(operation.id)) - return operation - - def delete_entity( - self, project_name, entity_type, entity_id, nested_id=None - ): - """Fast access to 'ServerDeleteOperation'. - - Returns: - ServerDeleteOperation: Object of delete operation. - """ - - operation = ServerDeleteOperation( - project_name, entity_type, entity_id, self - ) - if nested_id: - self._nested_operations[nested_id].append(operation) - else: - self.add(operation) - if operation.id in self._nested_operations: - self.extend(self._nested_operations.pop(operation.id)) - return operation - - -def create_project( - project_name, - project_code, - library_project=False, - preset_name=None, - con=None -): - """Create project using OpenPype settings. - - This project creation function is not validating project document on - creation. It is because project document is created blindly with only - minimum required information about project which is it's name, code, type - and schema. - - Entered project name must be unique and project must not exist yet. - - Note: - This function is here to be OP v4 ready but in v3 has more logic - to do. That's why inner imports are in the body. - - Args: - project_name (str): New project name. Should be unique. - project_code (str): Project's code should be unique too. - library_project (bool): Project is library project. - preset_name (str): Name of anatomy preset. Default is used if not - passed. - con (ServerAPI): Connection to server with logged user. - - Raises: - ValueError: When project name already exists in MongoDB. - - Returns: - dict: Created project document. - """ - - if con is None: - con = get_ayon_server_api_connection() - - return con.create_project( - project_name, - project_code, - library_project, - preset_name - ) - - -def delete_project(project_name, con=None): - if con is None: - con = get_ayon_server_api_connection() - - return con.delete_project(project_name) - - -def create_thumbnail(project_name, src_filepath, thumbnail_id=None, con=None): - if con is None: - con = get_ayon_server_api_connection() - return con.create_thumbnail(project_name, src_filepath, thumbnail_id) diff --git a/client/ayon_core/client/operations_base.py b/client/ayon_core/client/operations_base.py deleted file mode 100644 index 887b237b1c..0000000000 --- a/client/ayon_core/client/operations_base.py +++ /dev/null @@ -1,289 +0,0 @@ -import uuid -import copy -from abc import ABCMeta, abstractmethod, abstractproperty -import six - -REMOVED_VALUE = object() - - -@six.add_metaclass(ABCMeta) -class AbstractOperation(object): - """Base operation class. - - Operation represent a call into database. The call can create, change or - remove data. - - Args: - project_name (str): On which project operation will happen. - entity_type (str): Type of entity on which change happens. - e.g. 'asset', 'representation' etc. - """ - - def __init__(self, project_name, entity_type): - self._project_name = project_name - self._entity_type = entity_type - self._id = str(uuid.uuid4()) - - @property - def project_name(self): - return self._project_name - - @property - def id(self): - """Identifier of operation.""" - - return self._id - - @property - def entity_type(self): - return self._entity_type - - @abstractproperty - def operation_name(self): - """Stringified type of operation.""" - - pass - - def to_data(self): - """Convert operation to data that can be converted to json or others. - - Warning: - Current state returns ObjectId objects which cannot be parsed by - json. - - Returns: - Dict[str, Any]: Description of operation. - """ - - return { - "id": self._id, - "entity_type": self.entity_type, - "project_name": self.project_name, - "operation": self.operation_name - } - - -class CreateOperation(AbstractOperation): - """Operation to create an entity. - - Args: - project_name (str): On which project operation will happen. - entity_type (str): Type of entity on which change happens. - e.g. 'asset', 'representation' etc. - data (Dict[str, Any]): Data of entity that will be created. - """ - - operation_name = "create" - - def __init__(self, project_name, entity_type, data): - super(CreateOperation, self).__init__(project_name, entity_type) - - if not data: - data = {} - else: - data = copy.deepcopy(dict(data)) - self._data = data - - def __setitem__(self, key, value): - self.set_value(key, value) - - def __getitem__(self, key): - return self.data[key] - - def set_value(self, key, value): - self.data[key] = value - - def get(self, key, *args, **kwargs): - return self.data.get(key, *args, **kwargs) - - @abstractproperty - def entity_id(self): - pass - - @property - def data(self): - return self._data - - def to_data(self): - output = super(CreateOperation, self).to_data() - output["data"] = copy.deepcopy(self.data) - return output - - -class UpdateOperation(AbstractOperation): - """Operation to update an entity. - - Args: - project_name (str): On which project operation will happen. - entity_type (str): Type of entity on which change happens. - e.g. 'asset', 'representation' etc. - entity_id (Union[str, ObjectId]): Identifier of an entity. - update_data (Dict[str, Any]): Key -> value changes that will be set in - database. If value is set to 'REMOVED_VALUE' the key will be - removed. Only first level of dictionary is checked (on purpose). - """ - - operation_name = "update" - - def __init__(self, project_name, entity_type, entity_id, update_data): - super(UpdateOperation, self).__init__(project_name, entity_type) - - self._entity_id = entity_id - self._update_data = update_data - - @property - def entity_id(self): - return self._entity_id - - @property - def update_data(self): - return self._update_data - - def to_data(self): - changes = {} - for key, value in self._update_data.items(): - if value is REMOVED_VALUE: - value = None - changes[key] = value - - output = super(UpdateOperation, self).to_data() - output.update({ - "entity_id": self.entity_id, - "changes": changes - }) - return output - - -class DeleteOperation(AbstractOperation): - """Operation to delete an entity. - - Args: - project_name (str): On which project operation will happen. - entity_type (str): Type of entity on which change happens. - e.g. 'asset', 'representation' etc. - entity_id (Union[str, ObjectId]): Entity id that will be removed. - """ - - operation_name = "delete" - - def __init__(self, project_name, entity_type, entity_id): - super(DeleteOperation, self).__init__(project_name, entity_type) - - self._entity_id = entity_id - - @property - def entity_id(self): - return self._entity_id - - def to_data(self): - output = super(DeleteOperation, self).to_data() - output["entity_id"] = self.entity_id - return output - - -class BaseOperationsSession(object): - """Session storing operations that should happen in an order. - - At this moment does not handle anything special can be considered as - stupid list of operations that will happen after each other. If creation - of same entity is there multiple times it's handled in any way and document - values are not validated. - """ - - def __init__(self): - self._operations = [] - - def __len__(self): - return len(self._operations) - - def add(self, operation): - """Add operation to be processed. - - Args: - operation (BaseOperation): Operation that should be processed. - """ - if not isinstance( - operation, - (CreateOperation, UpdateOperation, DeleteOperation) - ): - raise TypeError("Expected Operation object got {}".format( - str(type(operation)) - )) - - self._operations.append(operation) - - def append(self, operation): - """Add operation to be processed. - - Args: - operation (BaseOperation): Operation that should be processed. - """ - - self.add(operation) - - def extend(self, operations): - """Add operations to be processed. - - Args: - operations (List[BaseOperation]): Operations that should be - processed. - """ - - for operation in operations: - self.add(operation) - - def remove(self, operation): - """Remove operation.""" - - self._operations.remove(operation) - - def clear(self): - """Clear all registered operations.""" - - self._operations = [] - - def to_data(self): - return [ - operation.to_data() - for operation in self._operations - ] - - @abstractmethod - def commit(self): - """Commit session operations.""" - pass - - def create_entity(self, project_name, entity_type, data): - """Fast access to 'CreateOperation'. - - Returns: - CreateOperation: Object of update operation. - """ - - operation = CreateOperation(project_name, entity_type, data) - self.add(operation) - return operation - - def update_entity(self, project_name, entity_type, entity_id, update_data): - """Fast access to 'UpdateOperation'. - - Returns: - UpdateOperation: Object of update operation. - """ - - operation = UpdateOperation( - project_name, entity_type, entity_id, update_data - ) - self.add(operation) - return operation - - def delete_entity(self, project_name, entity_type, entity_id): - """Fast access to 'DeleteOperation'. - - Returns: - DeleteOperation: Object of delete operation. - """ - - operation = DeleteOperation(project_name, entity_type, entity_id) - self.add(operation) - return operation diff --git a/client/ayon_core/client/utils.py b/client/ayon_core/client/utils.py index 26da6e34e1..bf73da5234 100644 --- a/client/ayon_core/client/utils.py +++ b/client/ayon_core/client/utils.py @@ -1,134 +1,9 @@ -import os -import uuid - import ayon_api -from ayon_core.client.operations_base import REMOVED_VALUE - class _GlobalCache: initialized = False def get_ayon_server_api_connection(): - if _GlobalCache.initialized: - con = ayon_api.get_server_api_connection() - else: - from ayon_core.lib.local_settings import get_local_site_id - - _GlobalCache.initialized = True - site_id = get_local_site_id() - version = os.getenv("AYON_VERSION") - if ayon_api.is_connection_created(): - con = ayon_api.get_server_api_connection() - con.set_site_id(site_id) - con.set_client_version(version) - else: - con = ayon_api.create_connection(site_id, version) - return con - - -def create_entity_id(): - return uuid.uuid1().hex - - -def prepare_attribute_changes(old_entity, new_entity, replace=False): - """Prepare changes of attributes on entities. - - Compare 'attrib' of old and new entity data to prepare only changed - values that should be sent to server for update. - - Example: - >>> # Limited entity data to 'attrib' - >>> old_entity = { - ... "attrib": {"attr_1": 1, "attr_2": "MyString", "attr_3": True} - ... } - >>> new_entity = { - ... "attrib": {"attr_1": 2, "attr_3": True, "attr_4": 3} - ... } - >>> # Changes if replacement should not happen - >>> expected_changes = { - ... "attr_1": 2, - ... "attr_4": 3 - ... } - >>> changes = prepare_attribute_changes(old_entity, new_entity) - >>> changes == expected_changes - True - - >>> # Changes if replacement should happen - >>> expected_changes_replace = { - ... "attr_1": 2, - ... "attr_2": REMOVED_VALUE, - ... "attr_4": 3 - ... } - >>> changes_replace = prepare_attribute_changes( - ... old_entity, new_entity, True) - >>> changes_replace == expected_changes_replace - True - - Args: - old_entity (dict[str, Any]): Data of entity queried from server. - new_entity (dict[str, Any]): Entity data with applied changes. - replace (bool): New entity should fully replace all old entity values. - - Returns: - Dict[str, Any]: Values from new entity only if value has changed. - """ - - attrib_changes = {} - new_attrib = new_entity.get("attrib") - old_attrib = old_entity.get("attrib") - if new_attrib is None: - if not replace: - return attrib_changes - new_attrib = {} - - if old_attrib is None: - return new_attrib - - for attr, new_attr_value in new_attrib.items(): - old_attr_value = old_attrib.get(attr) - if old_attr_value != new_attr_value: - attrib_changes[attr] = new_attr_value - - if replace: - for attr in old_attrib: - if attr not in new_attrib: - attrib_changes[attr] = REMOVED_VALUE - - return attrib_changes - - -def prepare_entity_changes(old_entity, new_entity, replace=False): - """Prepare changes of AYON entities. - - Compare old and new entity to filter values from new data that changed. - - Args: - old_entity (dict[str, Any]): Data of entity queried from server. - new_entity (dict[str, Any]): Entity data with applied changes. - replace (bool): All attributes should be replaced by new values. So - all attribute values that are not on new entity will be removed. - - Returns: - Dict[str, Any]: Only values from new entity that changed. - """ - - changes = {} - for key, new_value in new_entity.items(): - if key == "attrib": - continue - - old_value = old_entity.get(key) - if old_value != new_value: - changes[key] = new_value - - if replace: - for key in old_entity: - if key not in new_entity: - changes[key] = REMOVED_VALUE - - attr_changes = prepare_attribute_changes(old_entity, new_entity, replace) - if attr_changes: - changes["attrib"] = attr_changes - return changes + return ayon_api.get_server_api_connection() From 4092d177f56a48e6cb5f79e690a341f83d16ca53 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 8 Mar 2024 23:21:15 +0800 Subject: [PATCH 500/573] QtWidget for setting the material options when updating --- .../hosts/max/plugins/load/load_max_scene.py | 47 ++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py index 915abc75a1..9daf9bb72d 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py +++ b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py @@ -1,5 +1,5 @@ import os - +from qtpy import QtWidgets, QtCore from ayon_core.lib import EnumDef from ayon_core.hosts.max.api import lib from ayon_core.hosts.max.api.lib import ( @@ -13,6 +13,47 @@ from ayon_core.hosts.max.api.pipeline import ( remove_container_data ) from ayon_core.pipeline import get_representation_path, load +from ayon_core.settings import get_project_settings + + +class MaterialDupOptionsWindow(QtWidgets.QDialog): + + def __init__(self, material_options): + super(MaterialDupOptionsWindow, self).__init__() + self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint) + + self.material_option = None + + self.widgets = { + "label": QtWidgets.QLabel( + "Select material duplicate options before loading the max scene."), + "material_options_list": QtWidgets.QListWidget(), + "warning": QtWidgets.QLabel("No material options selected!"), + "okButton": QtWidgets.QPushButton("Ok"), + } + for option in material_options: + self.widgets["material_options_list"].addItem(option) + # Build buttons. + layout = QtWidgets.QHBoxLayout(self.widgets["buttons"]) + layout.addWidget(self.widgets["okButton"]) + # Build layout. + layout = QtWidgets.QVBoxLayout(self) + layout.addWidget(self.widgets["label"]) + layout.addWidget(self.widgets["list"]) + layout.addWidget(self.widgets["buttons"]) + + self.widgets["okButton"].pressed.connect(self.on_ok_pressed) + self.widgets["material_options_list"].itemPressed.connect( + self.on_material_optionsPressed) + + def on_material_optionsPressed(self, item): + self.material_option = item.text() + + def on_ok_pressed(self): + if self.material_option is None: + self.widgets["warning"].setVisible(True) + return + self.close() class MaxSceneLoader(load.LoaderPlugin): @@ -82,7 +123,9 @@ class MaxSceneLoader(load.LoaderPlugin): for prev_max_obj in prev_max_objects: if rt.isValidNode(prev_max_obj): # noqa rt.Delete(prev_max_obj) - rt.MergeMaxFile(path, quiet=True) + window = MaterialDupOptionsWindow(self.mtl_dup_enum) + window.exec_() + rt.MergeMaxFile(path, rt.Name(window.material_option), quiet=True) current_max_objects = rt.getLastMergedNodes() From f69d77c54c0bb9af5ac36726be36472e893bdbfe Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 16:30:25 +0100 Subject: [PATCH 501/573] change 'USE_AYON_ENTITIES' value and use cleaner deprecation marks --- client/ayon_core/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/__init__.py b/client/ayon_core/__init__.py index 5f9eb6cea3..1a21da9735 100644 --- a/client/ayon_core/__init__.py +++ b/client/ayon_core/__init__.py @@ -3,10 +3,13 @@ import os AYON_CORE_ROOT = os.path.dirname(os.path.abspath(__file__)) -# TODO remove after '1.x.x' +# ------------------------- +# DEPRECATED - Remove before '1.x.x' release +# ------------------------- PACKAGE_DIR = AYON_CORE_ROOT PLUGINS_DIR = os.path.join(AYON_CORE_ROOT, "plugins") AYON_SERVER_ENABLED = True # Indicate if AYON entities should be used instead of OpenPype entities -USE_AYON_ENTITIES = False +USE_AYON_ENTITIES = True +# ------------------------- From 06f124c7bf83f01f39a5f4e18d8e929dbc6a2c48 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 16:36:49 +0100 Subject: [PATCH 502/573] remove client and implement 'initialize_ayon_connection' to initialize ayon api --- client/ayon_core/__init__.py | 1 + client/ayon_core/addon/base.py | 8 ++-- client/ayon_core/cli.py | 2 + client/ayon_core/client/__init__.py | 6 --- client/ayon_core/client/utils.py | 9 ----- client/ayon_core/lib/__init__.py | 47 +++++++++++----------- client/ayon_core/lib/ayon_connection.py | 34 ++++++++++++++++ client/ayon_core/lib/local_settings.py | 8 ++-- client/ayon_core/pipeline/context_tools.py | 4 +- client/ayon_core/settings/lib.py | 19 ++++----- 10 files changed, 76 insertions(+), 62 deletions(-) delete mode 100644 client/ayon_core/client/__init__.py delete mode 100644 client/ayon_core/client/utils.py create mode 100644 client/ayon_core/lib/ayon_connection.py diff --git a/client/ayon_core/__init__.py b/client/ayon_core/__init__.py index 1a21da9735..7d95587e8a 100644 --- a/client/ayon_core/__init__.py +++ b/client/ayon_core/__init__.py @@ -1,4 +1,5 @@ import os +from .version import __version__ AYON_CORE_ROOT = os.path.dirname(os.path.abspath(__file__)) diff --git a/client/ayon_core/addon/base.py b/client/ayon_core/addon/base.py index f0763649ca..bbd5a486fe 100644 --- a/client/ayon_core/addon/base.py +++ b/client/ayon_core/addon/base.py @@ -14,9 +14,9 @@ from abc import ABCMeta, abstractmethod import six import appdirs +import ayon_api from ayon_core.lib import Logger, is_dev_mode_enabled -from ayon_core.client import get_ayon_server_api_connection from ayon_core.settings import get_studio_settings from .interfaces import ( @@ -147,8 +147,7 @@ def load_addons(force=False): def _get_ayon_bundle_data(): - con = get_ayon_server_api_connection() - bundles = con.get_bundles()["bundles"] + bundles = ayon_api.get_bundles()["bundles"] bundle_name = os.getenv("AYON_BUNDLE_NAME") @@ -176,8 +175,7 @@ def _get_ayon_addons_information(bundle_info): output = [] bundle_addons = bundle_info["addons"] - con = get_ayon_server_api_connection() - addons = con.get_addons_info()["addons"] + addons = ayon_api.get_addons_info()["addons"] for addon in addons: name = addon["name"] versions = addon.get("versions") diff --git a/client/ayon_core/cli.py b/client/ayon_core/cli.py index 88b574da76..0ad4364bcd 100644 --- a/client/ayon_core/cli.py +++ b/client/ayon_core/cli.py @@ -11,6 +11,7 @@ import acre from ayon_core import AYON_CORE_ROOT from ayon_core.addon import AddonsManager from ayon_core.settings import get_general_environments +from ayon_core.lib import initialize_ayon_connection from .cli_commands import Commands @@ -243,6 +244,7 @@ def _set_addons_environments(): def main(*args, **kwargs): + initialize_ayon_connection() python_path = os.getenv("PYTHONPATH", "") split_paths = python_path.split(os.pathsep) diff --git a/client/ayon_core/client/__init__.py b/client/ayon_core/client/__init__.py deleted file mode 100644 index ea651ba8db..0000000000 --- a/client/ayon_core/client/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .utils import get_ayon_server_api_connection - - -__all__ = ( - "get_ayon_server_api_connection", -) diff --git a/client/ayon_core/client/utils.py b/client/ayon_core/client/utils.py deleted file mode 100644 index bf73da5234..0000000000 --- a/client/ayon_core/client/utils.py +++ /dev/null @@ -1,9 +0,0 @@ -import ayon_api - - -class _GlobalCache: - initialized = False - - -def get_ayon_server_api_connection(): - return ayon_api.get_server_api_connection() diff --git a/client/ayon_core/lib/__init__.py b/client/ayon_core/lib/__init__.py index ab6a604adc..6f76506dd8 100644 --- a/client/ayon_core/lib/__init__.py +++ b/client/ayon_core/lib/__init__.py @@ -15,7 +15,18 @@ python_version_dir = os.path.join( sys.path.insert(0, python_version_dir) site.addsitedir(python_version_dir) - +from .local_settings import ( + IniSettingRegistry, + JSONSettingRegistry, + AYONSecureRegistry, + AYONSettingsRegistry, + OpenPypeSecureRegistry, + OpenPypeSettingsRegistry, + get_local_site_id, + get_ayon_username, + get_openpype_username, +) +from .ayon_connection import initialize_ayon_connection from .events import ( emit_event, register_event_callback @@ -112,18 +123,6 @@ from .transcoding import ( get_rescaled_command_arguments, ) -from .local_settings import ( - IniSettingRegistry, - JSONSettingRegistry, - AYONSecureRegistry, - AYONSettingsRegistry, - OpenPypeSecureRegistry, - OpenPypeSettingsRegistry, - get_local_site_id, - get_ayon_username, - get_openpype_username, -) - from .applications import ( ApplicationLaunchFailed, ApplictionExecutableNotFound, @@ -170,6 +169,18 @@ from .connections import ( terminal = Terminal __all__ = [ + "IniSettingRegistry", + "JSONSettingRegistry", + "AYONSecureRegistry", + "AYONSettingsRegistry", + "OpenPypeSecureRegistry", + "OpenPypeSettingsRegistry", + "get_local_site_id", + "get_ayon_username", + "get_openpype_username", + + "initialize_ayon_connection", + "emit_event", "register_event_callback", @@ -228,16 +239,6 @@ __all__ = [ "convert_ffprobe_fps_to_float", "get_rescaled_command_arguments", - "IniSettingRegistry", - "JSONSettingRegistry", - "AYONSecureRegistry", - "AYONSettingsRegistry", - "OpenPypeSecureRegistry", - "OpenPypeSettingsRegistry", - "get_local_site_id", - "get_ayon_username", - "get_openpype_username", - "ApplicationLaunchFailed", "ApplictionExecutableNotFound", "ApplicationNotFound", diff --git a/client/ayon_core/lib/ayon_connection.py b/client/ayon_core/lib/ayon_connection.py new file mode 100644 index 0000000000..aaa7dd3b4d --- /dev/null +++ b/client/ayon_core/lib/ayon_connection.py @@ -0,0 +1,34 @@ +import os + +import ayon_api + +from .local_settings import get_local_site_id + + +class _Cache: + initialized = False + + +def initialize_ayon_connection(force=False): + """Initialize global AYON api connection. + + Create global connection in ayon_api module and set site id + and client version. + Is silently skipped if already happened. + + Args: + force (Optional[bool]): Force reinitialize connection. + Defaults to False. + + """ + if not force and _Cache.initialized: + return + _Cache.initialized = True + site_id = get_local_site_id() + version = os.getenv("AYON_VERSION") + if ayon_api.is_connection_created(): + con = ayon_api.get_server_api_connection() + con.set_site_id(site_id) + con.set_client_version(version) + else: + ayon_api.create_connection(site_id, version) diff --git a/client/ayon_core/lib/local_settings.py b/client/ayon_core/lib/local_settings.py index 022f63a618..9eba3d1ed1 100644 --- a/client/ayon_core/lib/local_settings.py +++ b/client/ayon_core/lib/local_settings.py @@ -26,8 +26,7 @@ except ImportError: import six import appdirs - -from ayon_core.client import get_ayon_server_api_connection +import ayon_api _PLACEHOLDER = object() @@ -556,10 +555,9 @@ def get_ayon_username(): Returns: str: Username. - """ - con = get_ayon_server_api_connection() - return con.get_user()["name"] + """ + return ayon_api.get_user()["name"] def get_openpype_username(): diff --git a/client/ayon_core/pipeline/context_tools.py b/client/ayon_core/pipeline/context_tools.py index b314e02998..1ad80694bf 100644 --- a/client/ayon_core/pipeline/context_tools.py +++ b/client/ayon_core/pipeline/context_tools.py @@ -13,8 +13,7 @@ from pyblish.lib import MessageHandler from ayon_core import AYON_CORE_ROOT from ayon_core.host import HostBase from ayon_core.client import get_ayon_server_api_connection -from ayon_core.lib import is_in_tests -from ayon_core.lib.events import emit_event +from ayon_core.lib import is_in_tests, initialize_ayon_connection, emit_event from ayon_core.addon import load_addons, AddonsManager from ayon_core.settings import get_project_settings @@ -157,6 +156,7 @@ def install_host(host): def install_ayon_plugins(project_name=None, host_name=None): # Make sure modules are loaded + initialize_ayon_connection() load_addons() log.info("Registering global plug-ins..") diff --git a/client/ayon_core/settings/lib.py b/client/ayon_core/settings/lib.py index 69525d5b86..d72e4f357a 100644 --- a/client/ayon_core/settings/lib.py +++ b/client/ayon_core/settings/lib.py @@ -5,7 +5,7 @@ import collections import copy import time -from ayon_core.client import get_ayon_server_api_connection +import ayon_api log = logging.getLogger(__name__) @@ -46,8 +46,7 @@ class _AyonSettingsCache: @classmethod def _use_bundles(cls): if _AyonSettingsCache.use_bundles is None: - con = get_ayon_server_api_connection() - major, minor, _, _, _ = con.get_server_version_tuple() + major, minor, _, _, _ = ayon_api.get_server_version_tuple() use_bundles = True if (major, minor) < (0, 3): use_bundles = False @@ -69,8 +68,7 @@ class _AyonSettingsCache: _AyonSettingsCache.variant = variant # Set the variant to global ayon api connection - con = get_ayon_server_api_connection() - con.set_default_settings_variant(variant) + ayon_api.set_default_settings_variant(variant) return _AyonSettingsCache.variant @classmethod @@ -81,23 +79,21 @@ class _AyonSettingsCache: def get_value_by_project(cls, project_name): cache_item = _AyonSettingsCache.cache_by_project_name[project_name] if cache_item.is_outdated: - con = get_ayon_server_api_connection() if cls._use_bundles(): - value = con.get_addons_settings( + value = ayon_api.get_addons_settings( bundle_name=cls._get_bundle_name(), project_name=project_name, variant=cls._get_variant() ) else: - value = con.get_addons_settings(project_name) + value = ayon_api.get_addons_settings(project_name) cache_item.update_value(value) return cache_item.get_value() @classmethod def _get_addon_versions_from_bundle(cls): - con = get_ayon_server_api_connection() expected_bundle = cls._get_bundle_name() - bundles = con.get_bundles()["bundles"] + bundles = ayon_api.get_bundles()["bundles"] bundle = next( ( bundle @@ -117,8 +113,7 @@ class _AyonSettingsCache: if cls._use_bundles(): addons = cls._get_addon_versions_from_bundle() else: - con = get_ayon_server_api_connection() - settings_data = con.get_addons_settings( + settings_data = ayon_api.get_addons_settings( only_values=False, variant=cls._get_variant() ) From 142206c71b75303143a969c5fd58e5d8f9718fa3 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Sat, 9 Mar 2024 00:01:37 +0800 Subject: [PATCH 503/573] make sure Pop up window works --- .../ayon_core/hosts/max/plugins/load/load_max_scene.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py index 9daf9bb72d..f30d214cbe 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py +++ b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py @@ -13,7 +13,6 @@ from ayon_core.hosts.max.api.pipeline import ( remove_container_data ) from ayon_core.pipeline import get_representation_path, load -from ayon_core.settings import get_project_settings class MaterialDupOptionsWindow(QtWidgets.QDialog): @@ -29,20 +28,24 @@ class MaterialDupOptionsWindow(QtWidgets.QDialog): "Select material duplicate options before loading the max scene."), "material_options_list": QtWidgets.QListWidget(), "warning": QtWidgets.QLabel("No material options selected!"), + "buttons": QtWidgets.QWidget(), "okButton": QtWidgets.QPushButton("Ok"), + "cancelButton": QtWidgets.QPushButton("Cancel") } for option in material_options: self.widgets["material_options_list"].addItem(option) # Build buttons. layout = QtWidgets.QHBoxLayout(self.widgets["buttons"]) layout.addWidget(self.widgets["okButton"]) + layout.addWidget(self.widgets["cancelButton"]) # Build layout. layout = QtWidgets.QVBoxLayout(self) layout.addWidget(self.widgets["label"]) - layout.addWidget(self.widgets["list"]) + layout.addWidget(self.widgets["material_options_list"]) layout.addWidget(self.widgets["buttons"]) self.widgets["okButton"].pressed.connect(self.on_ok_pressed) + self.widgets["cancelButton"].pressed.connect(self.on_cancel_pressed) self.widgets["material_options_list"].itemPressed.connect( self.on_material_optionsPressed) @@ -55,6 +58,9 @@ class MaterialDupOptionsWindow(QtWidgets.QDialog): return self.close() + def on_cancel_pressed(self): + self.material_option = "promptMtlDups" + self.close() class MaxSceneLoader(load.LoaderPlugin): """Max Scene Loader.""" From 92abf5a14b995cf4f6b51fa4e396c35aaa8090bc Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 17:20:14 +0100 Subject: [PATCH 504/573] use folder naming on create instance --- client/ayon_core/pipeline/create/context.py | 36 +++++++++---------- .../tools/publisher/widgets/widgets.py | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index 4fdba96bb6..308762fae6 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -978,7 +978,7 @@ class CreatedInstance: if not self._data.get("instance_id"): self._data["instance_id"] = str(uuid4()) - self._asset_is_valid = self.has_set_asset + self._folder_is_valid = self.has_set_folder self._task_is_valid = self.has_set_task def __str__(self): @@ -1279,8 +1279,8 @@ class CreatedInstance: # Context validation related methods/properties @property - def has_set_asset(self): - """Asset name is set in data.""" + def has_set_folder(self): + """Folder path is set in data.""" return "folderPath" in self._data @@ -1294,15 +1294,15 @@ class CreatedInstance: def has_valid_context(self): """Context data are valid for publishing.""" - return self.has_valid_asset and self.has_valid_task + return self.has_valid_folder and self.has_valid_task @property - def has_valid_asset(self): - """Asset set in context exists in project.""" + def has_valid_folder(self): + """Folder set in context exists in project.""" - if not self.has_set_asset: + if not self.has_set_folder: return False - return self._asset_is_valid + return self._folder_is_valid @property def has_valid_task(self): @@ -1312,9 +1312,9 @@ class CreatedInstance: return False return self._task_is_valid - def set_asset_invalid(self, invalid): - # TODO replace with `set_asset_name` - self._asset_is_valid = not invalid + def set_folder_invalid(self, invalid): + # TODO replace with `set_folder_path` + self._folder_is_valid = not invalid def set_task_invalid(self, invalid): # TODO replace with `set_task_name` @@ -1554,10 +1554,10 @@ class CreateContext: return self._current_project_name def get_current_folder_path(self): - """Asset name which was used as current context on context reset. + """Folder path which was used as current context on context reset. Returns: - Union[str, None]: Asset name. + Union[str, None]: Folder path. """ return self._current_folder_path @@ -1596,7 +1596,7 @@ class CreateContext: def context_has_changed(self): """Host context has changed. - As context is used project, asset, task name and workfile path if + As context is used project, folder, task name and workfile path if host does support workfiles. Returns: @@ -1971,7 +1971,7 @@ class CreateContext: Any: Output of triggered creator's 'create' method. Raises: - CreatorError: If creator was not found or asset is empty. + CreatorError: If creator was not found or folder is empty. """ creator = self._get_creator_in_create(creator_identifier) @@ -2228,7 +2228,7 @@ class CreateContext: raise CreatorsCreateFailed(failed_info) def validate_instances_context(self, instances=None): - """Validate 'asset' and 'task' instance context.""" + """Validate 'folder' and 'task' instance context.""" # Use all instances from context if 'instances' are not passed if instances is None: instances = tuple(self._instances_by_id.values()) @@ -2295,7 +2295,7 @@ class CreateContext: task_names_by_folder_path[folder_path].add(task_entity["name"]) for instance in instances: - if not instance.has_valid_asset or not instance.has_valid_task: + if not instance.has_valid_folder or not instance.has_valid_task: continue folder_path = instance["folderPath"] @@ -2306,7 +2306,7 @@ class CreateContext: instance["folderPath"] = folder_path if folder_path not in task_names_by_folder_path: - instance.set_asset_invalid(True) + instance.set_folder_invalid(True) continue task_name = instance["task"] diff --git a/client/ayon_core/tools/publisher/widgets/widgets.py b/client/ayon_core/tools/publisher/widgets/widgets.py index 4005cf2c84..12c03c7eeb 100644 --- a/client/ayon_core/tools/publisher/widgets/widgets.py +++ b/client/ayon_core/tools/publisher/widgets/widgets.py @@ -1206,7 +1206,7 @@ class GlobalAttrsWidget(QtWidgets.QWidget): if folder_path is not None: instance["folderPath"] = folder_path - instance.set_asset_invalid(False) + instance.set_folder_invalid(False) if task_name is not None: instance["task"] = task_name or None From 1428e84b11ff99581068e4652d3476319100c258 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 17:29:22 +0100 Subject: [PATCH 505/573] removed assetlinks widgets --- client/ayon_core/tools/assetlinks/__init__.py | 0 client/ayon_core/tools/assetlinks/widgets.py | 155 ------------------ 2 files changed, 155 deletions(-) delete mode 100644 client/ayon_core/tools/assetlinks/__init__.py delete mode 100644 client/ayon_core/tools/assetlinks/widgets.py diff --git a/client/ayon_core/tools/assetlinks/__init__.py b/client/ayon_core/tools/assetlinks/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/client/ayon_core/tools/assetlinks/widgets.py b/client/ayon_core/tools/assetlinks/widgets.py deleted file mode 100644 index 7db6243358..0000000000 --- a/client/ayon_core/tools/assetlinks/widgets.py +++ /dev/null @@ -1,155 +0,0 @@ -import collections -from ayon_core.client import ( - get_versions, - get_subsets, - get_assets, - get_output_link_versions, -) - -from qtpy import QtWidgets - - -class SimpleLinkView(QtWidgets.QWidget): - def __init__(self, dbcon, parent): - super(SimpleLinkView, self).__init__(parent=parent) - self.dbcon = dbcon - - # TODO: display selected target - - in_text = QtWidgets.QLabel("Inputs") - in_view = QtWidgets.QListWidget(parent=self) - out_text = QtWidgets.QLabel("Outputs") - out_view = QtWidgets.QListWidget(parent=self) - - layout = QtWidgets.QGridLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.addWidget(in_text, 0, 0) - layout.addWidget(in_view, 1, 0) - layout.addWidget(out_text, 0, 1) - layout.addWidget(out_view, 1, 1) - - self._in_view = in_view - self._out_view = out_view - self._version_doc_to_process = None - - @property - def project_name(self): - return self.dbcon.current_project() - - def clear(self): - self._in_view.clear() - self._out_view.clear() - - def set_version(self, version_doc): - self.clear() - self._version_doc_to_process = version_doc - if version_doc and self.isVisible(): - self._fill_values() - - def showEvent(self, event): - super(SimpleLinkView, self).showEvent(event) - self._fill_values() - - def _fill_values(self): - if self._version_doc_to_process is None: - return - version_doc = self._version_doc_to_process - self._version_doc_to_process = None - self._fill_inputs(version_doc) - self._fill_outputs(version_doc) - - def _fill_inputs(self, version_doc): - version_ids = set() - for link in version_doc["data"].get("inputLinks", []): - # Backwards compatibility for "input" key used as "id" - if "id" not in link: - link_id = link["input"] - else: - link_id = link["id"] - version_ids.add(link_id) - - version_docs = list(get_versions( - self.project_name, - version_ids=version_ids, - fields=["name", "parent"] - )) - - versions_by_subset_id = collections.defaultdict(list) - for version_doc in version_docs: - subset_id = version_doc["parent"] - versions_by_subset_id[subset_id].append(version_doc) - - subset_docs = [] - if versions_by_subset_id: - subset_docs = list(get_subsets( - self.project_name, - subset_ids=versions_by_subset_id.keys(), - fields=["_id", "name", "parent"] - )) - - asset_docs = [] - subsets_by_asset_id = collections.defaultdict(list) - if subset_docs: - for subset_doc in subset_docs: - asset_id = subset_doc["parent"] - subsets_by_asset_id[asset_id].append(subset_doc) - - asset_docs = list(get_assets( - self.project_name, - asset_ids=subsets_by_asset_id.keys(), - fields=["_id", "name"] - )) - - for asset_doc in asset_docs: - asset_id = asset_doc["_id"] - for subset_doc in subsets_by_asset_id[asset_id]: - subset_id = subset_doc["_id"] - for version_doc in versions_by_subset_id[subset_id]: - self._in_view.addItem("{} {} v{:0>3}".format( - asset_doc["name"], - subset_doc["name"], - version_doc["name"], - )) - - def _fill_outputs(self, version_doc): - version_docs = list(get_output_link_versions( - self.project_name, - version_doc["_id"], - fields=["name", "parent"] - )) - versions_by_subset_id = collections.defaultdict(list) - for version_doc in version_docs: - subset_id = version_doc["parent"] - versions_by_subset_id[subset_id].append(version_doc) - - subset_docs = [] - if versions_by_subset_id: - subset_docs = list(get_subsets( - self.project_name, - subset_ids=versions_by_subset_id.keys(), - fields=["_id", "name", "parent"] - )) - - asset_docs = [] - subsets_by_asset_id = collections.defaultdict(list) - if subset_docs: - for subset_doc in subset_docs: - asset_id = subset_doc["parent"] - subsets_by_asset_id[asset_id].append(subset_doc) - - asset_docs = list(get_assets( - self.project_name, - asset_ids=subsets_by_asset_id.keys(), - fields=["_id", "name"] - )) - - for asset_doc in asset_docs: - asset_id = asset_doc["_id"] - for subset_doc in subsets_by_asset_id[asset_id]: - subset_id = subset_doc["_id"] - for version_doc in versions_by_subset_id[subset_id]: - self._out_view.addItem("{} {} v{:0>3}".format( - asset_doc["name"], - subset_doc["name"], - version_doc["name"], - )) From bfcfdeb674f260730f8cfeb000bc30b45f03a436 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 17:32:18 +0100 Subject: [PATCH 506/573] fix context tools --- client/ayon_core/pipeline/context_tools.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/context_tools.py b/client/ayon_core/pipeline/context_tools.py index 1ad80694bf..cc5acd810c 100644 --- a/client/ayon_core/pipeline/context_tools.py +++ b/client/ayon_core/pipeline/context_tools.py @@ -12,7 +12,6 @@ from pyblish.lib import MessageHandler from ayon_core import AYON_CORE_ROOT from ayon_core.host import HostBase -from ayon_core.client import get_ayon_server_api_connection from ayon_core.lib import is_in_tests, initialize_ayon_connection, emit_event from ayon_core.addon import load_addons, AddonsManager from ayon_core.settings import get_project_settings @@ -106,7 +105,7 @@ def install_host(host): _is_installed = True # Make sure global AYON connection has set site id and version - get_ayon_server_api_connection() + initialize_ayon_connection() addons_manager = _get_addons_manager() @@ -155,8 +154,10 @@ def install_host(host): def install_ayon_plugins(project_name=None, host_name=None): - # Make sure modules are loaded + # Make sure global AYON connection has set site id and version + # - this is necessary if 'install_host' is not called initialize_ayon_connection() + # Make sure addons are loaded load_addons() log.info("Registering global plug-ins..") From 394186bfb89259f2cb66f8fc516335d7e74447b4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 17:32:42 +0100 Subject: [PATCH 507/573] use more suitable functions to get context data --- client/ayon_core/hosts/nuke/api/lib.py | 4 ++-- client/ayon_core/pipeline/colorspace.py | 4 ++-- client/ayon_core/pipeline/context_tools.py | 23 ++++++++++++++++++++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index 374a4608fc..8e39475f10 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -41,7 +41,7 @@ from ayon_core.pipeline import ( AVALON_INSTANCE_ID, ) from ayon_core.pipeline.context_tools import ( - get_custom_workfile_template_from_session + get_current_context_custom_workfile_template ) from ayon_core.pipeline.colorspace import get_imageio_config from ayon_core.pipeline.workfile import BuildWorkfile @@ -2458,7 +2458,7 @@ def process_workfile_builder(): # generate first version in file not existing and feature is enabled if create_fv_on and not os.path.exists(last_workfile_path): # get custom template path if any - custom_template_path = get_custom_workfile_template_from_session( + custom_template_path = get_current_context_custom_workfile_template( project_settings=project_settings ) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index 7100984217..034c90d27b 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -774,8 +774,8 @@ def get_imageio_config( if not anatomy_data: from ayon_core.pipeline.context_tools import ( - get_template_data_from_session) - anatomy_data = get_template_data_from_session() + get_current_context_template_data) + anatomy_data = get_current_context_template_data() formatting_data = deepcopy(anatomy_data) diff --git a/client/ayon_core/pipeline/context_tools.py b/client/ayon_core/pipeline/context_tools.py index cc5acd810c..50c384bf88 100644 --- a/client/ayon_core/pipeline/context_tools.py +++ b/client/ayon_core/pipeline/context_tools.py @@ -590,6 +590,29 @@ def get_custom_workfile_template_from_session( ) +def get_current_context_custom_workfile_template(project_settings=None): + """Filter and fill workfile template profiles by current context. + + This function can be used only inside host where context is set. + + Args: + project_settings(Optional[Dict[str, Any]]): Project settings. + + Returns: + str: Path to template or None if none of profiles match current + context. (Existence of formatted path is not validated.) + + """ + context = get_current_context() + return get_custom_workfile_template_by_string_context( + context["project_name"], + context["folder_path"], + context["task_name"], + get_current_host_name(), + project_settings=project_settings + ) + + def change_current_context(folder_entity, task_entity, template_key=None): """Update active Session to a new task work area. From 7b0641a00fc51b42ca21ccaeb1bf4d6c63b6e69e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 18:07:04 +0100 Subject: [PATCH 508/573] remove unused schemas --- .../pipeline/schema/application-1.0.json | 68 --------- .../ayon_core/pipeline/schema/asset-1.0.json | 35 ----- .../ayon_core/pipeline/schema/asset-2.0.json | 55 ------- .../ayon_core/pipeline/schema/asset-3.0.json | 55 ------- .../ayon_core/pipeline/schema/config-1.0.json | 85 ----------- .../ayon_core/pipeline/schema/config-1.1.json | 87 ----------- .../ayon_core/pipeline/schema/config-2.0.json | 87 ----------- .../pipeline/schema/hero_version-1.0.json | 44 ------ .../pipeline/schema/inventory-1.0.json | 10 -- .../pipeline/schema/inventory-1.1.json | 10 -- .../pipeline/schema/project-2.0.json | 86 ----------- .../pipeline/schema/project-2.1.json | 86 ----------- .../pipeline/schema/project-3.0.json | 59 -------- .../pipeline/schema/representation-1.0.json | 28 ---- .../pipeline/schema/representation-2.0.json | 78 ---------- .../pipeline/schema/session-1.0.json | 143 ------------------ .../pipeline/schema/session-2.0.json | 134 ---------------- .../pipeline/schema/session-3.0.json | 81 ---------- .../pipeline/schema/session-4.0.json | 61 -------- .../pipeline/schema/shaders-1.0.json | 32 ---- .../ayon_core/pipeline/schema/subset-1.0.json | 35 ----- .../ayon_core/pipeline/schema/subset-2.0.json | 51 ------- .../ayon_core/pipeline/schema/subset-3.0.json | 62 -------- .../pipeline/schema/thumbnail-1.0.json | 42 ----- .../pipeline/schema/version-1.0.json | 50 ------ .../pipeline/schema/version-2.0.json | 92 ----------- .../pipeline/schema/version-3.0.json | 84 ---------- .../pipeline/schema/workfile-1.0.json | 52 ------- 28 files changed, 1792 deletions(-) delete mode 100644 client/ayon_core/pipeline/schema/application-1.0.json delete mode 100644 client/ayon_core/pipeline/schema/asset-1.0.json delete mode 100644 client/ayon_core/pipeline/schema/asset-2.0.json delete mode 100644 client/ayon_core/pipeline/schema/asset-3.0.json delete mode 100644 client/ayon_core/pipeline/schema/config-1.0.json delete mode 100644 client/ayon_core/pipeline/schema/config-1.1.json delete mode 100644 client/ayon_core/pipeline/schema/config-2.0.json delete mode 100644 client/ayon_core/pipeline/schema/hero_version-1.0.json delete mode 100644 client/ayon_core/pipeline/schema/inventory-1.0.json delete mode 100644 client/ayon_core/pipeline/schema/inventory-1.1.json delete mode 100644 client/ayon_core/pipeline/schema/project-2.0.json delete mode 100644 client/ayon_core/pipeline/schema/project-2.1.json delete mode 100644 client/ayon_core/pipeline/schema/project-3.0.json delete mode 100644 client/ayon_core/pipeline/schema/representation-1.0.json delete mode 100644 client/ayon_core/pipeline/schema/representation-2.0.json delete mode 100644 client/ayon_core/pipeline/schema/session-1.0.json delete mode 100644 client/ayon_core/pipeline/schema/session-2.0.json delete mode 100644 client/ayon_core/pipeline/schema/session-3.0.json delete mode 100644 client/ayon_core/pipeline/schema/session-4.0.json delete mode 100644 client/ayon_core/pipeline/schema/shaders-1.0.json delete mode 100644 client/ayon_core/pipeline/schema/subset-1.0.json delete mode 100644 client/ayon_core/pipeline/schema/subset-2.0.json delete mode 100644 client/ayon_core/pipeline/schema/subset-3.0.json delete mode 100644 client/ayon_core/pipeline/schema/thumbnail-1.0.json delete mode 100644 client/ayon_core/pipeline/schema/version-1.0.json delete mode 100644 client/ayon_core/pipeline/schema/version-2.0.json delete mode 100644 client/ayon_core/pipeline/schema/version-3.0.json delete mode 100644 client/ayon_core/pipeline/schema/workfile-1.0.json diff --git a/client/ayon_core/pipeline/schema/application-1.0.json b/client/ayon_core/pipeline/schema/application-1.0.json deleted file mode 100644 index 953abee569..0000000000 --- a/client/ayon_core/pipeline/schema/application-1.0.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:application-1.0", - "description": "An application definition.", - - "type": "object", - - "additionalProperties": true, - - "required": [ - "schema", - "label", - "application_dir", - "executable" - ], - - "properties": { - "schema": { - "description": "Schema identifier for payload", - "type": "string" - }, - "label": { - "description": "Nice name of application.", - "type": "string" - }, - "application_dir": { - "description": "Name of directory used for application resources.", - "type": "string" - }, - "executable": { - "description": "Name of callable executable, this is called to launch the application", - "type": "string" - }, - "description": { - "description": "Description of application.", - "type": "string" - }, - "environment": { - "description": "Key/value pairs for environment variables related to this application. Supports lists for paths, such as PYTHONPATH.", - "type": "object", - "items": { - "oneOf": [ - {"type": "string"}, - {"type": "array", "items": {"type": "string"}} - ] - } - }, - "default_dirs": { - "type": "array", - "items": { - "type": "string" - } - }, - "copy": { - "type": "object", - "patternProperties": { - "^.*$": { - "anyOf": [ - {"type": "string"}, - {"type": "null"} - ] - } - }, - "additionalProperties": false - } - } -} diff --git a/client/ayon_core/pipeline/schema/asset-1.0.json b/client/ayon_core/pipeline/schema/asset-1.0.json deleted file mode 100644 index ab104c002a..0000000000 --- a/client/ayon_core/pipeline/schema/asset-1.0.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:asset-1.0", - "description": "A unit of data", - - "type": "object", - - "additionalProperties": true, - - "required": [ - "schema", - "name", - "subsets" - ], - - "properties": { - "schema": { - "description": "Schema identifier for payload", - "type": "string" - }, - "name": { - "description": "Name of directory", - "type": "string" - }, - "subsets": { - "type": "array", - "items": { - "$ref": "subset.json" - } - } - }, - - "definitions": {} -} diff --git a/client/ayon_core/pipeline/schema/asset-2.0.json b/client/ayon_core/pipeline/schema/asset-2.0.json deleted file mode 100644 index b894d79792..0000000000 --- a/client/ayon_core/pipeline/schema/asset-2.0.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:asset-2.0", - "description": "A unit of data", - - "type": "object", - - "additionalProperties": true, - - "required": [ - "schema", - "type", - "name", - "silo", - "data" - ], - - "properties": { - "schema": { - "description": "Schema identifier for payload", - "type": "string", - "enum": ["openpype:asset-2.0"], - "example": "openpype:asset-2.0" - }, - "type": { - "description": "The type of document", - "type": "string", - "enum": ["asset"], - "example": "asset" - }, - "parent": { - "description": "Unique identifier to parent document", - "example": "592c33475f8c1b064c4d1696" - }, - "name": { - "description": "Name of asset", - "type": "string", - "pattern": "^[a-zA-Z0-9_.]*$", - "example": "Bruce" - }, - "silo": { - "description": "Group or container of asset", - "type": "string", - "example": "assets" - }, - "data": { - "description": "Document metadata", - "type": "object", - "example": {"key": "value"} - } - }, - - "definitions": {} -} diff --git a/client/ayon_core/pipeline/schema/asset-3.0.json b/client/ayon_core/pipeline/schema/asset-3.0.json deleted file mode 100644 index 948704d2a1..0000000000 --- a/client/ayon_core/pipeline/schema/asset-3.0.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:asset-3.0", - "description": "A unit of data", - - "type": "object", - - "additionalProperties": true, - - "required": [ - "schema", - "type", - "name", - "data" - ], - - "properties": { - "schema": { - "description": "Schema identifier for payload", - "type": "string", - "enum": ["openpype:asset-3.0"], - "example": "openpype:asset-3.0" - }, - "type": { - "description": "The type of document", - "type": "string", - "enum": ["asset"], - "example": "asset" - }, - "parent": { - "description": "Unique identifier to parent document", - "example": "592c33475f8c1b064c4d1696" - }, - "name": { - "description": "Name of asset", - "type": "string", - "pattern": "^[a-zA-Z0-9_.]*$", - "example": "Bruce" - }, - "silo": { - "description": "Group or container of asset", - "type": "string", - "pattern": "^[a-zA-Z0-9_.]*$", - "example": "assets" - }, - "data": { - "description": "Document metadata", - "type": "object", - "example": {"key": "value"} - } - }, - - "definitions": {} -} diff --git a/client/ayon_core/pipeline/schema/config-1.0.json b/client/ayon_core/pipeline/schema/config-1.0.json deleted file mode 100644 index 49398a57cd..0000000000 --- a/client/ayon_core/pipeline/schema/config-1.0.json +++ /dev/null @@ -1,85 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:config-1.0", - "description": "A project configuration.", - - "type": "object", - - "additionalProperties": false, - "required": [ - "tasks", - "apps" - ], - - "properties": { - "schema": { - "description": "Schema identifier for payload", - "type": "string" - }, - "template": { - "type": "object", - "additionalProperties": false, - "patternProperties": { - "^.*$": { - "type": "string" - } - } - }, - "tasks": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": {"type": "string"}, - "icon": {"type": "string"}, - "group": {"type": "string"}, - "label": {"type": "string"} - }, - "required": ["name"] - } - }, - "apps": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": {"type": "string"}, - "icon": {"type": "string"}, - "group": {"type": "string"}, - "label": {"type": "string"} - }, - "required": ["name"] - } - }, - "families": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": {"type": "string"}, - "icon": {"type": "string"}, - "label": {"type": "string"}, - "hideFilter": {"type": "boolean"} - }, - "required": ["name"] - } - }, - "groups": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": {"type": "string"}, - "icon": {"type": "string"}, - "color": {"type": "string"}, - "order": {"type": ["integer", "number"]} - }, - "required": ["name"] - } - }, - "copy": { - "type": "object" - } - } -} diff --git a/client/ayon_core/pipeline/schema/config-1.1.json b/client/ayon_core/pipeline/schema/config-1.1.json deleted file mode 100644 index 6e15514aaf..0000000000 --- a/client/ayon_core/pipeline/schema/config-1.1.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:config-1.1", - "description": "A project configuration.", - - "type": "object", - - "additionalProperties": false, - "required": [ - "tasks", - "apps" - ], - - "properties": { - "schema": { - "description": "Schema identifier for payload", - "type": "string" - }, - "template": { - "type": "object", - "additionalProperties": false, - "patternProperties": { - "^.*$": { - "type": "string" - } - } - }, - "tasks": { - "type": "object", - "items": { - "type": "object", - "properties": { - "name": {"type": "string"}, - "icon": {"type": "string"}, - "group": {"type": "string"}, - "label": {"type": "string"} - }, - "required": [ - "short_name" - ] - } - }, - "apps": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": {"type": "string"}, - "icon": {"type": "string"}, - "group": {"type": "string"}, - "label": {"type": "string"} - }, - "required": ["name"] - } - }, - "families": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": {"type": "string"}, - "icon": {"type": "string"}, - "label": {"type": "string"}, - "hideFilter": {"type": "boolean"} - }, - "required": ["name"] - } - }, - "groups": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": {"type": "string"}, - "icon": {"type": "string"}, - "color": {"type": "string"}, - "order": {"type": ["integer", "number"]} - }, - "required": ["name"] - } - }, - "copy": { - "type": "object" - } - } -} diff --git a/client/ayon_core/pipeline/schema/config-2.0.json b/client/ayon_core/pipeline/schema/config-2.0.json deleted file mode 100644 index 54b226711a..0000000000 --- a/client/ayon_core/pipeline/schema/config-2.0.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:config-2.0", - "description": "A project configuration.", - - "type": "object", - - "additionalProperties": false, - "required": [ - "tasks", - "apps" - ], - - "properties": { - "schema": { - "description": "Schema identifier for payload", - "type": "string" - }, - "templates": { - "type": "object" - }, - "roots": { - "type": "object" - }, - "imageio": { - "type": "object" - }, - "tasks": { - "type": "object", - "items": { - "type": "object", - "properties": { - "name": {"type": "string"}, - "icon": {"type": "string"}, - "group": {"type": "string"}, - "label": {"type": "string"} - }, - "required": [ - "short_name" - ] - } - }, - "apps": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": {"type": "string"}, - "icon": {"type": "string"}, - "group": {"type": "string"}, - "label": {"type": "string"} - }, - "required": ["name"] - } - }, - "families": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": {"type": "string"}, - "icon": {"type": "string"}, - "label": {"type": "string"}, - "hideFilter": {"type": "boolean"} - }, - "required": ["name"] - } - }, - "groups": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": {"type": "string"}, - "icon": {"type": "string"}, - "color": {"type": "string"}, - "order": {"type": ["integer", "number"]} - }, - "required": ["name"] - } - }, - "copy": { - "type": "object" - } - } -} diff --git a/client/ayon_core/pipeline/schema/hero_version-1.0.json b/client/ayon_core/pipeline/schema/hero_version-1.0.json deleted file mode 100644 index b720dc2887..0000000000 --- a/client/ayon_core/pipeline/schema/hero_version-1.0.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:hero_version-1.0", - "description": "Hero version of asset", - - "type": "object", - - "additionalProperties": true, - - "required": [ - "version_id", - "schema", - "type", - "parent" - ], - - "properties": { - "_id": { - "description": "Document's id (database will create it's if not entered)", - "example": "ObjectId(592c33475f8c1b064c4d1696)" - }, - "version_id": { - "description": "The version ID from which it was created", - "example": "ObjectId(592c33475f8c1b064c4d1695)" - }, - "schema": { - "description": "The schema associated with this document", - "type": "string", - "enum": ["openpype:hero_version-1.0"], - "example": "openpype:hero_version-1.0" - }, - "type": { - "description": "The type of document", - "type": "string", - "enum": ["hero_version"], - "example": "hero_version" - }, - "parent": { - "description": "Unique identifier to parent document", - "example": "ObjectId(592c33475f8c1b064c4d1697)" - } - } -} diff --git a/client/ayon_core/pipeline/schema/inventory-1.0.json b/client/ayon_core/pipeline/schema/inventory-1.0.json deleted file mode 100644 index 2fe78794ab..0000000000 --- a/client/ayon_core/pipeline/schema/inventory-1.0.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:config-1.0", - "description": "A project configuration.", - - "type": "object", - - "additionalProperties": true -} diff --git a/client/ayon_core/pipeline/schema/inventory-1.1.json b/client/ayon_core/pipeline/schema/inventory-1.1.json deleted file mode 100644 index b61a76b32a..0000000000 --- a/client/ayon_core/pipeline/schema/inventory-1.1.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:config-1.1", - "description": "A project configuration.", - - "type": "object", - - "additionalProperties": true -} diff --git a/client/ayon_core/pipeline/schema/project-2.0.json b/client/ayon_core/pipeline/schema/project-2.0.json deleted file mode 100644 index 0ed5a55599..0000000000 --- a/client/ayon_core/pipeline/schema/project-2.0.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:project-2.0", - "description": "A unit of data", - - "type": "object", - - "additionalProperties": true, - - "required": [ - "schema", - "type", - "name", - "data", - "config" - ], - - "properties": { - "schema": { - "description": "Schema identifier for payload", - "type": "string", - "enum": ["openpype:project-2.0"], - "example": "openpype:project-2.0" - }, - "type": { - "description": "The type of document", - "type": "string", - "enum": ["project"], - "example": "project" - }, - "parent": { - "description": "Unique identifier to parent document", - "example": "592c33475f8c1b064c4d1696" - }, - "name": { - "description": "Name of directory", - "type": "string", - "pattern": "^[a-zA-Z0-9_.]*$", - "example": "hulk" - }, - "data": { - "description": "Document metadata", - "type": "object", - "example": { - "fps": 24, - "width": 1920, - "height": 1080 - } - }, - "config": { - "type": "object", - "description": "Document metadata", - "example": { - "schema": "openpype:config-1.0", - "apps": [ - { - "name": "maya2016", - "label": "Autodesk Maya 2016" - }, - { - "name": "nuke10", - "label": "The Foundry Nuke 10.0" - } - ], - "tasks": [ - {"name": "model"}, - {"name": "render"}, - {"name": "animate"}, - {"name": "rig"}, - {"name": "lookdev"}, - {"name": "layout"} - ], - "template": { - "work": - "{root}/{project}/{silo}/{asset}/work/{task}/{app}", - "publish": - "{root}/{project}/{silo}/{asset}/publish/{subset}/v{version:0>3}/{subset}.{representation}" - } - }, - "$ref": "config-1.0.json" - } - }, - - "definitions": {} -} diff --git a/client/ayon_core/pipeline/schema/project-2.1.json b/client/ayon_core/pipeline/schema/project-2.1.json deleted file mode 100644 index 9413c9f691..0000000000 --- a/client/ayon_core/pipeline/schema/project-2.1.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:project-2.1", - "description": "A unit of data", - - "type": "object", - - "additionalProperties": true, - - "required": [ - "schema", - "type", - "name", - "data", - "config" - ], - - "properties": { - "schema": { - "description": "Schema identifier for payload", - "type": "string", - "enum": ["openpype:project-2.1"], - "example": "openpype:project-2.1" - }, - "type": { - "description": "The type of document", - "type": "string", - "enum": ["project"], - "example": "project" - }, - "parent": { - "description": "Unique identifier to parent document", - "example": "592c33475f8c1b064c4d1696" - }, - "name": { - "description": "Name of directory", - "type": "string", - "pattern": "^[a-zA-Z0-9_.]*$", - "example": "hulk" - }, - "data": { - "description": "Document metadata", - "type": "object", - "example": { - "fps": 24, - "width": 1920, - "height": 1080 - } - }, - "config": { - "type": "object", - "description": "Document metadata", - "example": { - "schema": "openpype:config-1.1", - "apps": [ - { - "name": "maya2016", - "label": "Autodesk Maya 2016" - }, - { - "name": "nuke10", - "label": "The Foundry Nuke 10.0" - } - ], - "tasks": { - "Model": {"short_name": "mdl"}, - "Render": {"short_name": "rnd"}, - "Animate": {"short_name": "anim"}, - "Rig": {"short_name": "rig"}, - "Lookdev": {"short_name": "look"}, - "Layout": {"short_name": "lay"} - }, - "template": { - "work": - "{root}/{project}/{silo}/{asset}/work/{task}/{app}", - "publish": - "{root}/{project}/{silo}/{asset}/publish/{subset}/v{version:0>3}/{subset}.{representation}" - } - }, - "$ref": "config-1.1.json" - } - }, - - "definitions": {} -} diff --git a/client/ayon_core/pipeline/schema/project-3.0.json b/client/ayon_core/pipeline/schema/project-3.0.json deleted file mode 100644 index be23e10c93..0000000000 --- a/client/ayon_core/pipeline/schema/project-3.0.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:project-3.0", - "description": "A unit of data", - - "type": "object", - - "additionalProperties": true, - - "required": [ - "schema", - "type", - "name", - "data", - "config" - ], - - "properties": { - "schema": { - "description": "Schema identifier for payload", - "type": "string", - "enum": ["openpype:project-3.0"], - "example": "openpype:project-3.0" - }, - "type": { - "description": "The type of document", - "type": "string", - "enum": ["project"], - "example": "project" - }, - "parent": { - "description": "Unique identifier to parent document", - "example": "592c33475f8c1b064c4d1696" - }, - "name": { - "description": "Name of directory", - "type": "string", - "pattern": "^[a-zA-Z0-9_.]*$", - "example": "hulk" - }, - "data": { - "description": "Document metadata", - "type": "object", - "example": { - "fps": 24, - "width": 1920, - "height": 1080 - } - }, - "config": { - "type": "object", - "description": "Document metadata", - "$ref": "config-2.0.json" - } - }, - - "definitions": {} -} diff --git a/client/ayon_core/pipeline/schema/representation-1.0.json b/client/ayon_core/pipeline/schema/representation-1.0.json deleted file mode 100644 index 347c585f52..0000000000 --- a/client/ayon_core/pipeline/schema/representation-1.0.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:representation-1.0", - "description": "The inverse of an instance", - - "type": "object", - - "additionalProperties": true, - - "required": [ - "schema", - "format", - "path" - ], - - "properties": { - "schema": {"type": "string"}, - "format": { - "description": "File extension, including '.'", - "type": "string" - }, - "path": { - "description": "Unformatted path to version.", - "type": "string" - } - } -} diff --git a/client/ayon_core/pipeline/schema/representation-2.0.json b/client/ayon_core/pipeline/schema/representation-2.0.json deleted file mode 100644 index f47c16a10a..0000000000 --- a/client/ayon_core/pipeline/schema/representation-2.0.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:representation-2.0", - "description": "The inverse of an instance", - - "type": "object", - - "additionalProperties": true, - - "required": [ - "schema", - "type", - "parent", - "name", - "data" - ], - - "properties": { - "schema": { - "description": "Schema identifier for payload", - "type": "string", - "enum": ["openpype:representation-2.0"], - "example": "openpype:representation-2.0" - }, - "type": { - "description": "The type of document", - "type": "string", - "enum": ["representation"], - "example": "representation" - }, - "parent": { - "description": "Unique identifier to parent document", - "example": "592c33475f8c1b064c4d1696" - }, - "name": { - "description": "Name of representation", - "type": "string", - "pattern": "^[a-zA-Z0-9_.]*$", - "example": "abc" - }, - "data": { - "description": "Document metadata", - "type": "object", - "example": { - "label": "Alembic" - } - }, - "dependencies": { - "description": "Other representation that this representation depends on", - "type": "array", - "items": {"type": "string"}, - "example": [ - "592d547a5f8c1b388093c145" - ] - }, - "context": { - "description": "Summary of the context to which this representation belong.", - "type": "object", - "properties": { - "project": {"type": "object"}, - "asset": {"type": "string"}, - "silo": {"type": ["string", "null"]}, - "subset": {"type": "string"}, - "version": {"type": "number"}, - "representation": {"type": "string"} - }, - "example": { - "project": "hulk", - "asset": "Bruce", - "silo": "assets", - "subset": "rigDefault", - "version": 12, - "representation": "ma" - } - } - } -} diff --git a/client/ayon_core/pipeline/schema/session-1.0.json b/client/ayon_core/pipeline/schema/session-1.0.json deleted file mode 100644 index 5ced0a6f08..0000000000 --- a/client/ayon_core/pipeline/schema/session-1.0.json +++ /dev/null @@ -1,143 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:session-1.0", - "description": "The Avalon environment", - - "type": "object", - - "additionalProperties": true, - - "required": [ - "AVALON_PROJECTS", - "AVALON_PROJECT", - "AVALON_ASSET", - "AVALON_SILO", - "AVALON_CONFIG" - ], - - "properties": { - "AVALON_PROJECTS": { - "description": "Absolute path to root of project directories", - "type": "string", - "example": "/nas/projects" - }, - "AVALON_PROJECT": { - "description": "Name of project", - "type": "string", - "pattern": "^\\w*$", - "example": "Hulk" - }, - "AVALON_ASSET": { - "description": "Name of asset", - "type": "string", - "pattern": "^\\w*$", - "example": "Bruce" - }, - "AVALON_SILO": { - "description": "Name of asset group or container", - "type": "string", - "pattern": "^\\w*$", - "example": "assets" - }, - "AVALON_TASK": { - "description": "Name of task", - "type": "string", - "pattern": "^\\w*$", - "example": "modeling" - }, - "AVALON_CONFIG": { - "description": "Name of Avalon configuration", - "type": "string", - "pattern": "^\\w*$", - "example": "polly" - }, - "AVALON_APP": { - "description": "Name of application", - "type": "string", - "pattern": "^\\w*$", - "example": "maya2016" - }, - "AVALON_MONGO": { - "description": "Address to the asset database", - "type": "string", - "pattern": "^mongodb://[\\w/@:.]*$", - "example": "mongodb://localhost:27017", - "default": "mongodb://localhost:27017" - }, - "AVALON_DB": { - "description": "Name of database", - "type": "string", - "pattern": "^\\w*$", - "example": "avalon", - "default": "avalon" - }, - "AVALON_LABEL": { - "description": "Nice name of Avalon, used in e.g. graphical user interfaces", - "type": "string", - "example": "Mindbender", - "default": "Avalon" - }, - "AVALON_SENTRY": { - "description": "Address to Sentry", - "type": "string", - "pattern": "^http[\\w/@:.]*$", - "example": "https://5b872b280de742919b115bdc8da076a5:8d278266fe764361b8fa6024af004a9c@logs.mindbender.com/2", - "default": null - }, - "AVALON_DEADLINE": { - "description": "Address to Deadline", - "type": "string", - "pattern": "^http[\\w/@:.]*$", - "example": "http://192.168.99.101", - "default": null - }, - "AVALON_TIMEOUT": { - "description": "Wherever there is a need for a timeout, this is the default value.", - "type": "string", - "pattern": "^[0-9]*$", - "default": "1000", - "example": "1000" - }, - "AVALON_UPLOAD": { - "description": "Boolean of whether to upload published material to central asset repository", - "type": "string", - "default": null, - "example": "True" - }, - "AVALON_USERNAME": { - "description": "Generic username", - "type": "string", - "pattern": "^\\w*$", - "default": "avalon", - "example": "myself" - }, - "AVALON_PASSWORD": { - "description": "Generic password", - "type": "string", - "pattern": "^\\w*$", - "default": "secret", - "example": "abc123" - }, - "AVALON_INSTANCE_ID": { - "description": "Unique identifier for instances in a working file", - "type": "string", - "pattern": "^[\\w.]*$", - "default": "avalon.instance", - "example": "avalon.instance" - }, - "AVALON_CONTAINER_ID": { - "description": "Unique identifier for a loaded representation in a working file", - "type": "string", - "pattern": "^[\\w.]*$", - "default": "avalon.container", - "example": "avalon.container" - }, - "AVALON_DEBUG": { - "description": "Enable debugging mode. Some applications may use this for e.g. extended verbosity or mock plug-ins.", - "type": "string", - "default": null, - "example": "True" - } - } -} diff --git a/client/ayon_core/pipeline/schema/session-2.0.json b/client/ayon_core/pipeline/schema/session-2.0.json deleted file mode 100644 index 0a4d51beb2..0000000000 --- a/client/ayon_core/pipeline/schema/session-2.0.json +++ /dev/null @@ -1,134 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:session-2.0", - "description": "The Avalon environment", - - "type": "object", - - "additionalProperties": true, - - "required": [ - "AVALON_PROJECT", - "AVALON_ASSET", - "AVALON_CONFIG" - ], - - "properties": { - "AVALON_PROJECTS": { - "description": "Absolute path to root of project directories", - "type": "string", - "example": "/nas/projects" - }, - "AVALON_PROJECT": { - "description": "Name of project", - "type": "string", - "pattern": "^\\w*$", - "example": "Hulk" - }, - "AVALON_ASSET": { - "description": "Name of asset", - "type": "string", - "pattern": "^\\w*$", - "example": "Bruce" - }, - "AVALON_SILO": { - "description": "Name of asset group or container", - "type": "string", - "pattern": "^\\w*$", - "example": "assets" - }, - "AVALON_TASK": { - "description": "Name of task", - "type": "string", - "pattern": "^\\w*$", - "example": "modeling" - }, - "AVALON_CONFIG": { - "description": "Name of Avalon configuration", - "type": "string", - "pattern": "^\\w*$", - "example": "polly" - }, - "AVALON_APP": { - "description": "Name of application", - "type": "string", - "pattern": "^\\w*$", - "example": "maya2016" - }, - "AVALON_DB": { - "description": "Name of database", - "type": "string", - "pattern": "^\\w*$", - "example": "avalon", - "default": "avalon" - }, - "AVALON_LABEL": { - "description": "Nice name of Avalon, used in e.g. graphical user interfaces", - "type": "string", - "example": "Mindbender", - "default": "Avalon" - }, - "AVALON_SENTRY": { - "description": "Address to Sentry", - "type": "string", - "pattern": "^http[\\w/@:.]*$", - "example": "https://5b872b280de742919b115bdc8da076a5:8d278266fe764361b8fa6024af004a9c@logs.mindbender.com/2", - "default": null - }, - "AVALON_DEADLINE": { - "description": "Address to Deadline", - "type": "string", - "pattern": "^http[\\w/@:.]*$", - "example": "http://192.168.99.101", - "default": null - }, - "AVALON_TIMEOUT": { - "description": "Wherever there is a need for a timeout, this is the default value.", - "type": "string", - "pattern": "^[0-9]*$", - "default": "1000", - "example": "1000" - }, - "AVALON_UPLOAD": { - "description": "Boolean of whether to upload published material to central asset repository", - "type": "string", - "default": null, - "example": "True" - }, - "AVALON_USERNAME": { - "description": "Generic username", - "type": "string", - "pattern": "^\\w*$", - "default": "avalon", - "example": "myself" - }, - "AVALON_PASSWORD": { - "description": "Generic password", - "type": "string", - "pattern": "^\\w*$", - "default": "secret", - "example": "abc123" - }, - "AVALON_INSTANCE_ID": { - "description": "Unique identifier for instances in a working file", - "type": "string", - "pattern": "^[\\w.]*$", - "default": "avalon.instance", - "example": "avalon.instance" - }, - "AVALON_CONTAINER_ID": { - "description": "Unique identifier for a loaded representation in a working file", - "type": "string", - "pattern": "^[\\w.]*$", - "default": "avalon.container", - "example": "avalon.container" - }, - "AVALON_DEBUG": { - "description": "Enable debugging mode. Some applications may use this for e.g. extended verbosity or mock plug-ins.", - "type": "string", - "default": null, - "example": "True" - } - } -} diff --git a/client/ayon_core/pipeline/schema/session-3.0.json b/client/ayon_core/pipeline/schema/session-3.0.json deleted file mode 100644 index 9f785939e4..0000000000 --- a/client/ayon_core/pipeline/schema/session-3.0.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:session-3.0", - "description": "The Avalon environment", - - "type": "object", - - "additionalProperties": true, - - "required": [ - "AVALON_PROJECT", - "AVALON_ASSET" - ], - - "properties": { - "AVALON_PROJECTS": { - "description": "Absolute path to root of project directories", - "type": "string", - "example": "/nas/projects" - }, - "AVALON_PROJECT": { - "description": "Name of project", - "type": "string", - "pattern": "^\\w*$", - "example": "Hulk" - }, - "AVALON_ASSET": { - "description": "Name of asset", - "type": "string", - "pattern": "^\\w*$", - "example": "Bruce" - }, - "AVALON_TASK": { - "description": "Name of task", - "type": "string", - "pattern": "^\\w*$", - "example": "modeling" - }, - "AVALON_APP": { - "description": "Name of host", - "type": "string", - "pattern": "^\\w*$", - "example": "maya2016" - }, - "AVALON_DB": { - "description": "Name of database", - "type": "string", - "pattern": "^\\w*$", - "example": "avalon", - "default": "avalon" - }, - "AVALON_LABEL": { - "description": "Nice name of Avalon, used in e.g. graphical user interfaces", - "type": "string", - "example": "Mindbender", - "default": "Avalon" - }, - "AVALON_TIMEOUT": { - "description": "Wherever there is a need for a timeout, this is the default value.", - "type": "string", - "pattern": "^[0-9]*$", - "default": "1000", - "example": "1000" - }, - "AVALON_INSTANCE_ID": { - "description": "Unique identifier for instances in a working file", - "type": "string", - "pattern": "^[\\w.]*$", - "default": "avalon.instance", - "example": "avalon.instance" - }, - "AVALON_CONTAINER_ID": { - "description": "Unique identifier for a loaded representation in a working file", - "type": "string", - "pattern": "^[\\w.]*$", - "default": "avalon.container", - "example": "avalon.container" - } - } -} diff --git a/client/ayon_core/pipeline/schema/session-4.0.json b/client/ayon_core/pipeline/schema/session-4.0.json deleted file mode 100644 index 0dab48aa46..0000000000 --- a/client/ayon_core/pipeline/schema/session-4.0.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:session-4.0", - "description": "The Avalon environment", - - "type": "object", - - "additionalProperties": true, - - "required": [ - "AVALON_PROJECT" - ], - - "properties": { - "AVALON_PROJECT": { - "description": "Name of project", - "type": "string", - "pattern": "^\\w*$", - "example": "Hulk" - }, - "AVALON_ASSET": { - "description": "Name of asset", - "type": "string", - "pattern": "^[\\/\\w]*$", - "example": "Bruce" - }, - "AVALON_TASK": { - "description": "Name of task", - "type": "string", - "pattern": "^\\w*$", - "example": "modeling" - }, - "AVALON_APP": { - "description": "Name of host", - "type": "string", - "pattern": "^\\w*$", - "example": "maya" - }, - "AVALON_DB": { - "description": "Name of database", - "type": "string", - "pattern": "^\\w*$", - "example": "avalon", - "default": "avalon" - }, - "AVALON_LABEL": { - "description": "Nice name of Avalon, used in e.g. graphical user interfaces", - "type": "string", - "example": "MyLabel", - "default": "Avalon" - }, - "AVALON_TIMEOUT": { - "description": "Wherever there is a need for a timeout, this is the default value.", - "type": "string", - "pattern": "^[0-9]*$", - "default": "1000", - "example": "1000" - } - } -} diff --git a/client/ayon_core/pipeline/schema/shaders-1.0.json b/client/ayon_core/pipeline/schema/shaders-1.0.json deleted file mode 100644 index 7102ba1861..0000000000 --- a/client/ayon_core/pipeline/schema/shaders-1.0.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:shaders-1.0", - "description": "Relationships between shaders and Avalon IDs", - - "type": "object", - - "additionalProperties": true, - - "required": [ - "schema", - "shader" - ], - - "properties": { - "schema": { - "description": "Schema identifier for payload", - "type": "string" - }, - "shader": { - "description": "Name of directory", - "type": "array", - "items": { - "type": "str", - "description": "Avalon ID and optional face indexes, e.g. 'f9520572-ac1d-11e6-b39e-3085a99791c9.f[5002:5185]'" - } - } - }, - - "definitions": {} -} diff --git a/client/ayon_core/pipeline/schema/subset-1.0.json b/client/ayon_core/pipeline/schema/subset-1.0.json deleted file mode 100644 index a299a6d341..0000000000 --- a/client/ayon_core/pipeline/schema/subset-1.0.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:subset-1.0", - "description": "A container of instances", - - "type": "object", - - "additionalProperties": true, - - "required": [ - "schema", - "name", - "versions" - ], - - "properties": { - "schema": { - "description": "Schema identifier for payload", - "type": "string" - }, - "name": { - "description": "Name of directory", - "type": "string" - }, - "versions": { - "type": "array", - "items": { - "$ref": "version.json" - } - } - }, - - "definitions": {} -} diff --git a/client/ayon_core/pipeline/schema/subset-2.0.json b/client/ayon_core/pipeline/schema/subset-2.0.json deleted file mode 100644 index db256ec7fb..0000000000 --- a/client/ayon_core/pipeline/schema/subset-2.0.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:subset-2.0", - "description": "A container of instances", - - "type": "object", - - "additionalProperties": true, - - "required": [ - "schema", - "type", - "parent", - "name", - "data" - ], - - "properties": { - "schema": { - "description": "The schema associated with this document", - "type": "string", - "enum": ["openpype:subset-2.0"], - "example": "openpype:subset-2.0" - }, - "type": { - "description": "The type of document", - "type": "string", - "enum": ["subset"], - "example": "subset" - }, - "parent": { - "description": "Unique identifier to parent document", - "example": "592c33475f8c1b064c4d1696" - }, - "name": { - "description": "Name of directory", - "type": "string", - "pattern": "^[a-zA-Z0-9_.]*$", - "example": "shot01" - }, - "data": { - "type": "object", - "description": "Document metadata", - "example": { - "frameStart": 1000, - "frameEnd": 1201 - } - } - } -} diff --git a/client/ayon_core/pipeline/schema/subset-3.0.json b/client/ayon_core/pipeline/schema/subset-3.0.json deleted file mode 100644 index 1a0db53c04..0000000000 --- a/client/ayon_core/pipeline/schema/subset-3.0.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:subset-3.0", - "description": "A container of instances", - - "type": "object", - - "additionalProperties": true, - - "required": [ - "schema", - "type", - "parent", - "name", - "data" - ], - - "properties": { - "schema": { - "description": "The schema associated with this document", - "type": "string", - "enum": ["openpype:subset-3.0"], - "example": "openpype:subset-3.0" - }, - "type": { - "description": "The type of document", - "type": "string", - "enum": ["subset"], - "example": "subset" - }, - "parent": { - "description": "Unique identifier to parent document", - "example": "592c33475f8c1b064c4d1696" - }, - "name": { - "description": "Name of directory", - "type": "string", - "pattern": "^[a-zA-Z0-9_.]*$", - "example": "shot01" - }, - "data": { - "description": "Document metadata", - "type": "object", - "required": ["families"], - "properties": { - "families": { - "type": "array", - "items": {"type": "string"}, - "description": "One or more families associated with this subset" - } - }, - "example": { - "families" : [ - "avalon.camera" - ], - "frameStart": 1000, - "frameEnd": 1201 - } - } - } -} diff --git a/client/ayon_core/pipeline/schema/thumbnail-1.0.json b/client/ayon_core/pipeline/schema/thumbnail-1.0.json deleted file mode 100644 index 5bdf78a4b1..0000000000 --- a/client/ayon_core/pipeline/schema/thumbnail-1.0.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:thumbnail-1.0", - "description": "Entity with thumbnail data", - - "type": "object", - - "additionalProperties": true, - - "required": [ - "schema", - "type", - "data" - ], - - "properties": { - "schema": { - "description": "The schema associated with this document", - "type": "string", - "enum": ["openpype:thumbnail-1.0"], - "example": "openpype:thumbnail-1.0" - }, - "type": { - "description": "The type of document", - "type": "string", - "enum": ["thumbnail"], - "example": "thumbnail" - }, - "data": { - "description": "Thumbnail data", - "type": "object", - "example": { - "binary_data": "Binary({byte data of image})", - "template": "{thumbnail_root}/{project[name]}/{_id}{ext}}", - "template_data": { - "ext": ".jpg" - } - } - } - } -} diff --git a/client/ayon_core/pipeline/schema/version-1.0.json b/client/ayon_core/pipeline/schema/version-1.0.json deleted file mode 100644 index daa1997721..0000000000 --- a/client/ayon_core/pipeline/schema/version-1.0.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:version-1.0", - "description": "An individual version", - - "type": "object", - - "additionalProperties": true, - - "required": [ - "schema", - "version", - "path", - "time", - "author", - "source", - "representations" - ], - - "properties": { - "schema": {"type": "string"}, - "representations": { - "type": "array", - "items": { - "$ref": "representation.json" - } - }, - "time": { - "description": "ISO formatted, file-system compatible time", - "type": "string" - }, - "author": { - "description": "User logged on to the machine at time of publish", - "type": "string" - }, - "version": { - "description": "Number of this version", - "type": "number" - }, - "path": { - "description": "Unformatted path, e.g. '{root}/assets/Bruce/publish/lookdevDefault/v001", - "type": "string" - }, - "source": { - "description": "Original file from which this version was made.", - "type": "string" - } - } -} diff --git a/client/ayon_core/pipeline/schema/version-2.0.json b/client/ayon_core/pipeline/schema/version-2.0.json deleted file mode 100644 index 099e9be70a..0000000000 --- a/client/ayon_core/pipeline/schema/version-2.0.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:version-2.0", - "description": "An individual version", - - "type": "object", - - "additionalProperties": true, - - "required": [ - "schema", - "type", - "parent", - "name", - "data" - ], - - "properties": { - "schema": { - "description": "The schema associated with this document", - "type": "string", - "enum": ["openpype:version-2.0"], - "example": "openpype:version-2.0" - }, - "type": { - "description": "The type of document", - "type": "string", - "enum": ["version"], - "example": "version" - }, - "parent": { - "description": "Unique identifier to parent document", - "example": "592c33475f8c1b064c4d1696" - }, - "name": { - "description": "Number of version", - "type": "number", - "example": 12 - }, - "locations": { - "description": "Where on the planet this version can be found.", - "type": "array", - "items": {"type": "string"}, - "example": ["data.avalon.com"] - }, - "data": { - "description": "Document metadata", - "type": "object", - "required": ["families", "author", "source", "time"], - "properties": { - "time": { - "description": "ISO formatted, file-system compatible time", - "type": "string" - }, - "timeFormat": { - "description": "ISO format of time", - "type": "string" - }, - "author": { - "description": "User logged on to the machine at time of publish", - "type": "string" - }, - "version": { - "description": "Number of this version", - "type": "number" - }, - "path": { - "description": "Unformatted path, e.g. '{root}/assets/Bruce/publish/lookdevDefault/v001", - "type": "string" - }, - "source": { - "description": "Original file from which this version was made.", - "type": "string" - }, - "families": { - "type": "array", - "items": {"type": "string"}, - "description": "One or more families associated with this version" - } - }, - "example": { - "source" : "{root}/f02_prod/assets/BubbleWitch/work/modeling/marcus/maya/scenes/model_v001.ma", - "author" : "marcus", - "families" : [ - "avalon.model" - ], - "time" : "20170510T090203Z" - } - } - } -} diff --git a/client/ayon_core/pipeline/schema/version-3.0.json b/client/ayon_core/pipeline/schema/version-3.0.json deleted file mode 100644 index 3e07fc4499..0000000000 --- a/client/ayon_core/pipeline/schema/version-3.0.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:version-3.0", - "description": "An individual version", - - "type": "object", - - "additionalProperties": true, - - "required": [ - "schema", - "type", - "parent", - "name", - "data" - ], - - "properties": { - "schema": { - "description": "The schema associated with this document", - "type": "string", - "enum": ["openpype:version-3.0"], - "example": "openpype:version-3.0" - }, - "type": { - "description": "The type of document", - "type": "string", - "enum": ["version"], - "example": "version" - }, - "parent": { - "description": "Unique identifier to parent document", - "example": "592c33475f8c1b064c4d1696" - }, - "name": { - "description": "Number of version", - "type": "number", - "example": 12 - }, - "locations": { - "description": "Where on the planet this version can be found.", - "type": "array", - "items": {"type": "string"}, - "example": ["data.avalon.com"] - }, - "data": { - "description": "Document metadata", - "type": "object", - "required": ["author", "source", "time"], - "properties": { - "time": { - "description": "ISO formatted, file-system compatible time", - "type": "string" - }, - "timeFormat": { - "description": "ISO format of time", - "type": "string" - }, - "author": { - "description": "User logged on to the machine at time of publish", - "type": "string" - }, - "version": { - "description": "Number of this version", - "type": "number" - }, - "path": { - "description": "Unformatted path, e.g. '{root}/assets/Bruce/publish/lookdevDefault/v001", - "type": "string" - }, - "source": { - "description": "Original file from which this version was made.", - "type": "string" - } - }, - "example": { - "source" : "{root}/f02_prod/assets/BubbleWitch/work/modeling/marcus/maya/scenes/model_v001.ma", - "author" : "marcus", - "time" : "20170510T090203Z" - } - } - } -} diff --git a/client/ayon_core/pipeline/schema/workfile-1.0.json b/client/ayon_core/pipeline/schema/workfile-1.0.json deleted file mode 100644 index 5f9600ef20..0000000000 --- a/client/ayon_core/pipeline/schema/workfile-1.0.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - - "title": "openpype:workfile-1.0", - "description": "Workfile additional information.", - - "type": "object", - - "additionalProperties": true, - - "required": [ - "schema", - "type", - "filename", - "task_name", - "parent" - ], - - "properties": { - "schema": { - "description": "Schema identifier for payload", - "type": "string", - "enum": ["openpype:workfile-1.0"], - "example": "openpype:workfile-1.0" - }, - "type": { - "description": "The type of document", - "type": "string", - "enum": ["workfile"], - "example": "workfile" - }, - "parent": { - "description": "Unique identifier to parent document", - "example": "592c33475f8c1b064c4d1696" - }, - "filename": { - "description": "Workfile's filename", - "type": "string", - "example": "kuba_each_case_Alpaca_01_animation_v001.ma" - }, - "task_name": { - "description": "Task name", - "type": "string", - "example": "animation" - }, - "data": { - "description": "Document metadata", - "type": "object", - "example": {"key": "value"} - } - } -} From 26960b789f00c308dc6fabb680c3926c34b6218e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 8 Mar 2024 18:07:37 +0100 Subject: [PATCH 509/573] remove unused 'get_schema_version' --- client/ayon_core/pipeline/schema/__init__.py | 28 -------------------- 1 file changed, 28 deletions(-) diff --git a/client/ayon_core/pipeline/schema/__init__.py b/client/ayon_core/pipeline/schema/__init__.py index d7b33f2621..3abc576f89 100644 --- a/client/ayon_core/pipeline/schema/__init__.py +++ b/client/ayon_core/pipeline/schema/__init__.py @@ -29,34 +29,6 @@ CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) _CACHED = False -def get_schema_version(schema_name): - """Extract version form schema name. - - It is expected that schema name contain only major and minor version. - - Expected name should match to: - "{name}:{type}-{major version}.{minor version}" - - `name` - must not contain colon - - `type` - must not contain dash - - major and minor versions must be numbers separated by dot - - Args: - schema_name(str): Name of schema that should be parsed. - - Returns: - tuple: Contain two values major version as first and minor version as - second. When schema does not match parsing regex then `(0, 0)` is - returned. - """ - schema_regex = re.compile(r"[^:]+:[^-]+-(\d.\d)") - groups = schema_regex.findall(schema_name) - if not groups: - return 0, 0 - - maj_version, min_version = groups[0].split(".") - return int(maj_version), int(min_version) - - def validate(data, schema=None): """Validate `data` with `schema` From a35db9aaebeba9644b8c0f6dac21ea90a67abb20 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 8 Mar 2024 22:21:41 +0100 Subject: [PATCH 510/573] Add option to compress .blend exports - fix #156 --- .../blender/plugins/publish/extract_blend.py | 5 ++++- .../publish/extract_blend_animation.py | 5 ++++- .../server/settings/publish_plugins.py | 19 +++++++++++++++---- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_blend.py b/client/ayon_core/hosts/blender/plugins/publish/extract_blend.py index dd2e33df80..731e91ad76 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_blend.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_blend.py @@ -13,6 +13,9 @@ class ExtractBlend(publish.Extractor, publish.OptionalPyblishPluginMixin): families = ["model", "camera", "rig", "action", "layout", "blendScene"] optional = True + # From settings + compress = False + def process(self, instance): if not self.is_active(instance.data): return @@ -53,7 +56,7 @@ class ExtractBlend(publish.Extractor, publish.OptionalPyblishPluginMixin): if node.image and node.image.packed_file is None: node.image.pack() - bpy.data.libraries.write(filepath, data_blocks) + bpy.data.libraries.write(filepath, data_blocks, compress=self.compress) if "representations" not in instance.data: instance.data["representations"] = [] diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_blend_animation.py b/client/ayon_core/hosts/blender/plugins/publish/extract_blend_animation.py index da663b46ea..64009c4b6c 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_blend_animation.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_blend_animation.py @@ -16,6 +16,9 @@ class ExtractBlendAnimation( families = ["animation"] optional = True + # From settings + compress = False + def process(self, instance): if not self.is_active(instance.data): return @@ -46,7 +49,7 @@ class ExtractBlendAnimation( data_blocks.add(child.animation_data.action) data_blocks.add(obj) - bpy.data.libraries.write(filepath, data_blocks) + bpy.data.libraries.write(filepath, data_blocks, compress=self.compress) if "representations" not in instance.data: instance.data["representations"] = [] diff --git a/server_addon/blender/server/settings/publish_plugins.py b/server_addon/blender/server/settings/publish_plugins.py index c2a989dd55..79c489d080 100644 --- a/server_addon/blender/server/settings/publish_plugins.py +++ b/server_addon/blender/server/settings/publish_plugins.py @@ -44,6 +44,14 @@ class ExtractBlendModel(BaseSettingsModel): default_factory=list, title="Families" ) + compress: bool = SettingsField(True, title="Compress") + + +class ExtractBlendAnimationModel(BaseSettingsModel): + enabled: bool = SettingsField(True) + optional: bool = SettingsField(title="Optional") + active: bool = SettingsField(title="Active") + compress: bool = SettingsField(False, title="Compress") class ExtractPlayblastModel(BaseSettingsModel): @@ -51,6 +59,7 @@ class ExtractPlayblastModel(BaseSettingsModel): optional: bool = SettingsField(title="Optional") active: bool = SettingsField(title="Active") presets: str = SettingsField("", title="Presets", widget="textarea") + compress: bool = SettingsField(False, title="Compress") @validator("presets") def validate_json(cls, value): @@ -110,8 +119,8 @@ class PublishPuginsModel(BaseSettingsModel): default_factory=ValidatePluginModel, title="Extract ABC" ) - ExtractBlendAnimation: ValidatePluginModel = SettingsField( - default_factory=ValidatePluginModel, + ExtractBlendAnimation: ExtractBlendAnimationModel = SettingsField( + default_factory=ExtractBlendAnimationModel, title="Extract Blend Animation" ) ExtractAnimationFBX: ValidatePluginModel = SettingsField( @@ -198,7 +207,8 @@ DEFAULT_BLENDER_PUBLISH_SETTINGS = { "action", "layout", "blendScene" - ] + ], + "compress": False }, "ExtractFBX": { "enabled": False, @@ -213,7 +223,8 @@ DEFAULT_BLENDER_PUBLISH_SETTINGS = { "ExtractBlendAnimation": { "enabled": True, "optional": True, - "active": True + "active": True, + "compress": False }, "ExtractAnimationFBX": { "enabled": False, From 485b669389728e6c3a18a7ee15c6827408012f5d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 8 Mar 2024 22:22:05 +0100 Subject: [PATCH 511/573] Bump blender addon version --- server_addon/blender/server/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/blender/server/version.py b/server_addon/blender/server/version.py index 0a8da88258..f1380eede2 100644 --- a/server_addon/blender/server/version.py +++ b/server_addon/blender/server/version.py @@ -1 +1 @@ -__version__ = "0.1.6" +__version__ = "0.1.7" From b3048277e2d34054c1420c435c0800d8276d69ee Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Sat, 9 Mar 2024 22:18:52 +0800 Subject: [PATCH 512/573] make sure the code is also running when it is at headless mode --- .../hosts/max/plugins/load/load_max_scene.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py index f30d214cbe..3e8a918424 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py +++ b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py @@ -5,7 +5,8 @@ from ayon_core.hosts.max.api import lib from ayon_core.hosts.max.api.lib import ( unique_namespace, get_namespace, - object_transform_set + object_transform_set, + is_headless ) from ayon_core.hosts.max.api.pipeline import ( containerise, get_previous_loaded_object, @@ -129,9 +130,12 @@ class MaxSceneLoader(load.LoaderPlugin): for prev_max_obj in prev_max_objects: if rt.isValidNode(prev_max_obj): # noqa rt.Delete(prev_max_obj) - window = MaterialDupOptionsWindow(self.mtl_dup_enum) - window.exec_() - rt.MergeMaxFile(path, rt.Name(window.material_option), quiet=True) + material_option = self.mtl_dup_default + if not is_headless(): + window = MaterialDupOptionsWindow(self.mtl_dup_enum) + window.exec_() + material_option = window.material_option + rt.MergeMaxFile(path, rt.Name(material_option), quiet=True) current_max_objects = rt.getLastMergedNodes() From 98ee8a0486ab85bbb1232ba73d1b96136ee67652 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 6 Feb 2024 13:34:36 +0100 Subject: [PATCH 513/573] Add Validate Instance In Context validator in Houdini --- .../publish/validate_instance_in_context.py | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 client/ayon_core/hosts/houdini/plugins/publish/validate_instance_in_context.py diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_instance_in_context.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_instance_in_context.py new file mode 100644 index 0000000000..ac31254228 --- /dev/null +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_instance_in_context.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +"""Validate if instance asset is the same as context asset.""" + +import pyblish.api +from ayon_core.hosts.houdini.api.action import SelectROPAction +from ayon_core.pipeline.publish import ( + RepairAction, + ValidateContentsOrder, + PublishValidationError, + OptionalPyblishPluginMixin +) + + +class ValidateInstanceInContextHoudini(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): + """Validator to check if instance asset match context asset. + + When working in per-shot style you always publish data in context of + current asset (shot). This validator checks if this is so. It is optional + so it can be disabled when needed. + """ + # Similar to maya-equivalent `ValidateInstanceInContext` + + order = ValidateContentsOrder + label = "Instance in same Context" + optional = True + hosts = ["houdini"] + actions = [SelectROPAction, RepairAction] + + def process(self, instance): + if not self.is_active(instance.data): + return + + folderPath = instance.data.get("folderPath") + task = instance.data.get("task") + context = self.get_context(instance) + if (folderPath, task) != context: + context_label = "{} > {}".format(*context) + instance_label = "{} > {}".format(folderPath, task) + + raise PublishValidationError( + message=( + "Instance '{}' publishes to different asset than current " + "context: {}. Current context: {}".format( + instance.name, instance_label, context_label + ) + ), + description=( + "## Publishing to a different asset\n" + "There are publish instances present which are publishing " + "into a different asset than your current context.\n\n" + "Usually this is not what you want but there can be cases " + "where you might want to publish into another asset or " + "shot. If that's the case you can disable the validation " + "on the instance to ignore it." + ) + ) + + @classmethod + def repair(cls, instance): + context_asset, context_task = cls.get_context(instance) + + create_context = instance.context.data["create_context"] + instance_id = instance.data.get("instance_id") + created_instance = create_context.get_instance_by_id( + instance_id + ) + created_instance["folderPath"] = context_asset + created_instance["task"] = context_task + create_context.save_changes() + + @staticmethod + def get_context(instance): + """Return folderPath, task from publishing context data""" + context = instance.context + return context.data["folderPath"], context.data["task"] From a544ed4c4eadb561f0fd66538d60ce9427b5e2e0 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Mon, 11 Mar 2024 12:46:24 +0200 Subject: [PATCH 514/573] Add ValidateInstanceInContext settings --- server_addon/houdini/server/settings/publish.py | 8 ++++++++ server_addon/houdini/server/version.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/server_addon/houdini/server/settings/publish.py b/server_addon/houdini/server/settings/publish.py index 1741568d63..8e0e7f7795 100644 --- a/server_addon/houdini/server/settings/publish.py +++ b/server_addon/houdini/server/settings/publish.py @@ -53,6 +53,9 @@ class PublishPluginsModel(BaseSettingsModel): default_factory=BasicValidateModel, title="Validate Latest Containers.", section="Validators") + ValidateInstanceInContextHoudini: BasicValidateModel = SettingsField( + default_factory=BasicValidateModel, + title="Validate Instance is in same Context.") ValidateMeshIsStatic: BasicValidateModel = SettingsField( default_factory=BasicValidateModel, title="Validate Mesh is Static.") @@ -84,6 +87,11 @@ DEFAULT_HOUDINI_PUBLISH_SETTINGS = { "optional": True, "active": True }, + "ValidateInstanceInContextHoudini": { + "enabled": True, + "optional": True, + "active": True + }, "ValidateMeshIsStatic": { "enabled": True, "optional": True, diff --git a/server_addon/houdini/server/version.py b/server_addon/houdini/server/version.py index 5635676f6b..b5c9b6cb71 100644 --- a/server_addon/houdini/server/version.py +++ b/server_addon/houdini/server/version.py @@ -1 +1 @@ -__version__ = "0.2.11" +__version__ = "0.2.12" From d4a8ada5694378dc3d063cb92b38ab6f204b0043 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 11 Mar 2024 11:52:02 +0100 Subject: [PATCH 515/573] fix dictionary loop --- client/ayon_core/tools/loader/models/actions.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/ayon_core/tools/loader/models/actions.py b/client/ayon_core/tools/loader/models/actions.py index 39c0ca5693..aab5ba49d1 100644 --- a/client/ayon_core/tools/loader/models/actions.py +++ b/client/ayon_core/tools/loader/models/actions.py @@ -408,8 +408,7 @@ class LoaderActionsModel: project_entity = ayon_api.get_project(project_name) - for version_entity in version_entities_by_id: - version_id = version_entity["id"] + for version_id, version_entity in version_entities_by_id.items(): product_id = version_entity["productId"] product_entity = product_entities_by_id[product_id] folder_id = product_entity["folderId"] From a8f9ac5569a91ce042494591db30147dcf6f5b14 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 11 Mar 2024 11:53:57 +0100 Subject: [PATCH 516/573] @kalisp suggestions --- .../ayon_core/plugins/publish/collect_anatomy_instance_data.py | 2 +- .../ayon_core/plugins/publish/collect_scene_loaded_versions.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py index 80ea42dd6b..d6ac1544f5 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py @@ -132,7 +132,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): context_task_entity = context.data.get("taskEntity") context_task_name = None if context_task_entity: - context_task_name = context_task_entity["path"] + context_task_name = context_task_entity["name"] instances_missing_task = {} folder_path_by_id = {} diff --git a/client/ayon_core/plugins/publish/collect_scene_loaded_versions.py b/client/ayon_core/plugins/publish/collect_scene_loaded_versions.py index 7cbdd9c4ba..1267c009e7 100644 --- a/client/ayon_core/plugins/publish/collect_scene_loaded_versions.py +++ b/client/ayon_core/plugins/publish/collect_scene_loaded_versions.py @@ -57,7 +57,7 @@ class CollectSceneLoadedVersions(pyblish.api.ContextPlugin): for con in containers: repre_id = con["representation"] repre_entity = repre_entities_by_id.get(repre_id) - if repre_doc is None: + if repre_entity is None: self.log.warning(( "Skipping container," " did not find representation document. {}" From 7e9dfd5b47795f53cb2f038e57dcb19b49eeda34 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 11 Mar 2024 12:11:22 +0100 Subject: [PATCH 517/573] 'get_product_name' now expects 'task_name' and 'task_type' --- .../plugins/publish/collect_timeline_otio.py | 8 +++++++- .../harmony/plugins/publish/collect_workfile.py | 8 +++++++- .../plugins/publish/validate_subset_name.py | 17 ++++++++++++++--- client/ayon_core/hosts/maya/api/plugin.py | 7 ++++++- .../plugins/create/create_flatten_image.py | 10 +++++++++- .../plugins/publish/collect_auto_image.py | 7 ++++++- .../plugins/publish/collect_auto_review.py | 7 ++++++- .../plugins/publish/collect_auto_workfile.py | 7 ++++++- .../publish/collect_textureset_images.py | 12 +++++++++--- .../plugins/create/create_movie_batch.py | 15 ++++++++------- client/ayon_core/hosts/tvpaint/api/plugin.py | 7 ++++++- .../pipeline/create/creator_plugins.py | 9 ++++++++- .../ayon_core/pipeline/create/legacy_create.py | 8 ++++++-- .../ayon_core/pipeline/create/product_name.py | 11 ++++------- .../tools/push_to_project/models/integrate.py | 12 ++++++------ 15 files changed, 108 insertions(+), 37 deletions(-) diff --git a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_otio.py b/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_otio.py index 9c06a4c76b..7609ea7879 100644 --- a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_otio.py +++ b/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_otio.py @@ -22,9 +22,15 @@ class CollecTimelineOTIO(pyblish.api.ContextPlugin): sequence = opfapi.get_current_sequence(opfapi.CTX.selection) # create product name + task_entity = context.data["taskEntity"] + task_name = task_type = None + if task_entity: + task_name = task_entity["name"] + task_type = task_entity["taskType"] product_name = get_product_name( context.data["projectName"], - context.data["taskEntity"], + task_name, + task_type, context.data["hostName"], product_type, variant, diff --git a/client/ayon_core/hosts/harmony/plugins/publish/collect_workfile.py b/client/ayon_core/hosts/harmony/plugins/publish/collect_workfile.py index 79356b3a5f..488a7c4c71 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/collect_workfile.py +++ b/client/ayon_core/hosts/harmony/plugins/publish/collect_workfile.py @@ -17,9 +17,15 @@ class CollectWorkfile(pyblish.api.ContextPlugin): """Plugin entry point.""" product_type = "workfile" basename = os.path.basename(context.data["currentFile"]) + task_entity = context.data["taskEntity"] + task_name = task_type = None + if task_entity: + task_name = task_entity["name"] + task_type = task_entity["taskType"] product_name = get_product_name( context.data["projectName"], - context.data["taskEntity"], + task_name, + task_type, context.data["hostName"], product_type, "", diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py index 8e62b85650..0481929824 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py @@ -55,9 +55,15 @@ class ValidateSubsetName(pyblish.api.InstancePlugin, # Check product name folder_entity = instance.data["folderEntity"] + task_entity = instance.data["taskEntity"] + task_name = task_type = None + if task_entity: + task_name = task_entity["name"] + task_type = task_entity["taskType"] product_name = get_product_name( instance.context.data["projectName"], - instance.data["taskEntity"], + task_name, + task_type, instance.context.data["hostName"], instance.data["productType"], variant=instance.data["variant"], @@ -79,10 +85,15 @@ class ValidateSubsetName(pyblish.api.InstancePlugin, # Check product name folder_entity = instance.data["folderEntity"] + task_entity = instance.data["taskEntity"] + task_name = task_type = None + if task_entity: + task_name = task_entity["name"] + task_type = task_entity["taskType"] product_name = get_product_name( instance.context.data["projectName"], - instance.data["taskEntity"], - instance.data["task"], + task_name, + task_type, instance.context.data["hostName"], instance.data["productType"], variant=instance.data["variant"], diff --git a/client/ayon_core/hosts/maya/api/plugin.py b/client/ayon_core/hosts/maya/api/plugin.py index f2268088e6..eaf93725f4 100644 --- a/client/ayon_core/hosts/maya/api/plugin.py +++ b/client/ayon_core/hosts/maya/api/plugin.py @@ -600,10 +600,15 @@ class RenderlayerCreator(NewCreator, MayaCreatorBase): host_name, instance ) + task_name = task_type = None + if task_entity: + task_name = task_entity["name"] + task_type = task_entity["taskType"] # creator.product_type != 'render' as expected return get_product_name( project_name, - task_entity, + task_name, + task_type host_name, self.layer_instance_prefix or self.product_type, variant, diff --git a/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py b/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py index 84c5548e7b..a3bc77c640 100644 --- a/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py @@ -136,10 +136,18 @@ class AutoImageCreator(PSAutoCreator): ): if host_name is None: host_name = self.create_context.host_name + + task_name = task_type = None + if task_entity: + task_name = task_entity["name"] + task_type = task_entity["taskType"] + dynamic_data = prepare_template_data({"layer": "{layer}"}) + product_name = get_product_name( project_name, - task_entity, + task_name, + task_type, host_name, self.product_type, variant, diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image.py index ab8254d747..b488ab364d 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image.py @@ -27,6 +27,10 @@ class CollectAutoImage(pyblish.api.ContextPlugin): host_name = context.data["hostName"] folder_entity = context.data["folderEntity"] task_entity = context.data["taskEntity"] + task_name = task_type = None + if task_entity: + task_name = task_entity["name"] + task_type = task_entity["taskType"] auto_creator = proj_settings.get( "photoshop", {}).get( @@ -79,7 +83,8 @@ class CollectAutoImage(pyblish.api.ContextPlugin): product_name = get_product_name( project_name, - task_entity, + task_name, + task_type, host_name, product_type, variant, diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_review.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_review.py index 49eac1cbd2..d7267d253a 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_review.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_review.py @@ -65,10 +65,15 @@ class CollectAutoReview(pyblish.api.ContextPlugin): host_name = context.data["hostName"] folder_entity = context.data["folderEntity"] task_entity = context.data["taskEntity"] + task_name = task_type = None + if task_entity: + task_name = task_entity["name"] + task_type = task_entity["taskType"] product_name = get_product_name( project_name, - task_entity, + task_name, + task_type, host_name, product_type, variant, diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_workfile.py b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_workfile.py index fd68529437..af74c76a15 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_workfile.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_workfile.py @@ -70,10 +70,15 @@ class CollectAutoWorkfile(pyblish.api.ContextPlugin): host_name = context.data["hostName"] folder_entity = context.data["folderEntity"] task_entity = context.data["taskEntity"] + task_name = task_type = None + if task_entity: + task_name = task_entity["name"] + task_type = task_entity["taskType"] product_name = get_product_name( project_name, - task_entity, + task_name, + task_type, host_name, product_type, variant, diff --git a/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py b/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py index 7f7a0acd75..20aaa56993 100644 --- a/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py +++ b/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py @@ -86,13 +86,19 @@ class CollectTextureSet(pyblish.api.InstancePlugin): map_identifier = strip_template(template) suffix += f".{map_identifier}" + task_name = task_type = None + if task_entity: + task_name = task_entity["name"] + task_type = task_entity["taskType"] + image_product_name = get_product_name( # TODO: The product type actually isn't 'texture' currently but # for now this is only done so the product name starts with # 'texture' - project_name=context.data["projectName"], - task_entity=task_entity, - host_name=context.data["hostName"], + context.data["projectName"], + task_name, + task_type, + context.data["hostName"], product_type="texture", variant=instance.data["variant"] + suffix, project_settings=context.data["project_settings"] diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_movie_batch.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_movie_batch.py index 1fa431fcd0..546408b4d6 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_movie_batch.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_movie_batch.py @@ -105,10 +105,15 @@ class BatchMovieCreator(TrayPublishCreator): def _get_product_name(self, project_name, task_entity, variant): """Create product name according to standard template process""" host_name = self.create_context.host_name + task_name = task_type = None + if task_entity: + task_name = task_entity["name"] + task_type = task_entity["taskType"] try: product_name = get_product_name( project_name, - task_entity, + task_name, + task_type, host_name, self.product_type, variant, @@ -119,14 +124,10 @@ class BatchMovieCreator(TrayPublishCreator): # but user have ability to change it # NOTE: This expect that there is not task 'Undefined' on folder dumb_value = "Undefined" - task_entity = { - "name": dumb_value, - "taskType": dumb_value, - "short": dumb_value, - } product_name = get_product_name( project_name, - task_entity, + dumb_value, + dumb_value, host_name, self.product_type, variant, diff --git a/client/ayon_core/hosts/tvpaint/api/plugin.py b/client/ayon_core/hosts/tvpaint/api/plugin.py index c198f62583..e715b959f4 100644 --- a/client/ayon_core/hosts/tvpaint/api/plugin.py +++ b/client/ayon_core/hosts/tvpaint/api/plugin.py @@ -70,10 +70,15 @@ class TVPaintCreatorCommon: host_name, instance ) + task_name = task_type = None + if task_entity: + task_name = task_entity["name"] + task_type = task_entity["taskType"] return get_product_name( project_name, - task_entity, + task_name, + task_type, host_name, self.product_type, variant, diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index a984b7affe..5505427d7e 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -514,6 +514,12 @@ class BaseCreator: if host_name is None: host_name = self.create_context.host_name + + task_name = task_type = None + if task_entity: + task_name = task_entity["name"] + task_type = task_entity["taskType"] + dynamic_data = self.get_dynamic_data( project_name, folder_entity, @@ -525,7 +531,8 @@ class BaseCreator: return get_product_name( project_name, - task_entity, + task_name, + task_type, host_name, self.product_type, variant, diff --git a/client/ayon_core/pipeline/create/legacy_create.py b/client/ayon_core/pipeline/create/legacy_create.py index 018e958329..a8a39b41e3 100644 --- a/client/ayon_core/pipeline/create/legacy_create.py +++ b/client/ayon_core/pipeline/create/legacy_create.py @@ -149,10 +149,14 @@ class LegacyCreator(object): dynamic_data = cls.get_dynamic_data( project_name, folder_entity, task_entity, variant, host_name ) - + task_name = task_type = None + if task_entity: + task_name = task_entity["name"] + task_type = task_entity["taskType"] return get_product_name( project_name, - task_entity, + task_name, + task_type, host_name, cls.product_type, variant, diff --git a/client/ayon_core/pipeline/create/product_name.py b/client/ayon_core/pipeline/create/product_name.py index 6da357b5aa..74e268fbb3 100644 --- a/client/ayon_core/pipeline/create/product_name.py +++ b/client/ayon_core/pipeline/create/product_name.py @@ -81,7 +81,8 @@ def get_product_name_template( def get_product_name( project_name, - task_entity, + task_name, + task_type, host_name, product_type, variant, @@ -106,7 +107,8 @@ def get_product_name( Args: project_name (str): Project name. - task_entity (Dict[str, Any]): Task entity. + task_name (Union[str, None]): Task name. + task_type (Union[str, None]): Task type. host_name (str): Host name. product_type (str): Product type. variant (str): In most of the cases it is user input during creation. @@ -129,11 +131,6 @@ def get_product_name( if not product_type: return "" - task_name = task_type = None - if task_entity: - task_name = task_entity["name"] - task_type = task_entity["taskType"] - template = get_product_name_template( project_name, product_type_filter or product_type, diff --git a/client/ayon_core/tools/push_to_project/models/integrate.py b/client/ayon_core/tools/push_to_project/models/integrate.py index 30bccc46a3..8a29da2fe4 100644 --- a/client/ayon_core/tools/push_to_project/models/integrate.py +++ b/client/ayon_core/tools/push_to_project/models/integrate.py @@ -819,15 +819,15 @@ class ProjectPushItemProcess: def _determine_product_name(self): product_type = self._product_type task_info = self._task_info - new_task_info = None + task_name = task_type = None if task_info: - new_task_info = { - "name": task_info["name"], - "taskType": task_info["type"] - } + task_name = task_info["name"] + task_type = task_info["type"] + product_name = get_product_name( self._item.dst_project_name, - new_task_info, + task_name, + task_type, self.host_name, product_type, self._item.variant, From fc2dfd0a00a49579bd3327d223fd070005714760 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 11 Mar 2024 12:37:07 +0100 Subject: [PATCH 518/573] added missing import --- client/ayon_core/plugins/publish/integrate_hero_version.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/plugins/publish/integrate_hero_version.py b/client/ayon_core/plugins/publish/integrate_hero_version.py index 75fc444a55..4539727569 100644 --- a/client/ayon_core/plugins/publish/integrate_hero_version.py +++ b/client/ayon_core/plugins/publish/integrate_hero_version.py @@ -10,6 +10,7 @@ from ayon_api.operations import ( OperationsSession, new_version_entity, ) +from ayon_api.utils import create_entity_id from ayon_core.lib import create_hard_link from ayon_core.pipeline.publish import get_publish_template_name From c8f2ad632c41a30017d3d5d10859da7553ed81a9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 11 Mar 2024 12:38:59 +0100 Subject: [PATCH 519/573] don't limit existing entities to specific fields --- client/ayon_core/plugins/publish/integrate.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index fd66550141..b7839338ae 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -297,8 +297,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): repre_entity["name"].lower(): repre_entity for repre_entity in get_representations( project_name, - version_ids=[version_entity["id"]], - fields={"id", "name"} + version_ids=[version_entity["id"]] ) } @@ -506,8 +505,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): existing_version = get_version_by_name( project_name, version_number, - product_entity["id"], - fields={"id"} + product_entity["id"] ) version_id = None if existing_version: From 2f3778257cac914e429f22492258ac90bec0ca2f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 11 Mar 2024 14:56:14 +0100 Subject: [PATCH 520/573] add missing function and method to integrate hero version --- .../plugins/publish/integrate_hero_version.py | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/integrate_hero_version.py b/client/ayon_core/plugins/publish/integrate_hero_version.py index 4539727569..46ebde88a3 100644 --- a/client/ayon_core/plugins/publish/integrate_hero_version.py +++ b/client/ayon_core/plugins/publish/integrate_hero_version.py @@ -12,7 +12,7 @@ from ayon_api.operations import ( ) from ayon_api.utils import create_entity_id -from ayon_core.lib import create_hard_link +from ayon_core.lib import create_hard_link, source_hash from ayon_core.pipeline.publish import get_publish_template_name @@ -579,6 +579,33 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): logger=self.log ) + def get_rootless_path(self, anatomy, path): + """Returns, if possible, path without absolute portion from root + (eg. 'c:\' or '/opt/..') + + This information is platform dependent and shouldn't be captured. + Example: + 'c:/projects/MyProject1/Assets/publish...' > + '{root}/MyProject1/Assets...' + + Args: + anatomy (Anatomy): Project anatomy. + path (str): Absolute path. + + Returns: + str: Path where root path is replaced by formatting string. + + """ + success, rootless_path = anatomy.find_root_template_from_path(path) + if success: + path = rootless_path + else: + self.log.warning(( + "Could not find root path for remapping \"{}\"." + " This may cause issues on farm." + ).format(path)) + return path + def copy_file(self, src_path, dst_path): # TODO check drives if are the same to check if cas hardlink dirname = os.path.dirname(dst_path) From 0eaa438b1b6cb76916c744c56c0238c7396e2407 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 11 Mar 2024 15:00:32 +0100 Subject: [PATCH 521/573] fix variable name Co-authored-by: Mustafa Taher --- client/ayon_core/hosts/houdini/api/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/api/lib.py b/client/ayon_core/hosts/houdini/api/lib.py index c3b527b9fb..681052a44d 100644 --- a/client/ayon_core/hosts/houdini/api/lib.py +++ b/client/ayon_core/hosts/houdini/api/lib.py @@ -858,7 +858,7 @@ def get_current_context_template_data_with_folder_attrs(): anatomy = Anatomy(project_name, project_entity=project_entity) folder_entity = ayon_api.get_folder_by_path(project_name, folder_path) task_entity = ayon_api.get_task_by_name( - project_name, folder_path["id"], task_name + project_name, folder_entity["id"], task_name ) # get context specific vars From 1d45203491facaaad89ec3033e8d61966f964f49 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Mon, 11 Mar 2024 16:21:18 +0200 Subject: [PATCH 522/573] Update variables names - use squeare brackets instead of .get to retrieve 'instance_id' --- .../plugins/publish/validate_instance_in_context.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_instance_in_context.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_instance_in_context.py index ac31254228..26708e306b 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_instance_in_context.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_instance_in_context.py @@ -31,12 +31,12 @@ class ValidateInstanceInContextHoudini(pyblish.api.InstancePlugin, if not self.is_active(instance.data): return - folderPath = instance.data.get("folderPath") + folder_path = instance.data.get("folderPath") task = instance.data.get("task") context = self.get_context(instance) - if (folderPath, task) != context: + if (folder_path, task) != context: context_label = "{} > {}".format(*context) - instance_label = "{} > {}".format(folderPath, task) + instance_label = "{} > {}".format(folder_path, task) raise PublishValidationError( message=( @@ -58,14 +58,14 @@ class ValidateInstanceInContextHoudini(pyblish.api.InstancePlugin, @classmethod def repair(cls, instance): - context_asset, context_task = cls.get_context(instance) + context_folder, context_task = cls.get_context(instance) create_context = instance.context.data["create_context"] - instance_id = instance.data.get("instance_id") + instance_id = instance.data["instance_id"] created_instance = create_context.get_instance_by_id( instance_id ) - created_instance["folderPath"] = context_asset + created_instance["folderPath"] = context_folder created_instance["task"] = context_task create_context.save_changes() From 6ca0ce5759affd2914011a27235384889f0fd1dc Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 11 Mar 2024 17:09:18 +0100 Subject: [PATCH 523/573] ignore missing 'task' on 'instance.data' --- .../publish/collect_anatomy_instance_data.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py index d6ac1544f5..efe45fa806 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py @@ -70,7 +70,6 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): for instance in context: instance_folder_entity = instance.data.get("folderEntity") _folder_path = instance.data["folderPath"] - _task_name = instance.data["task"] # There is possibility that folderEntity on instance is set if instance_folder_entity: @@ -145,7 +144,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): folder_path_by_id[folder_id] = folder_entity["path"] task_entity = instance.data.get("taskEntity") - _task_name = instance.data["task"] + _task_name = instance.data.get("task") # There is possibility that taskEntity on instance is set if task_entity: @@ -180,12 +179,15 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): all_task_names = set() for per_task in instances_missing_task.values(): all_task_names |= set(per_task.keys()) + all_task_names.discard(None) - task_entities = ayon_api.get_tasks( - project_name, - folder_ids=all_folder_ids, - task_names=all_task_names - ) + task_entities = [] + if all_task_names: + task_entities = ayon_api.get_tasks( + project_name, + folder_ids=all_folder_ids, + task_names=all_task_names + ) task_entity_by_ids = {} for task_entity in task_entities: folder_id = task_entity["folderId"] @@ -206,7 +208,6 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): not_found_task_paths.append( "/".join([folder_path, task_name]) ) - continue for instance in instances: instance.data["taskEntity"] = task_entity From 4565db8841e6ad3295d037055edd192632c7aec9 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 12 Mar 2024 10:46:15 +0100 Subject: [PATCH 524/573] ignoring clips without linked mediapoolitems --- client/ayon_core/hosts/resolve/api/lib.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/hosts/resolve/api/lib.py b/client/ayon_core/hosts/resolve/api/lib.py index 2d15c93f87..731da6fc6f 100644 --- a/client/ayon_core/hosts/resolve/api/lib.py +++ b/client/ayon_core/hosts/resolve/api/lib.py @@ -330,19 +330,25 @@ def get_timeline_item(media_pool_item: object, Returns: object: resolve.TimelineItem """ - _clip_property = media_pool_item.GetClipProperty - clip_name = _clip_property("File Name") + clip_name = media_pool_item.GetClipProperty("File Name") output_timeline_item = None timeline = timeline or get_current_timeline() with maintain_current_timeline(timeline): # search the timeline for the added clip - for _ti_data in get_current_timeline_items(): - _ti_clip = _ti_data["clip"]["item"] - _ti_clip_property = _ti_clip.GetMediaPoolItem().GetClipProperty - if clip_name in _ti_clip_property("File Name"): - output_timeline_item = _ti_clip + for ti_data in get_current_timeline_items(): + ti_clip_item = ti_data["clip"]["item"] + ti_media_pool_item = ti_clip_item.GetMediaPoolItem() + + # Skip items that do not have a media pool item, like for example + # an "Adjustment Clip" or a "Fusion Composition" from the effects + # toolbox + if not ti_media_pool_item: + continue + + if clip_name in ti_media_pool_item.GetClipProperty("File Name"): + output_timeline_item = ti_clip_item return output_timeline_item From 466a3c977cf6be9150a738f50476b72d10eb5903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Tue, 12 Mar 2024 10:57:38 +0100 Subject: [PATCH 525/573] reversing changes since this situation will not happen --- client/ayon_core/hosts/resolve/api/plugin.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/client/ayon_core/hosts/resolve/api/plugin.py b/client/ayon_core/hosts/resolve/api/plugin.py index e95c9da82d..ccb20f712f 100644 --- a/client/ayon_core/hosts/resolve/api/plugin.py +++ b/client/ayon_core/hosts/resolve/api/plugin.py @@ -11,7 +11,6 @@ from ayon_core.pipeline import ( LoaderPlugin, Anatomy ) -from ayon_core.pipeline.load import LoadError from . import lib from .menu import load_stylesheet @@ -406,11 +405,6 @@ class ClipLoader: files, self.active_bin ) - - assert media_pool_item, LoadError( - "Cannot create media pool item for files: `{}`".format(files) - ) - _clip_property = media_pool_item.GetClipProperty source_in = int(_clip_property("Start")) source_out = int(_clip_property("End")) @@ -481,11 +475,6 @@ class ClipLoader: files, self.active_bin ) - - assert media_pool_item, LoadError( - "Cannot create media pool item for files: `{}`".format(files) - ) - _clip_property = media_pool_item.GetClipProperty source_in = int(_clip_property("Start")) From 4cf6e1a443412314cdd1e7f69a050082a56540af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Tue, 12 Mar 2024 10:58:45 +0100 Subject: [PATCH 526/573] Update client/ayon_core/hosts/resolve/api/lib.py --- client/ayon_core/hosts/resolve/api/lib.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/ayon_core/hosts/resolve/api/lib.py b/client/ayon_core/hosts/resolve/api/lib.py index 731da6fc6f..dd97303383 100644 --- a/client/ayon_core/hosts/resolve/api/lib.py +++ b/client/ayon_core/hosts/resolve/api/lib.py @@ -415,9 +415,6 @@ def get_current_timeline_items( } # get track item object and its color for clip_index, ti in enumerate(_clips[track_index]): - if not ti.GetMediaPoolItem(): - continue - data = _data.copy() data["clip"] = { "item": ti, From cf3d15e51d650c0f6b01cd6420dde9a2eede16a1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 12 Mar 2024 11:00:47 +0100 Subject: [PATCH 527/573] do not store missing task path if task is not set --- .../ayon_core/plugins/publish/collect_anatomy_instance_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py index efe45fa806..e3b27a0db5 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py @@ -203,7 +203,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): .get(folder_id, {}) .get(task_name) ) - if not task_entity: + if task_name and not task_entity: folder_path = folder_path_by_id[folder_id] not_found_task_paths.append( "/".join([folder_path, task_name]) From 66adf30a9b63a52018a35bd82d2618ba03a6e5d3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 12 Mar 2024 11:23:54 +0100 Subject: [PATCH 528/573] integrate representations later when files are already integrated --- .../plugins/publish/integrate_hero_version.py | 57 +++++++++++-------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/client/ayon_core/plugins/publish/integrate_hero_version.py b/client/ayon_core/plugins/publish/integrate_hero_version.py index 46ebde88a3..a251ac80ca 100644 --- a/client/ayon_core/plugins/publish/integrate_hero_version.py +++ b/client/ayon_core/plugins/publish/integrate_hero_version.py @@ -323,8 +323,10 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): "Could not create hero version because it is not" " possible to replace current hero files." )) + try: src_to_dst_file_paths = [] + repre_integrate_data = [] path_template_obj = anatomy.templates_obj[template_key]["path"] for repre_info in published_repres.values(): @@ -339,10 +341,6 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): # Get filled path to repre context template_filled = path_template_obj.format_strict(anatomy_data) - repre_attributes = { - "path": str(template_filled), - "template": hero_template - } repre_context = template_filled.used_values for key in self.db_representation_context_keys: value = anatomy_data.get(key) @@ -354,10 +352,15 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): repre_entity.pop("id", None) repre_entity["versionId"] = new_hero_version["id"] repre_entity["context"] = repre_context - repre_entity["attrib"] = repre_attributes + repre_entity["attrib"] = { + "path": str(template_filled), + "template": hero_template + } + dst_paths = [] # Prepare paths of source and destination files if len(published_files) == 1: + dst_paths.append(str(template_filled)) src_to_dst_file_paths.append( (published_files[0], template_filled) ) @@ -393,11 +396,28 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): src_to_dst_file_paths.append( (src_file, dst_file) ) + dst_paths.append(dst_file) - # replace original file name with hero name in repre doc - dst_paths = [] - for _, dst_path in src_to_dst_file_paths: - dst_paths.append(dst_path) + repre_integrate_data.append( + (repre_entity, dst_paths) + ) + + self.path_checks = [] + + # Copy(hardlink) paths of source and destination files + # TODO should we *only* create hardlinks? + # TODO should we keep files for deletion until this is successful? + for src_path, dst_path in src_to_dst_file_paths: + self.copy_file(src_path, dst_path) + + for src_path, dst_path in other_file_paths_mapping: + self.copy_file(src_path, dst_path) + + # Update prepared representation etity data with files + # and integrate it to server. + # NOTE: This must happen with existing files on disk because of + # file hash. + for repre_entity, dst_paths in repre_integrate_data: repre_files = self.get_files_info(dst_paths, anatomy) repre_entity["files"] = repre_files @@ -416,13 +436,15 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): update_data ) - # Unarchive representation + # Activate representation elif repre_name_low in inactive_old_repres_by_name: inactive_repre = inactive_old_repres_by_name.pop( repre_name_low ) repre_entity["id"] = inactive_repre["id"] - update_data = prepare_changes(inactive_repre, repre_entity) + update_data = prepare_changes( + inactive_repre, repre_entity + ) op_session.update_entity( project_name, "representation", @@ -438,18 +460,7 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): repre_entity ) - self.path_checks = [] - - # Copy(hardlink) paths of source and destination files - # TODO should we *only* create hardlinks? - # TODO should we keep files for deletion until this is successful? - for src_path, dst_path in src_to_dst_file_paths: - self.copy_file(src_path, dst_path) - - for src_path, dst_path in other_file_paths_mapping: - self.copy_file(src_path, dst_path) - - # Archive not replaced old representations + # Deactivate not replaced representations for repre in old_repres_to_delete.values(): op_session.update_entity( project_name, From 24f65f4d90e9cbdc8c79f57b8b51fcb5c69a7310 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 12 Mar 2024 12:07:39 +0100 Subject: [PATCH 529/573] fix integrate thumbnail --- .../plugins/publish/integrate_thumbnail.py | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/client/ayon_core/plugins/publish/integrate_thumbnail.py b/client/ayon_core/plugins/publish/integrate_thumbnail.py index 31a7f801fa..600bea5da1 100644 --- a/client/ayon_core/plugins/publish/integrate_thumbnail.py +++ b/client/ayon_core/plugins/publish/integrate_thumbnail.py @@ -133,27 +133,30 @@ class IntegrateThumbnailsAYON(pyblish.api.ContextPlugin): for repre_info in published_representations.values(): return repre_info["representation"]["versionId"] - def _get_instance_thumbnail_path(self, published_representations): - thumb_repre_doc = None + def _get_instance_thumbnail_path( + self, published_representations, anatomy + ): + thumb_repre_entity = None for repre_info in published_representations.values(): - repre_doc = repre_info["representation"] - if "thumbnail" in repre_doc["name"].lower(): - thumb_repre_doc = repre_doc + repre_entity = repre_info["representation"] + if "thumbnail" in repre_entity["name"].lower(): + thumb_repre_entity = repre_entity break - if thumb_repre_doc is None: + if thumb_repre_entity is None: self.log.debug( "There is no representation with name \"thumbnail\"" ) return None - path = thumb_repre_doc["data"]["path"] - if not os.path.exists(path): + path = thumb_repre_entity["attrib"]["path"] + filled_path = anatomy.fill_root(path) + if not os.path.exists(filled_path): self.log.warning( - "Thumbnail file cannot be found. Path: {}".format(path) + "Thumbnail file cannot be found. Path: {}".format(filled_path) ) return None - return os.path.normpath(path) + return os.path.normpath(filled_path) def _integrate_thumbnails( self, From 88cd493f2df99b07582e397b102bb0c11a7ba6ac Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 12 Mar 2024 19:19:51 +0800 Subject: [PATCH 530/573] fix the bug in regards to the non-existence of instance.context.data['subset'] --- .../ayon_core/hosts/max/plugins/publish/validate_model_name.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py b/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py index a0cad4e454..d33b1af3ed 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py @@ -104,7 +104,7 @@ class ValidateModelName(pyblish.api.InstancePlugin, compare = { "project": instance.context.data["projectName"], "asset": instance.context.data["folderPath"], - "subset": instance.context.data["subset"], + "subset": instance.name, } for key, required_value in compare.items(): if key in regex.groupindex: From d5bdcf2b3c5661108bf222f7e6158cc73738bd93 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 12 Mar 2024 19:49:36 +0800 Subject: [PATCH 531/573] use instance.data['productName'] instead of instance.name --- .../ayon_core/hosts/max/plugins/publish/validate_model_name.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py b/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py index d33b1af3ed..55b0908f29 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py @@ -100,11 +100,12 @@ class ValidateModelName(pyblish.api.InstancePlugin, return obj # Validate regex groups + cls.log.debug(instance.data["productName"]) invalid = False compare = { "project": instance.context.data["projectName"], "asset": instance.context.data["folderPath"], - "subset": instance.name, + "subset": instance.data["productName"], } for key, required_value in compare.items(): if key in regex.groupindex: From 1db9fbfe4056deca2da3238172e8328f28acc7b6 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 12 Mar 2024 19:51:32 +0800 Subject: [PATCH 532/573] clean up unnecessary msg --- .../ayon_core/hosts/max/plugins/publish/validate_model_name.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py b/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py index 55b0908f29..903a31d54a 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py @@ -100,12 +100,11 @@ class ValidateModelName(pyblish.api.InstancePlugin, return obj # Validate regex groups - cls.log.debug(instance.data["productName"]) invalid = False compare = { "project": instance.context.data["projectName"], "asset": instance.context.data["folderPath"], - "subset": instance.data["productName"], + "subset": instance.data["productName"] } for key, required_value in compare.items(): if key in regex.groupindex: From 1d8eea5809274d8124a4a5cba6454703bf795cb5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 12 Mar 2024 13:00:24 +0100 Subject: [PATCH 533/573] monkey patch ayon api because of bug --- client/ayon_core/lib/ayon_connection.py | 63 +++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/client/ayon_core/lib/ayon_connection.py b/client/ayon_core/lib/ayon_connection.py index aaa7dd3b4d..5ee654943f 100644 --- a/client/ayon_core/lib/ayon_connection.py +++ b/client/ayon_core/lib/ayon_connection.py @@ -1,5 +1,6 @@ import os +import semver import ayon_api from .local_settings import get_local_site_id @@ -9,6 +10,49 @@ class _Cache: initialized = False +def _new_get_last_version_by_product_name( + self, + project_name, + product_name, + folder_id, + active=True, + fields=None, + own_attributes=False +): + """Query last version entity by product name and folder id. + + Args: + project_name (str): Project where to look for representation. + product_name (str): Product name. + folder_id (str): Folder id. + active (Optional[bool]): Receive active/inactive entities. + Both are returned when 'None' is passed. + fields (Optional[Iterable[str]]): fields to be queried + for representations. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + Returns: + Union[dict[str, Any], None]: Queried version entity or None. + + """ + if not folder_id: + return None + + product = self.get_product_by_name( + project_name, product_name, folder_id, fields={"id"} + ) + if not product: + return None + return self.get_last_version_by_product_id( + project_name, + product["id"], + active=active, + fields=fields, + own_attributes=own_attributes + ) + + def initialize_ayon_connection(force=False): """Initialize global AYON api connection. @@ -23,11 +67,30 @@ def initialize_ayon_connection(force=False): """ if not force and _Cache.initialized: return + _Cache.initialized = True + ayon_api_version = ( + semver.VersionInfo.parse(ayon_api.__version__).to_tuple() + ) + # TODO remove mokey patching after when AYON api is safely updated + fix_last_version_by_product_name = ayon_api_version < (1, 0, 2) + # Monkey patching to fix 'get_last_version_by_product_name' + if fix_last_version_by_product_name: + ayon_api.ServerAPI.get_last_version_by_product_name = ( + _new_get_last_version_by_product_name + ) + site_id = get_local_site_id() version = os.getenv("AYON_VERSION") if ayon_api.is_connection_created(): con = ayon_api.get_server_api_connection() + # Monkey patching to fix 'get_last_version_by_product_name' + if fix_last_version_by_product_name: + def _con_wrapper(*args, **kwargs): + return _new_get_last_version_by_product_name( + con, *args, **kwargs + ) + con.get_last_version_by_product_name = _con_wrapper con.set_site_id(site_id) con.set_client_version(version) else: From 40e41c56ae6b70abcb30a958799539c2110fbf6e Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 12 Mar 2024 20:32:47 +0800 Subject: [PATCH 534/573] use instance.data['folderPath'] --- .../ayon_core/hosts/max/plugins/publish/validate_model_name.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py b/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py index 903a31d54a..eb86e2e5bd 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py @@ -103,7 +103,7 @@ class ValidateModelName(pyblish.api.InstancePlugin, invalid = False compare = { "project": instance.context.data["projectName"], - "asset": instance.context.data["folderPath"], + "asset": instance.data["folderPath"], "subset": instance.data["productName"] } for key, required_value in compare.items(): From b116272a1fb6e76f21cde059e106284c4081fe4c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 12 Mar 2024 14:02:15 +0100 Subject: [PATCH 535/573] add missing anatomy argument --- client/ayon_core/plugins/publish/integrate_thumbnail.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/integrate_thumbnail.py b/client/ayon_core/plugins/publish/integrate_thumbnail.py index 600bea5da1..ca32e60cc2 100644 --- a/client/ayon_core/plugins/publish/integrate_thumbnail.py +++ b/client/ayon_core/plugins/publish/integrate_thumbnail.py @@ -83,6 +83,7 @@ class IntegrateThumbnailsAYON(pyblish.api.ContextPlugin): ) filtered_instances = [] + anatomy = context.data["anatomy"] for instance in context: instance_label = self._get_instance_label(instance) # Skip instances without published representations @@ -98,7 +99,9 @@ class IntegrateThumbnailsAYON(pyblish.api.ContextPlugin): # Find thumbnail path on instance thumbnail_path = ( instance.data.get("thumbnailPath") - or self._get_instance_thumbnail_path(published_repres) + or self._get_instance_thumbnail_path( + published_repres, anatomy + ) ) if thumbnail_path: self.log.debug(( From 5d854115b881970a21823ab17a4690164637da57 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 12 Mar 2024 14:04:07 +0100 Subject: [PATCH 536/573] use negative version --- client/ayon_core/plugins/publish/integrate_hero_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/integrate_hero_version.py b/client/ayon_core/plugins/publish/integrate_hero_version.py index a251ac80ca..e800ecdd72 100644 --- a/client/ayon_core/plugins/publish/integrate_hero_version.py +++ b/client/ayon_core/plugins/publish/integrate_hero_version.py @@ -237,7 +237,7 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): entity_id = old_version["id"] new_hero_version = new_version_entity( - src_version_entity["version"], + - src_version_entity["version"], src_version_entity["productId"], task_id=src_version_entity["taskId"], data=copy.deepcopy(src_version_entity["data"]), From 5137eabaafc480b3fafe52a3ec0cdc9a690896c8 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 12 Mar 2024 22:54:57 +0800 Subject: [PATCH 537/573] update the options to be more artist-friendly --- .../hosts/max/plugins/load/load_max_scene.py | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py index 3e8a918424..beb465a6ef 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py +++ b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py @@ -1,6 +1,6 @@ import os from qtpy import QtWidgets, QtCore -from ayon_core.lib import EnumDef +from ayon_core.lib.attribute_definitions import EnumDef from ayon_core.hosts.max.api import lib from ayon_core.hosts.max.api.lib import ( unique_namespace, @@ -23,6 +23,7 @@ class MaterialDupOptionsWindow(QtWidgets.QDialog): self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint) self.material_option = None + self.material_options = material_options self.widgets = { "label": QtWidgets.QLabel( @@ -33,7 +34,7 @@ class MaterialDupOptionsWindow(QtWidgets.QDialog): "okButton": QtWidgets.QPushButton("Ok"), "cancelButton": QtWidgets.QPushButton("Cancel") } - for option in material_options: + for option in material_options.values(): self.widgets["material_options_list"].addItem(option) # Build buttons. layout = QtWidgets.QHBoxLayout(self.widgets["buttons"]) @@ -51,7 +52,9 @@ class MaterialDupOptionsWindow(QtWidgets.QDialog): self.on_material_optionsPressed) def on_material_optionsPressed(self, item): - self.material_option = item.text() + self.material_option = next((key for key in self.material_options.keys() + if self.material_options[key]== item.text()), + None) def on_ok_pressed(self): if self.material_option is None: @@ -75,14 +78,17 @@ class MaxSceneLoader(load.LoaderPlugin): icon = "code-fork" color = "green" mtl_dup_default = "promptMtlDups" - mtl_dup_enum = ["promptMtlDups", "useMergedMtlDups", - "useSceneMtlDups", "renameMtlDups"] - + mtl_dup_enum_dict = { + "promptMtlDups": "Prompt Material", + "useMergedMtlDups": "Use Incoming Material", + "useSceneMtlDups": "Use Scene Material", + "renameMtlDups": "Merge and Rename Incoming Material" + } @classmethod def get_options(cls, contexts): return [ EnumDef("mtldup", - cls.mtl_dup_enum, + items=cls.mtl_dup_enum_dict, default=cls.mtl_dup_default, label="Material Duplicate Options") ] @@ -132,7 +138,7 @@ class MaxSceneLoader(load.LoaderPlugin): rt.Delete(prev_max_obj) material_option = self.mtl_dup_default if not is_headless(): - window = MaterialDupOptionsWindow(self.mtl_dup_enum) + window = MaterialDupOptionsWindow(self.mtl_dup_enum_dict) window.exec_() material_option = window.material_option rt.MergeMaxFile(path, rt.Name(material_option), quiet=True) From 21d7dd3080af9ebfb3ab7ba2301016a73bc6d022 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 12 Mar 2024 16:00:28 +0100 Subject: [PATCH 538/573] don't expect task id to be filled --- client/ayon_core/plugins/publish/integrate_hero_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/integrate_hero_version.py b/client/ayon_core/plugins/publish/integrate_hero_version.py index e800ecdd72..6d690ba94b 100644 --- a/client/ayon_core/plugins/publish/integrate_hero_version.py +++ b/client/ayon_core/plugins/publish/integrate_hero_version.py @@ -239,7 +239,7 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): new_hero_version = new_version_entity( - src_version_entity["version"], src_version_entity["productId"], - task_id=src_version_entity["taskId"], + task_id=src_version_entity.get("taskId"), data=copy.deepcopy(src_version_entity["data"]), attribs=copy.deepcopy(src_version_entity["attrib"]), entity_id=entity_id, From 68a95078a87b9d0d3c2dcd139b78f1b38bd28aa1 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 12 Mar 2024 23:10:07 +0800 Subject: [PATCH 539/573] rename prompt materials as Prompt on Duplicate Materials --- client/ayon_core/hosts/max/plugins/load/load_max_scene.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py index beb465a6ef..5c55455d00 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py +++ b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py @@ -79,7 +79,7 @@ class MaxSceneLoader(load.LoaderPlugin): color = "green" mtl_dup_default = "promptMtlDups" mtl_dup_enum_dict = { - "promptMtlDups": "Prompt Material", + "promptMtlDups": "Prompt on Duplicate Materials", "useMergedMtlDups": "Use Incoming Material", "useSceneMtlDups": "Use Scene Material", "renameMtlDups": "Merge and Rename Incoming Material" From 5f86e9eabaee04fd4e7e75e56d6385aeda19c05d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 12 Mar 2024 16:12:12 +0100 Subject: [PATCH 540/573] copy and open of published workfile should work --- client/ayon_core/tools/workfiles/control.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/tools/workfiles/control.py b/client/ayon_core/tools/workfiles/control.py index 86c6a62a11..6793cb23a9 100644 --- a/client/ayon_core/tools/workfiles/control.py +++ b/client/ayon_core/tools/workfiles/control.py @@ -573,6 +573,7 @@ class BaseWorkfileController( workdir, filename, template_key, + src_filepath=representation_filepath ) except Exception: failed = True From 513882e6d981880b69d2982f3ef625e6aeb8ddb7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 12 Mar 2024 17:00:37 +0100 Subject: [PATCH 541/573] fix get_products call in scene inventory --- client/ayon_core/tools/sceneinventory/view.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index 140a4a32af..d576bdc139 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -185,6 +185,7 @@ class SceneInventoryView(QtWidgets.QTreeView): return standard_versions = ayon_api.get_versions( + project_name, product_ids=hero_versions_by_product_id.keys(), versions=hero_versions_by_product_id.values() ) From 4c9280b3a7161f9d26fc0725db6f8cb51f93640e Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 13 Mar 2024 14:08:55 +0800 Subject: [PATCH 542/573] add docstring to describe the material pop-up dialog & some code tweaks --- .../hosts/max/plugins/load/load_max_scene.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py index 5c55455d00..9707297c2f 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py +++ b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py @@ -17,7 +17,10 @@ from ayon_core.pipeline import get_representation_path, load class MaterialDupOptionsWindow(QtWidgets.QDialog): - + """The pop-up dialog allows users to choose material + duplicate options for importing Max objects when updating + or switching assets. + """ def __init__(self, material_options): super(MaterialDupOptionsWindow, self).__init__() self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint) @@ -34,8 +37,10 @@ class MaterialDupOptionsWindow(QtWidgets.QDialog): "okButton": QtWidgets.QPushButton("Ok"), "cancelButton": QtWidgets.QPushButton("Cancel") } - for option in material_options.values(): - self.widgets["material_options_list"].addItem(option) + for key, value in material_options.items(): + item = QtWidgets.QListWidgetItem(value) + self.widgets["material_options_list"].addItem(item) + item.setData(QtCore.Qt.UserRole, key) # Build buttons. layout = QtWidgets.QHBoxLayout(self.widgets["buttons"]) layout.addWidget(self.widgets["okButton"]) @@ -49,12 +54,10 @@ class MaterialDupOptionsWindow(QtWidgets.QDialog): self.widgets["okButton"].pressed.connect(self.on_ok_pressed) self.widgets["cancelButton"].pressed.connect(self.on_cancel_pressed) self.widgets["material_options_list"].itemPressed.connect( - self.on_material_optionsPressed) + self.on_material_options_pressed) - def on_material_optionsPressed(self, item): - self.material_option = next((key for key in self.material_options.keys() - if self.material_options[key]== item.text()), - None) + def on_material_options_pressed(self, item): + self.material_option = item.data(QtCore.Qt.UserRole) def on_ok_pressed(self): if self.material_option is None: From aa5566241e140bf270a2b436759b85d99f370ef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Wed, 13 Mar 2024 10:13:20 +0100 Subject: [PATCH 543/573] :package: update unreal integration submodule --- client/ayon_core/hosts/unreal/integration | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/unreal/integration b/client/ayon_core/hosts/unreal/integration index 6d2793170e..04b35dbf5f 160000 --- a/client/ayon_core/hosts/unreal/integration +++ b/client/ayon_core/hosts/unreal/integration @@ -1 +1 @@ -Subproject commit 6d2793170ed57187842f683a943593973abcc337 +Subproject commit 04b35dbf5fc42d905281fc30d3a22b139c1855e5 From 30b854128ebaed681d7a84a2da014327cbc418d2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 13 Mar 2024 12:29:47 +0100 Subject: [PATCH 544/573] change scrollbar stylesheet to avoid issues --- client/ayon_core/style/style.css | 32 +++++++++++++++++++ .../tools/publisher/widgets/report_page.py | 2 ++ 2 files changed, 34 insertions(+) diff --git a/client/ayon_core/style/style.css b/client/ayon_core/style/style.css index fcc76b0bff..607fd1fa31 100644 --- a/client/ayon_core/style/style.css +++ b/client/ayon_core/style/style.css @@ -1067,6 +1067,38 @@ PixmapButton:disabled { font-size: 13pt; } +#PublisherVerticalScrollArea QScrollBar { + background: transparent; + margin: 0; + border: none; +} + +#PublisherVerticalScrollArea QScrollBar:horizontal { + height: 10px; + margin: 0; +} + +#PublisherVerticalScrollArea QScrollBar:vertical { + width: 10px; + margin: 0; +} + +#PublisherVerticalScrollArea QScrollBar::handle { + background: {color:bg-scroll-handle}; + border-radius: 4px; + margin: 1px; +} + +#PublisherVerticalScrollArea QScrollBar::handle:horizontal { + min-width: 20px; + min-height: 8px; +} + +#PublisherVerticalScrollArea QScrollBar::handle:vertical { + min-height: 20px; + min-width: 8px; +} + ValidationArtistMessage QLabel { font-size: 20pt; font-weight: bold; diff --git a/client/ayon_core/tools/publisher/widgets/report_page.py b/client/ayon_core/tools/publisher/widgets/report_page.py index 1bbe8033f9..9d16fb67b2 100644 --- a/client/ayon_core/tools/publisher/widgets/report_page.py +++ b/client/ayon_core/tools/publisher/widgets/report_page.py @@ -56,6 +56,8 @@ class VerticalScrollArea(QtWidgets.QScrollArea): self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) self.setLayoutDirection(QtCore.Qt.RightToLeft) + self.setObjectName("PublisherVerticalScrollArea") + self.setAttribute(QtCore.Qt.WA_TranslucentBackground) # Background of scrollbar will be transparent scrollbar_bg = self.verticalScrollBar().parent() From 0b7efa19fe44845b3d1ec6628d6f8ff23903b2cb Mon Sep 17 00:00:00 2001 From: ChunYou Date: Wed, 13 Mar 2024 13:05:38 +0000 Subject: [PATCH 545/573] Maximise viewport instead of changing layout. Adding validator for extended viewport --- .../hosts/max/api/preview_animation.py | 25 +++++++++++-------- .../publish/validate_extended_viewport.py | 21 ++++++++++++++++ 2 files changed, 35 insertions(+), 11 deletions(-) create mode 100644 client/ayon_core/hosts/max/plugins/publish/validate_extended_viewport.py diff --git a/client/ayon_core/hosts/max/api/preview_animation.py b/client/ayon_core/hosts/max/api/preview_animation.py index f715efa53d..3872c4e493 100644 --- a/client/ayon_core/hosts/max/api/preview_animation.py +++ b/client/ayon_core/hosts/max/api/preview_animation.py @@ -31,23 +31,26 @@ def viewport_layout_and_camera(camera, layout="layout_1"): layout (str): layout to use in viewport, defaults to `layout_1` Use None to not change viewport layout during context. """ + needs_maximise = 0 + # Set to first active non extended viewport + rt.viewport.activeViewportEx(1) original_camera = rt.viewport.getCamera() - original_layout = rt.viewport.getLayout() - if not original_camera: - # if there is no original camera - # use the current camera as original - original_camera = rt.getNodeByName(camera) + original_type = rt.viewport.getType() review_camera = rt.getNodeByName(camera) + try: - if layout is not None: - layout = rt.Name(layout) - if rt.viewport.getLayout() != layout: - rt.viewport.setLayout(layout) + if rt.viewport.getLayout()!=rt.name(layout): + rt.execute("max tool maximize") + needs_maximise = 1 rt.viewport.setCamera(review_camera) yield finally: - rt.viewport.setLayout(original_layout) - rt.viewport.setCamera(original_camera) + if needs_maximise == 1: + rt.execute("max tool maximize") + if original_type == rt.Name("view_camera"): + rt.viewport.setCamera(original_camera) + else: + rt.viewport.setType(original_type) @contextlib.contextmanager diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_extended_viewport.py b/client/ayon_core/hosts/max/plugins/publish/validate_extended_viewport.py new file mode 100644 index 0000000000..96686970c6 --- /dev/null +++ b/client/ayon_core/hosts/max/plugins/publish/validate_extended_viewport.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +import pyblish.api +from ayon_core.pipeline import PublishValidationError +from pymxs import runtime as rt + + +class ValidateExtendedViewport(pyblish.api.InstancePlugin): + """Validate if the first viewport is an extended viewport.""" + + order = pyblish.api.ValidatorOrder + families = ["review"] + hosts = ["max"] + label = "Validate Extended Viewport" + + def process(self, instance): + try: + rt.viewport.activeViewportEx(1) + except RuntimeError: + raise PublishValidationError( + "Please make sure one viewport is not an extended viewport", title=self.label) + From 242b38b3d9b55e08537db75d07b55195b7efce24 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 13 Mar 2024 14:38:24 +0100 Subject: [PATCH 546/573] add margin to left to offset content from scroll bar --- client/ayon_core/tools/publisher/widgets/report_page.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/publisher/widgets/report_page.py b/client/ayon_core/tools/publisher/widgets/report_page.py index 9d16fb67b2..7475b39f52 100644 --- a/client/ayon_core/tools/publisher/widgets/report_page.py +++ b/client/ayon_core/tools/publisher/widgets/report_page.py @@ -502,7 +502,9 @@ class ValidationErrorsView(QtWidgets.QWidget): errors_scroll.setWidget(errors_widget) errors_layout = QtWidgets.QVBoxLayout(errors_widget) - errors_layout.setContentsMargins(0, 0, 0, 0) + # Add 5 margin to left so the is not directly on the edge of the + # scroll widget + errors_layout.setContentsMargins(5, 0, 0, 0) layout = QtWidgets.QVBoxLayout(self) layout.addWidget(errors_scroll, 1) From 5d946ad2e5cf46391dd65cc8b06eafa270e645cb Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 13 Mar 2024 15:14:02 +0100 Subject: [PATCH 547/573] removed 'requests_get' and 'requests_post' from lib functions --- client/ayon_core/lib/__init__.py | 9 ------- client/ayon_core/lib/connections.py | 38 ----------------------------- 2 files changed, 47 deletions(-) delete mode 100644 client/ayon_core/lib/connections.py diff --git a/client/ayon_core/lib/__init__.py b/client/ayon_core/lib/__init__.py index ab6a604adc..acd960d9b5 100644 --- a/client/ayon_core/lib/__init__.py +++ b/client/ayon_core/lib/__init__.py @@ -161,12 +161,6 @@ from .ayon_info import ( is_in_tests, ) - -from .connections import ( - requests_get, - requests_post -) - terminal = Terminal __all__ = [ @@ -283,7 +277,4 @@ __all__ = [ "is_staging_enabled", "is_dev_mode_enabled", "is_in_tests", - - "requests_get", - "requests_post" ] diff --git a/client/ayon_core/lib/connections.py b/client/ayon_core/lib/connections.py deleted file mode 100644 index 6a0cf4ae1c..0000000000 --- a/client/ayon_core/lib/connections.py +++ /dev/null @@ -1,38 +0,0 @@ -import requests -import os - - -def requests_post(*args, **kwargs): - """Wrap request post method. - - Disabling SSL certificate validation if ``DONT_VERIFY_SSL`` environment - variable is found. This is useful when Deadline server is - running with self-signed certificates and its certificate is not - added to trusted certificates on client machines. - - Warning: - Disabling SSL certificate validation is defeating one line - of defense SSL is providing, and it is not recommended. - - """ - if "verify" not in kwargs: - kwargs["verify"] = not os.getenv("OPENPYPE_DONT_VERIFY_SSL", True) - return requests.post(*args, **kwargs) - - -def requests_get(*args, **kwargs): - """Wrap request get method. - - Disabling SSL certificate validation if ``DONT_VERIFY_SSL`` environment - variable is found. This is useful when Deadline server is - running with self-signed certificates and its certificate is not - added to trusted certificates on client machines. - - Warning: - Disabling SSL certificate validation is defeating one line - of defense SSL is providing, and it is not recommended. - - """ - if "verify" not in kwargs: - kwargs["verify"] = not os.getenv("OPENPYPE_DONT_VERIFY_SSL", True) - return requests.get(*args, **kwargs) From 72d4cdd629498885a78742dce8f82cae53487e57 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 13 Mar 2024 15:14:14 +0100 Subject: [PATCH 548/573] remove it's usage from deadline addon --- client/ayon_core/modules/deadline/deadline_module.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/modules/deadline/deadline_module.py b/client/ayon_core/modules/deadline/deadline_module.py index 97d346c287..d2f0e263d4 100644 --- a/client/ayon_core/modules/deadline/deadline_module.py +++ b/client/ayon_core/modules/deadline/deadline_module.py @@ -1,9 +1,10 @@ import os -import requests -import six import sys -from ayon_core.lib import requests_get, Logger +import requests +import six + +from ayon_core.lib import Logger from ayon_core.modules import AYONAddon, IPluginPaths @@ -56,6 +57,8 @@ class DeadlineModule(AYONAddon, IPluginPaths): RuntimeError: If deadline webservice is unreachable. """ + from .abstract_submit_deadline import requests_get + if not log: log = Logger.get_logger(__name__) From 85a022276f4d06867c6415393fd62603a813339d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 13 Mar 2024 15:14:22 +0100 Subject: [PATCH 549/573] added todo comment --- client/ayon_core/modules/deadline/abstract_submit_deadline.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/ayon_core/modules/deadline/abstract_submit_deadline.py b/client/ayon_core/modules/deadline/abstract_submit_deadline.py index b2da4d1398..2e0518ae20 100644 --- a/client/ayon_core/modules/deadline/abstract_submit_deadline.py +++ b/client/ayon_core/modules/deadline/abstract_submit_deadline.py @@ -29,6 +29,10 @@ from ayon_core.pipeline.publish.lib import ( JSONDecodeError = getattr(json.decoder, "JSONDecodeError", ValueError) +# TODO both 'requests_post' and 'requests_get' should not set 'verify' based +# on environment variable. This should be done in a more controlled way, +# e.g. each deadline url could have checkbox to enabled/disable +# ssl verification. def requests_post(*args, **kwargs): """Wrap request post method. From 1d2848af455f7c2f5bd0624878bc58d2e0d65ea5 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 13 Mar 2024 22:05:24 +0100 Subject: [PATCH 550/573] old publisher settings stored in core --- client/ayon_core/tools/creator/window.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/creator/window.py b/client/ayon_core/tools/creator/window.py index e461e2e2e0..5bdc6da9b6 100644 --- a/client/ayon_core/tools/creator/window.py +++ b/client/ayon_core/tools/creator/window.py @@ -382,7 +382,7 @@ class CreatorWindow(QtWidgets.QDialog): product_types_smart_select = ( get_current_project_settings() - ["global"] + ["core"] ["tools"] ["creator"] ["product_types_smart_select"] From 6af601b784fd4e33e64ecf12aba0d76f8b774fc2 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 13 Mar 2024 22:12:15 +0100 Subject: [PATCH 551/573] versionData key fixed to versionAttributes --- client/ayon_core/hosts/resolve/api/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/resolve/api/plugin.py b/client/ayon_core/hosts/resolve/api/plugin.py index 8218000148..8c97df98b8 100644 --- a/client/ayon_core/hosts/resolve/api/plugin.py +++ b/client/ayon_core/hosts/resolve/api/plugin.py @@ -412,7 +412,7 @@ class ClipLoader: source_duration = int(_clip_property("Frames")) # Trim clip start if slate is present - if "slate" in self.data["versionData"]["families"]: + if "slate" in self.data["versionAttributes"]["families"]: source_in += 1 source_duration = source_out - source_in + 1 From fc85f469f7884852f7d1036c61c14a375c80a1aa Mon Sep 17 00:00:00 2001 From: ChunYou Date: Thu, 14 Mar 2024 02:17:05 +0000 Subject: [PATCH 552/573] Update Publish Validation Error Description --- .../hosts/max/plugins/publish/validate_extended_viewport.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_extended_viewport.py b/client/ayon_core/hosts/max/plugins/publish/validate_extended_viewport.py index 96686970c6..7d9e3dc6fe 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_extended_viewport.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_extended_viewport.py @@ -17,5 +17,8 @@ class ValidateExtendedViewport(pyblish.api.InstancePlugin): rt.viewport.activeViewportEx(1) except RuntimeError: raise PublishValidationError( - "Please make sure one viewport is not an extended viewport", title=self.label) + "Please make sure at least one viewport is not an extended viewport but a 3dsmax supported viewport " + "i.e camera/persp/orthographic view. To rectify it, please go to view in the top menubar, " + "go to Views -> Viewports Configuration -> layout and right click on one of the panels to change " + "it.", title=self.label) From b591030d33281ad6da47d63d1888996244c241fc Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 14 Mar 2024 09:42:21 +0100 Subject: [PATCH 553/573] fix version update --- client/ayon_core/pipeline/load/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index 28bb3b8955..8fdaf52e27 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -476,16 +476,16 @@ def update_container(container, version=-1): ) if isinstance(version, HeroVersionType): new_version = ayon_api.get_hero_version_by_product_id( - project_name, current_version["productId"], fields={"id"} + project_name, current_version["productId"] ) elif version == -1: new_version = ayon_api.get_last_version_by_product_id( - project_name, current_version["productId"], fields={"id"} + project_name, current_version["productId"] ) else: new_version = ayon_api.get_version_by_name( - project_name, version, current_version["productId"], fields={"id"} + project_name, version, current_version["productId"] ) if new_version is None: From 4944f956700704d2ac969fd40a3a9521d5815fcd Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 14 Mar 2024 19:23:49 +0800 Subject: [PATCH 554/573] check if there is the name endswith 'Main' in the renderlayer --- .../ayon_core/hosts/maya/plugins/publish/collect_inputs.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_inputs.py b/client/ayon_core/hosts/maya/plugins/publish/collect_inputs.py index d0b1029a03..6dff64681f 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/collect_inputs.py +++ b/client/ayon_core/hosts/maya/plugins/publish/collect_inputs.py @@ -180,6 +180,11 @@ class CollectUpstreamInputs(pyblish.api.InstancePlugin): return copy.deepcopy(scene_containers) else: # Get the members of the layer + renderlayer = next((i for i in cmds.ls(type='renderLayer') + if i.endswith(renderlayer)), None) + if renderlayer is None: + return copy.deepcopy(scene_containers) + members = cmds.editRenderLayerMembers(renderlayer, query=True, fullNames=True) or [] From eb981d83c2a2df8a537ea29479c0439a700b5076 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 14 Mar 2024 19:49:51 +0800 Subject: [PATCH 555/573] use setMembers instead of renderlayer --- .../ayon_core/hosts/maya/plugins/publish/collect_inputs.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_inputs.py b/client/ayon_core/hosts/maya/plugins/publish/collect_inputs.py index 6dff64681f..d084804e05 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/collect_inputs.py +++ b/client/ayon_core/hosts/maya/plugins/publish/collect_inputs.py @@ -172,7 +172,7 @@ class CollectUpstreamInputs(pyblish.api.InstancePlugin): """Collects inputs from nodes in renderlayer, incl. shaders + camera""" # Get the renderlayer - renderlayer = instance.data.get("renderlayer") + renderlayer = instance.data.get("setMembers") if renderlayer == "defaultRenderLayer": # Assume all loaded containers in the scene are inputs @@ -180,11 +180,6 @@ class CollectUpstreamInputs(pyblish.api.InstancePlugin): return copy.deepcopy(scene_containers) else: # Get the members of the layer - renderlayer = next((i for i in cmds.ls(type='renderLayer') - if i.endswith(renderlayer)), None) - if renderlayer is None: - return copy.deepcopy(scene_containers) - members = cmds.editRenderLayerMembers(renderlayer, query=True, fullNames=True) or [] From e8bfb903620d07f8a74b76db075384a48c5a106d Mon Sep 17 00:00:00 2001 From: ChunYou Date: Thu, 14 Mar 2024 11:54:54 +0000 Subject: [PATCH 556/573] Change Instance to Context --- .../hosts/max/plugins/publish/validate_extended_viewport.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_extended_viewport.py b/client/ayon_core/hosts/max/plugins/publish/validate_extended_viewport.py index 7d9e3dc6fe..8c64508a8b 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_extended_viewport.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_extended_viewport.py @@ -4,7 +4,7 @@ from ayon_core.pipeline import PublishValidationError from pymxs import runtime as rt -class ValidateExtendedViewport(pyblish.api.InstancePlugin): +class ValidateExtendedViewport(pyblish.api.ContextPlugin): """Validate if the first viewport is an extended viewport.""" order = pyblish.api.ValidatorOrder @@ -12,7 +12,7 @@ class ValidateExtendedViewport(pyblish.api.InstancePlugin): hosts = ["max"] label = "Validate Extended Viewport" - def process(self, instance): + def process(self, context): try: rt.viewport.activeViewportEx(1) except RuntimeError: From df83dfaf40fc5f87767e2645f96d05efbe7f97e8 Mon Sep 17 00:00:00 2001 From: r42-chun <73248638+r42-chun@users.noreply.github.com> Date: Thu, 14 Mar 2024 12:08:37 +0000 Subject: [PATCH 557/573] Commit description and rectify line length Co-authored-by: Roy Nieterau --- .../plugins/publish/validate_extended_viewport.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_extended_viewport.py b/client/ayon_core/hosts/max/plugins/publish/validate_extended_viewport.py index 8c64508a8b..ed476ec874 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_extended_viewport.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_extended_viewport.py @@ -17,8 +17,13 @@ class ValidateExtendedViewport(pyblish.api.ContextPlugin): rt.viewport.activeViewportEx(1) except RuntimeError: raise PublishValidationError( - "Please make sure at least one viewport is not an extended viewport but a 3dsmax supported viewport " - "i.e camera/persp/orthographic view. To rectify it, please go to view in the top menubar, " - "go to Views -> Viewports Configuration -> layout and right click on one of the panels to change " - "it.", title=self.label) + "Please make sure one viewport is not an extended viewport", + description = ( + "Please make sure at least one viewport is not an " + "extended viewport but a 3dsmax supported viewport " + "i.e camera/persp/orthographic view.\n\n" + "To rectify it, please go to view in the top menubar, " + "go to Views -> Viewports Configuration -> Layout and " + "right click on one of the panels to change it." + )) From 305c76b6016185fd770a00d587907bd88847e7c5 Mon Sep 17 00:00:00 2001 From: r42-chun <73248638+r42-chun@users.noreply.github.com> Date: Thu, 14 Mar 2024 12:08:57 +0000 Subject: [PATCH 558/573] Add space for cosmetics Co-authored-by: Roy Nieterau --- client/ayon_core/hosts/max/api/preview_animation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/max/api/preview_animation.py b/client/ayon_core/hosts/max/api/preview_animation.py index 3872c4e493..399d3b6222 100644 --- a/client/ayon_core/hosts/max/api/preview_animation.py +++ b/client/ayon_core/hosts/max/api/preview_animation.py @@ -39,7 +39,7 @@ def viewport_layout_and_camera(camera, layout="layout_1"): review_camera = rt.getNodeByName(camera) try: - if rt.viewport.getLayout()!=rt.name(layout): + if rt.viewport.getLayout() != rt.name(layout): rt.execute("max tool maximize") needs_maximise = 1 rt.viewport.setCamera(review_camera) From 25c5b7140b53472110a037f5725631f8f86073bf Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Fri, 15 Mar 2024 07:18:46 +0000 Subject: [PATCH 559/573] Update render_settings.py --- server_addon/maya/server/settings/render_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/maya/server/settings/render_settings.py b/server_addon/maya/server/settings/render_settings.py index 577049b42f..bc476ec49c 100644 --- a/server_addon/maya/server/settings/render_settings.py +++ b/server_addon/maya/server/settings/render_settings.py @@ -355,7 +355,7 @@ class RedshiftSettingsModel(BaseSettingsModel): ) additional_options: list[AdditionalOptionsModel] = SettingsField( default_factory=list, - title="Additional Vray Options", + title="Additional Redshift Options", description=( "Add additional options - put attribute and value, like " "reflectionMaxTraceDepth and 3" From 0f71a89f888ecbfa66e6b245c460830428184087 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 15 Mar 2024 21:31:05 +0800 Subject: [PATCH 560/573] make sure the scene file would be saved and increment before deadline submission --- .../max/plugins/publish/increment_workfile_version.py | 2 +- .../ayon_core/hosts/max/plugins/publish/save_scene.py | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/increment_workfile_version.py b/client/ayon_core/hosts/max/plugins/publish/increment_workfile_version.py index 5f319966fe..c7c3f49626 100644 --- a/client/ayon_core/hosts/max/plugins/publish/increment_workfile_version.py +++ b/client/ayon_core/hosts/max/plugins/publish/increment_workfile_version.py @@ -9,7 +9,7 @@ class IncrementWorkfileVersion(pyblish.api.ContextPlugin): order = pyblish.api.IntegratorOrder + 0.9 label = "Increment Workfile Version" hosts = ["max"] - families = ["workfile"] + families = ["maxrender", "workfile"] def process(self, context): path = context.data["currentFile"] diff --git a/client/ayon_core/hosts/max/plugins/publish/save_scene.py b/client/ayon_core/hosts/max/plugins/publish/save_scene.py index 1c59335ceb..fe2c7f50f4 100644 --- a/client/ayon_core/hosts/max/plugins/publish/save_scene.py +++ b/client/ayon_core/hosts/max/plugins/publish/save_scene.py @@ -2,7 +2,7 @@ import pyblish.api from ayon_core.pipeline import registered_host -class SaveCurrentScene(pyblish.api.ContextPlugin): +class SaveCurrentScene(pyblish.api.InstancePlugin): """Save current scene""" label = "Save current file" @@ -10,13 +10,15 @@ class SaveCurrentScene(pyblish.api.ContextPlugin): hosts = ["max"] families = ["maxrender", "workfile"] - def process(self, context): + def process(self, instance): host = registered_host() current_file = host.get_current_workfile() - assert context.data["currentFile"] == current_file + assert instance.context.data["currentFile"] == current_file + if instance.data["productType"] == "maxrender": + host.save_workfile(current_file) - if host.workfile_has_unsaved_changes(): + elif host.workfile_has_unsaved_changes(): self.log.info(f"Saving current file: {current_file}") host.save_workfile(current_file) else: From d919e1bb4d6e9a7aaf979b82b7f33ea4fda21165 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Mar 2024 10:19:33 +0100 Subject: [PATCH 561/573] fix tvpaint load plugin --- .../hosts/tvpaint/plugins/load/load_reference_image.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py b/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py index b9276a8f7c..2820b883ed 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py +++ b/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py @@ -3,7 +3,7 @@ import collections from ayon_core.lib.attribute_definitions import BoolDef from ayon_core.pipeline import ( get_representation_context, - register_host, + registered_host, ) from ayon_core.hosts.tvpaint.api import plugin from ayon_core.hosts.tvpaint.api.lib import ( @@ -176,7 +176,7 @@ class LoadImage(plugin.Loader): return representation = container["representation"] members = self.get_members_from_container(container) - host = register_host() + host = registered_host() current_containers = host.get_containers() pop_idx = None for idx, cur_con in enumerate(current_containers): From 21d09e4f5ae37b7fab5a0a8b7266fa68ec4ce690 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Mar 2024 10:26:48 +0100 Subject: [PATCH 562/573] fix collect audio --- client/ayon_core/plugins/publish/collect_audio.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/collect_audio.py b/client/ayon_core/plugins/publish/collect_audio.py index 47c806d5de..c1633e414e 100644 --- a/client/ayon_core/plugins/publish/collect_audio.py +++ b/client/ayon_core/plugins/publish/collect_audio.py @@ -112,9 +112,13 @@ class CollectAudio(pyblish.api.ContextPlugin): Returns: collections.defaultdict[str, List[Dict[Str, Any]]]: Representations related to audio products by folder path. - """ + """ output = collections.defaultdict(list) + # Skip the queries if audio product name is not defined + if not self.audio_product_name: + return output + # Query folder entities folder_entities = ayon_api.get_folders( project_name, From 587bfe462f6548b7bd7463ad8d9d4285ae98f886 Mon Sep 17 00:00:00 2001 From: moonyuet Date: Mon, 18 Mar 2024 12:15:58 +0100 Subject: [PATCH 563/573] code tweaks according to big roy's comment --- .../plugins/publish/validate_renderpasses.py | 27 +++++++++---------- .../max/server/settings/publishers.py | 2 +- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py b/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py index 4e2298045c..0fbdb2940e 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py @@ -12,19 +12,16 @@ from ayon_core.hosts.max.api.lib_rendersettings import RenderSettings class ValidateRenderPasses(OptionalPyblishPluginMixin, pyblish.api.InstancePlugin): - """Validates Render Passes before Deadline Submission + """Validates Render Passes before farm submission """ order = ValidateContentsOrder families = ["maxrender"] hosts = ["max"] label = "Validate Render Passes" - optional = True actions = [RepairAction] def process(self, instance): - if not self.is_active(instance.data): - return invalid = self.get_invalid(instance) if invalid: bullet_point_invalid_statement = "\n".join( @@ -64,7 +61,7 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, Args: instance (pyblish.api.Instance): instance - filename (str): filename of the Max scene + workfile_name (str): filename of the Max scene Returns: list: list of invalid filename which doesn't match @@ -72,11 +69,11 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, """ invalid = [] file = rt.maxFileName - filename, ext = os.path.splitext(file) - if filename not in rt.rendOutputFilename: + workfile_name, ext = os.path.splitext(file) + if workfile_name not in rt.rendOutputFilename: cls.log.error( "Render output folder must include" - f" the max scene name {filename} " + f" the max scene name {workfile_name} " ) invalid_folder_name = os.path.dirname( rt.rendOutputFilename).replace( @@ -104,13 +101,11 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, render_elem_num = render_elem.NumRenderElements() for i in range(render_elem_num): renderlayer_name = render_elem.GetRenderElement(i) - renderpass = str(renderlayer_name).split(":")[-1] + renderpass = str(renderlayer_name).rsplit(":", 1)[-1] rend_file = render_elem.GetRenderElementFilename(i) if not rend_file: - cls.log.error( - f"No filepath for render element {renderpass}") - invalid.append((f"Invalid {renderpass}", - "No filepath")) + continue + rend_fname, ext = os.path.splitext( os.path.basename(rend_file)) invalid_filenames = cls.get_invalid_filenames( @@ -123,6 +118,10 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, cls.log.debug( "Renderpass validation does not support Arnold yet," " validation skipped...") + else: + cls.log.debug( + "Skipping render element validation " + f"for renderer : {renderer}") return invalid @classmethod @@ -140,7 +139,7 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, """ invalid = [] if instance.name not in file_name: - cls.log.error("The renderpass should have instance name inside.") + cls.log.error("The renderpass filename should contain the instance name.") invalid.append((f"Invalid instance name", file_name)) if renderpass is not None: diff --git a/server_addon/max/server/settings/publishers.py b/server_addon/max/server/settings/publishers.py index 819bca1509..5e1b348d92 100644 --- a/server_addon/max/server/settings/publishers.py +++ b/server_addon/max/server/settings/publishers.py @@ -191,7 +191,7 @@ DEFAULT_PUBLISH_SETTINGS = { }, "ValidateRenderPasses": { "enabled": True, - "optional": True, + "optional": False, "active": True }, "ExtractModelObj": { From 53981f3fe1f15ef09f0f19428df1d106380f8e63 Mon Sep 17 00:00:00 2001 From: moonyuet Date: Mon, 18 Mar 2024 14:05:34 +0100 Subject: [PATCH 564/573] report detail filename in debug message --- .../hosts/max/plugins/publish/validate_renderpasses.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py b/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py index 0fbdb2940e..ba948747b9 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py @@ -121,7 +121,7 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, else: cls.log.debug( "Skipping render element validation " - f"for renderer : {renderer}") + f"for renderer: {renderer}") return invalid @classmethod @@ -146,7 +146,7 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, if not file_name.rstrip(".").endswith(renderpass): cls.log.error( f"Filename for {renderpass} should " - f"end with {renderpass}" + f"end with {renderpass}: {file_name}" ) invalid.append((f"Invalid {renderpass}", os.path.basename(file_name))) From 0ba8d7e6456b1b8f29333acc16f8b1d71333cb2e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Mar 2024 16:47:40 +0100 Subject: [PATCH 565/573] fix super call in class method --- client/ayon_core/hosts/maya/api/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/api/plugin.py b/client/ayon_core/hosts/maya/api/plugin.py index eaf93725f4..c25e99e92e 100644 --- a/client/ayon_core/hosts/maya/api/plugin.py +++ b/client/ayon_core/hosts/maya/api/plugin.py @@ -657,7 +657,7 @@ class Loader(LoaderPlugin): @classmethod def apply_settings(cls, project_settings): - super(Loader, cls).apply_settings(project_settings) + LoaderPlugin.apply_settings(cls, project_settings) cls.load_settings = project_settings['maya']['load'] def get_custom_namespace_and_group(self, context, options, loader_key): From c62993a394308fcf1ac45f6e25e7c66a62bdd023 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Mar 2024 16:59:07 +0100 Subject: [PATCH 566/573] remove cls passed to class method --- client/ayon_core/hosts/maya/api/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/api/plugin.py b/client/ayon_core/hosts/maya/api/plugin.py index c25e99e92e..2de029c516 100644 --- a/client/ayon_core/hosts/maya/api/plugin.py +++ b/client/ayon_core/hosts/maya/api/plugin.py @@ -657,7 +657,7 @@ class Loader(LoaderPlugin): @classmethod def apply_settings(cls, project_settings): - LoaderPlugin.apply_settings(cls, project_settings) + LoaderPlugin.apply_settings(project_settings) cls.load_settings = project_settings['maya']['load'] def get_custom_namespace_and_group(self, context, options, loader_key): From e913eefa0796cd71a404dc3ca283a9837aef1b0c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Mar 2024 16:59:15 +0100 Subject: [PATCH 567/573] fix get_product_name call --- client/ayon_core/hosts/maya/api/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/api/plugin.py b/client/ayon_core/hosts/maya/api/plugin.py index 2de029c516..f086295b57 100644 --- a/client/ayon_core/hosts/maya/api/plugin.py +++ b/client/ayon_core/hosts/maya/api/plugin.py @@ -608,7 +608,7 @@ class RenderlayerCreator(NewCreator, MayaCreatorBase): return get_product_name( project_name, task_name, - task_type + task_type, host_name, self.layer_instance_prefix or self.product_type, variant, From 24a00cd96a723ad5ad88af85238596b7bd3c5cb2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Mar 2024 17:03:37 +0100 Subject: [PATCH 568/573] keep super class call --- client/ayon_core/hosts/maya/api/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/api/plugin.py b/client/ayon_core/hosts/maya/api/plugin.py index f086295b57..bdb0cb1c99 100644 --- a/client/ayon_core/hosts/maya/api/plugin.py +++ b/client/ayon_core/hosts/maya/api/plugin.py @@ -657,7 +657,7 @@ class Loader(LoaderPlugin): @classmethod def apply_settings(cls, project_settings): - LoaderPlugin.apply_settings(project_settings) + super(Loader, cls).apply_settings(project_settings) cls.load_settings = project_settings['maya']['load'] def get_custom_namespace_and_group(self, context, options, loader_key): From ed268ccec679504101cce2bd60cb3fe09293cd76 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Mar 2024 17:58:16 +0100 Subject: [PATCH 569/573] move ayon_utils utils to tools utils --- .../tools/ayon_utils/widgets/utils.py | 109 ---------- client/ayon_core/tools/utils/__init__.py | 2 + client/ayon_core/tools/utils/lib.py | 199 ++++++++++++++---- 3 files changed, 156 insertions(+), 154 deletions(-) delete mode 100644 client/ayon_core/tools/ayon_utils/widgets/utils.py diff --git a/client/ayon_core/tools/ayon_utils/widgets/utils.py b/client/ayon_core/tools/ayon_utils/widgets/utils.py deleted file mode 100644 index ead8f4edb2..0000000000 --- a/client/ayon_core/tools/ayon_utils/widgets/utils.py +++ /dev/null @@ -1,109 +0,0 @@ -import os -from functools import partial - -from qtpy import QtCore, QtGui - -from ayon_core.tools.utils.lib import get_qta_icon_by_name_and_color - - -class RefreshThread(QtCore.QThread): - refresh_finished = QtCore.Signal(str) - - def __init__(self, thread_id, func, *args, **kwargs): - super(RefreshThread, self).__init__() - self._id = thread_id - self._callback = partial(func, *args, **kwargs) - self._exception = None - self._result = None - self.finished.connect(self._on_finish_callback) - - @property - def id(self): - return self._id - - @property - def failed(self): - return self._exception is not None - - def run(self): - try: - self._result = self._callback() - except Exception as exc: - self._exception = exc - - def get_result(self): - return self._result - - def _on_finish_callback(self): - """Trigger custom signal with thread id. - - Listening for 'finished' signal we make sure that execution of thread - finished and QThread object can be safely deleted. - """ - - self.refresh_finished.emit(self.id) - - -class _IconsCache: - """Cache for icons.""" - - _cache = {} - _default = None - - @classmethod - def _get_cache_key(cls, icon_def): - parts = [] - icon_type = icon_def["type"] - if icon_type == "path": - parts = [icon_type, icon_def["path"]] - - elif icon_type == "awesome-font": - parts = [icon_type, icon_def["name"], icon_def["color"]] - return "|".join(parts) - - @classmethod - def get_icon(cls, icon_def): - if not icon_def: - return None - icon_type = icon_def["type"] - cache_key = cls._get_cache_key(icon_def) - cache = cls._cache.get(cache_key) - if cache is not None: - return cache - - icon = None - if icon_type == "path": - path = icon_def["path"] - if os.path.exists(path): - icon = QtGui.QIcon(path) - - elif icon_type == "awesome-font": - icon_name = icon_def["name"] - icon_color = icon_def["color"] - icon = get_qta_icon_by_name_and_color(icon_name, icon_color) - if icon is None: - icon = get_qta_icon_by_name_and_color( - "fa.{}".format(icon_name), icon_color) - if icon is None: - icon = cls.get_default() - cls._cache[cache_key] = icon - return icon - - @classmethod - def get_default(cls): - pix = QtGui.QPixmap(1, 1) - pix.fill(QtCore.Qt.transparent) - return QtGui.QIcon(pix) - - -def get_qt_icon(icon_def): - """Returns icon from cache or creates new one. - - Args: - icon_def (dict[str, Any]): Icon definition. - - Returns: - QtGui.QIcon: Icon. - """ - - return _IconsCache.get_icon(icon_def) diff --git a/client/ayon_core/tools/utils/__init__.py b/client/ayon_core/tools/utils/__init__.py index 445b4d9b97..39570a4813 100644 --- a/client/ayon_core/tools/utils/__init__.py +++ b/client/ayon_core/tools/utils/__init__.py @@ -37,6 +37,7 @@ from .lib import ( get_qt_app, get_ayon_qt_app, get_openpype_qt_app, + get_qt_icon, ) from .models import ( @@ -96,6 +97,7 @@ __all__ = ( "get_qt_app", "get_ayon_qt_app", "get_openpype_qt_app", + "get_qt_icon", "RecursiveSortFilterProxyModel", diff --git a/client/ayon_core/tools/utils/lib.py b/client/ayon_core/tools/utils/lib.py index 741fc1f335..8bcfe8b985 100644 --- a/client/ayon_core/tools/utils/lib.py +++ b/client/ayon_core/tools/utils/lib.py @@ -1,6 +1,7 @@ import os import sys import contextlib +from functools import partial from qtpy import QtWidgets, QtCore, QtGui import qtawesome @@ -195,51 +196,6 @@ def get_openpype_qt_app(): return get_ayon_qt_app() -class _Cache: - 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 _Cache.icons: - return _Cache.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 - used_variant = None - for variant in variants: - try: - icon = qtawesome.icon(variant, color=icon_color) - used_variant = variant - break - except Exception: - pass - - if used_variant is None: - log.info("Didn't find icon \"{}\"".format(icon_name)) - - elif used_variant != icon_name: - log.debug("Icon \"{}\" was not found \"{}\" is used instead".format( - icon_name, used_variant - )) - - _Cache.icons[full_icon_name] = icon - return icon - - -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) - - def iter_model_rows(model, column, include_root=False): """Iterate over all row indices in a model""" indices = [QtCore.QModelIndex()] # start iteration at root @@ -457,3 +413,156 @@ def get_warning_pixmap(color=None): color = get_objected_colors("delete-btn-bg").get_qcolor() return paint_image_with_color(src_image, color) + + +class RefreshThread(QtCore.QThread): + refresh_finished = QtCore.Signal(str) + + def __init__(self, thread_id, func, *args, **kwargs): + super(RefreshThread, self).__init__() + self._id = thread_id + self._callback = partial(func, *args, **kwargs) + self._exception = None + self._result = None + self.finished.connect(self._on_finish_callback) + + @property + def id(self): + return self._id + + @property + def failed(self): + return self._exception is not None + + def run(self): + try: + self._result = self._callback() + except Exception as exc: + self._exception = exc + + def get_result(self): + return self._result + + def _on_finish_callback(self): + """Trigger custom signal with thread id. + + Listening for 'finished' signal we make sure that execution of thread + finished and QThread object can be safely deleted. + """ + + self.refresh_finished.emit(self.id) + + +class _IconsCache: + """Cache for icons.""" + + _cache = {} + _default = None + _qtawesome_cache = {} + + @classmethod + def _get_cache_key(cls, icon_def): + parts = [] + icon_type = icon_def["type"] + if icon_type == "path": + parts = [icon_type, icon_def["path"]] + + elif icon_type == "awesome-font": + parts = [icon_type, icon_def["name"], icon_def["color"]] + return "|".join(parts) + + @classmethod + def get_icon(cls, icon_def): + if not icon_def: + return None + icon_type = icon_def["type"] + cache_key = cls._get_cache_key(icon_def) + cache = cls._cache.get(cache_key) + if cache is not None: + return cache + + icon = None + if icon_type == "path": + path = icon_def["path"] + if os.path.exists(path): + icon = QtGui.QIcon(path) + + elif icon_type == "awesome-font": + icon_name = icon_def["name"] + icon_color = icon_def["color"] + icon = cls.get_qta_icon_by_name_and_color(icon_name, icon_color) + if icon is None: + icon = cls.get_qta_icon_by_name_and_color( + "fa.{}".format(icon_name), icon_color) + if icon is None: + icon = cls.get_default() + cls._cache[cache_key] = icon + return icon + + @classmethod + def get_default(cls): + pix = QtGui.QPixmap(1, 1) + pix.fill(QtCore.Qt.transparent) + return QtGui.QIcon(pix) + + @classmethod + def get_qta_icon_by_name_and_color(cls, 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 cls._qtawesome_cache: + return cls._qtawesome_cache[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 + used_variant = None + for variant in variants: + try: + icon = qtawesome.icon(variant, color=icon_color) + used_variant = variant + break + except Exception: + pass + + if used_variant is None: + log.info("Didn't find icon \"{}\"".format(icon_name)) + + elif used_variant != icon_name: + log.debug("Icon \"{}\" was not found \"{}\" is used instead".format( + icon_name, used_variant + )) + + cls._qtawesome_cache[full_icon_name] = icon + return icon + + +def get_qt_icon(icon_def): + """Returns icon from cache or creates new one. + + Args: + icon_def (dict[str, Any]): Icon definition. + + Returns: + QtGui.QIcon: Icon. + + """ + return _IconsCache.get_icon(icon_def) + + +def get_qta_icon_by_name_and_color(icon_name, icon_color): + """Returns icon from cache or creates new one. + + Args: + icon_name (str): Icon name. + icon_color (str): Icon color. + + Returns: + QtGui.QIcon: Icon. + + """ + return _IconsCache.get_qta_icon_by_name_and_color(icon_name, icon_color) From fb3df8c8067e66479815dbc8635f88ee5e663be2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Mar 2024 17:59:09 +0100 Subject: [PATCH 570/573] move ayon utils widgets to utils --- .../tools/ayon_utils/widgets/__init__.py | 51 ------------------- client/ayon_core/tools/utils/__init__.py | 39 ++++++++++++++ .../widgets => utils}/folders_widget.py | 9 ++-- .../widgets => utils}/projects_widget.py | 3 +- .../widgets => utils}/tasks_widget.py | 4 +- 5 files changed, 47 insertions(+), 59 deletions(-) delete mode 100644 client/ayon_core/tools/ayon_utils/widgets/__init__.py rename client/ayon_core/tools/{ayon_utils/widgets => utils}/folders_widget.py (99%) rename client/ayon_core/tools/{ayon_utils/widgets => utils}/projects_widget.py (99%) rename client/ayon_core/tools/{ayon_utils/widgets => utils}/tasks_widget.py (99%) diff --git a/client/ayon_core/tools/ayon_utils/widgets/__init__.py b/client/ayon_core/tools/ayon_utils/widgets/__init__.py deleted file mode 100644 index a62bab6751..0000000000 --- a/client/ayon_core/tools/ayon_utils/widgets/__init__.py +++ /dev/null @@ -1,51 +0,0 @@ -from .projects_widget import ( - # ProjectsWidget, - ProjectsCombobox, - ProjectsQtModel, - ProjectSortFilterProxy, - PROJECT_NAME_ROLE, - PROJECT_IS_CURRENT_ROLE, - PROJECT_IS_ACTIVE_ROLE, - PROJECT_IS_LIBRARY_ROLE, -) - -from .folders_widget import ( - FoldersWidget, - FoldersQtModel, - FOLDERS_MODEL_SENDER_NAME, - SimpleFoldersWidget, -) - -from .tasks_widget import ( - TasksWidget, - TasksQtModel, - TASKS_MODEL_SENDER_NAME, -) -from .utils import ( - get_qt_icon, - RefreshThread, -) - - -__all__ = ( - # "ProjectsWidget", - "ProjectsCombobox", - "ProjectsQtModel", - "ProjectSortFilterProxy", - "PROJECT_NAME_ROLE", - "PROJECT_IS_CURRENT_ROLE", - "PROJECT_IS_ACTIVE_ROLE", - "PROJECT_IS_LIBRARY_ROLE", - - "FoldersWidget", - "FoldersQtModel", - "FOLDERS_MODEL_SENDER_NAME", - "SimpleFoldersWidget", - - "TasksWidget", - "TasksQtModel", - "TASKS_MODEL_SENDER_NAME", - - "get_qt_icon", - "RefreshThread", -) diff --git a/client/ayon_core/tools/utils/__init__.py b/client/ayon_core/tools/utils/__init__.py index 39570a4813..4b5fbeaf67 100644 --- a/client/ayon_core/tools/utils/__init__.py +++ b/client/ayon_core/tools/utils/__init__.py @@ -56,6 +56,28 @@ from .dialogs import ( SimplePopup, PopupUpdateKeys, ) +from .projects_widget import ( + ProjectsCombobox, + ProjectsQtModel, + ProjectSortFilterProxy, + PROJECT_NAME_ROLE, + PROJECT_IS_CURRENT_ROLE, + PROJECT_IS_ACTIVE_ROLE, + PROJECT_IS_LIBRARY_ROLE, +) + +from .folders_widget import ( + FoldersWidget, + FoldersQtModel, + FOLDERS_MODEL_SENDER_NAME, + SimpleFoldersWidget, +) + +from .tasks_widget import ( + TasksWidget, + TasksQtModel, + TASKS_MODEL_SENDER_NAME, +) __all__ = ( @@ -115,4 +137,21 @@ __all__ = ( "ScrollMessageBox", "SimplePopup", "PopupUpdateKeys", + + "ProjectsCombobox", + "ProjectsQtModel", + "ProjectSortFilterProxy", + "PROJECT_NAME_ROLE", + "PROJECT_IS_CURRENT_ROLE", + "PROJECT_IS_ACTIVE_ROLE", + "PROJECT_IS_LIBRARY_ROLE", + + "FoldersWidget", + "FoldersQtModel", + "FOLDERS_MODEL_SENDER_NAME", + "SimpleFoldersWidget", + + "TasksWidget", + "TasksQtModel", + "TASKS_MODEL_SENDER_NAME", ) diff --git a/client/ayon_core/tools/ayon_utils/widgets/folders_widget.py b/client/ayon_core/tools/utils/folders_widget.py similarity index 99% rename from client/ayon_core/tools/ayon_utils/widgets/folders_widget.py rename to client/ayon_core/tools/utils/folders_widget.py index e42a5b635c..a2519f90c2 100644 --- a/client/ayon_core/tools/ayon_utils/widgets/folders_widget.py +++ b/client/ayon_core/tools/utils/folders_widget.py @@ -7,12 +7,11 @@ from ayon_core.tools.ayon_utils.models import ( HierarchyModel, HierarchyExpectedSelection, ) -from ayon_core.tools.utils import ( - RecursiveSortFilterProxyModel, - TreeView, -) -from .utils import RefreshThread, get_qt_icon +from .models import RecursiveSortFilterProxyModel +from .views import TreeView +from .lib import RefreshThread, get_qt_icon + FOLDERS_MODEL_SENDER_NAME = "qt_folders_model" FOLDER_ID_ROLE = QtCore.Qt.UserRole + 1 diff --git a/client/ayon_core/tools/ayon_utils/widgets/projects_widget.py b/client/ayon_core/tools/utils/projects_widget.py similarity index 99% rename from client/ayon_core/tools/ayon_utils/widgets/projects_widget.py rename to client/ayon_core/tools/utils/projects_widget.py index 79ffc77640..e4ac69198a 100644 --- a/client/ayon_core/tools/ayon_utils/widgets/projects_widget.py +++ b/client/ayon_core/tools/utils/projects_widget.py @@ -1,7 +1,8 @@ from qtpy import QtWidgets, QtCore, QtGui from ayon_core.tools.ayon_utils.models import PROJECTS_MODEL_SENDER -from .utils import RefreshThread, get_qt_icon + +from .lib import RefreshThread, get_qt_icon PROJECT_NAME_ROLE = QtCore.Qt.UserRole + 1 PROJECT_IS_ACTIVE_ROLE = QtCore.Qt.UserRole + 2 diff --git a/client/ayon_core/tools/ayon_utils/widgets/tasks_widget.py b/client/ayon_core/tools/utils/tasks_widget.py similarity index 99% rename from client/ayon_core/tools/ayon_utils/widgets/tasks_widget.py rename to client/ayon_core/tools/utils/tasks_widget.py index cfe901c492..0ff8e8a5c1 100644 --- a/client/ayon_core/tools/ayon_utils/widgets/tasks_widget.py +++ b/client/ayon_core/tools/utils/tasks_widget.py @@ -1,9 +1,9 @@ from qtpy import QtWidgets, QtGui, QtCore from ayon_core.style import get_disabled_entity_icon_color -from ayon_core.tools.utils import DeselectableTreeView -from .utils import RefreshThread, get_qt_icon +from .views import DeselectableTreeView +from .lib import RefreshThread, get_qt_icon TASKS_MODEL_SENDER_NAME = "qt_tasks_model" ITEM_ID_ROLE = QtCore.Qt.UserRole + 1 From 95a67b666fb9e5c043d87c03762ec1fabb07ab47 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Mar 2024 17:59:41 +0100 Subject: [PATCH 571/573] import widgets from new locations --- client/ayon_core/hosts/houdini/api/usd.py | 7 +++++-- client/ayon_core/tools/context_dialog/window.py | 6 ++---- client/ayon_core/tools/launcher/ui/actions_widget.py | 2 +- client/ayon_core/tools/launcher/ui/hierarchy_page.py | 2 +- client/ayon_core/tools/launcher/ui/projects_widget.py | 5 +++-- client/ayon_core/tools/loader/ui/actions_utils.py | 2 +- client/ayon_core/tools/loader/ui/folders_widget.py | 4 ++-- .../ayon_core/tools/loader/ui/product_types_widget.py | 2 +- client/ayon_core/tools/loader/ui/products_model.py | 2 +- client/ayon_core/tools/loader/ui/repres_widget.py | 2 +- client/ayon_core/tools/loader/ui/window.py | 2 +- .../tools/publisher/widgets/create_context_widgets.py | 2 +- .../tools/publisher/widgets/folders_dialog.py | 3 +-- .../ayon_core/tools/publisher/widgets/tasks_model.py | 10 ++++++++-- client/ayon_core/tools/push_to_project/ui/window.py | 2 -- client/ayon_core/tools/sceneinventory/model.py | 2 +- .../sceneinventory/switch_dialog/folders_input.py | 2 +- client/ayon_core/tools/traypublisher/window.py | 5 +++-- client/ayon_core/tools/workfiles/widgets/window.py | 8 ++++++-- 19 files changed, 40 insertions(+), 30 deletions(-) diff --git a/client/ayon_core/hosts/houdini/api/usd.py b/client/ayon_core/hosts/houdini/api/usd.py index 443a52bc37..ed33fbf590 100644 --- a/client/ayon_core/hosts/houdini/api/usd.py +++ b/client/ayon_core/hosts/houdini/api/usd.py @@ -8,8 +8,11 @@ from qtpy import QtWidgets, QtCore, QtGui from ayon_core import style from ayon_core.pipeline import get_current_project_name -from ayon_core.tools.utils import PlaceholderLineEdit, RefreshButton -from ayon_core.tools.ayon_utils.widgets import SimpleFoldersWidget +from ayon_core.tools.utils import ( + PlaceholderLineEdit, + RefreshButton, + SimpleFoldersWidget, +) from pxr import Sdf diff --git a/client/ayon_core/tools/context_dialog/window.py b/client/ayon_core/tools/context_dialog/window.py index b145e77515..df5dd868e8 100644 --- a/client/ayon_core/tools/context_dialog/window.py +++ b/client/ayon_core/tools/context_dialog/window.py @@ -10,15 +10,13 @@ from ayon_core.tools.ayon_utils.models import ( ProjectsModel, HierarchyModel, ) -from ayon_core.tools.ayon_utils.widgets import ( +from ayon_core.tools.utils import ( ProjectsCombobox, FoldersWidget, TasksWidget, -) -from ayon_core.tools.utils.lib import ( - center_window, get_ayon_qt_app, ) +from ayon_core.tools.utils.lib import center_window class SelectionModel(object): diff --git a/client/ayon_core/tools/launcher/ui/actions_widget.py b/client/ayon_core/tools/launcher/ui/actions_widget.py index 617f3b0c91..a225827418 100644 --- a/client/ayon_core/tools/launcher/ui/actions_widget.py +++ b/client/ayon_core/tools/launcher/ui/actions_widget.py @@ -4,7 +4,7 @@ import collections from qtpy import QtWidgets, QtCore, QtGui from ayon_core.tools.flickcharm import FlickCharm -from ayon_core.tools.ayon_utils.widgets import get_qt_icon +from ayon_core.tools.utils import get_qt_icon from .resources import get_options_image_path diff --git a/client/ayon_core/tools/launcher/ui/hierarchy_page.py b/client/ayon_core/tools/launcher/ui/hierarchy_page.py index 5b5f88a802..226a57930b 100644 --- a/client/ayon_core/tools/launcher/ui/hierarchy_page.py +++ b/client/ayon_core/tools/launcher/ui/hierarchy_page.py @@ -6,7 +6,7 @@ from ayon_core.tools.utils import ( SquareButton, RefreshButton, ) -from ayon_core.tools.ayon_utils.widgets import ( +from ayon_core.tools.utils import ( ProjectsCombobox, FoldersWidget, TasksWidget, diff --git a/client/ayon_core/tools/launcher/ui/projects_widget.py b/client/ayon_core/tools/launcher/ui/projects_widget.py index 729caf3232..39fc67fc0f 100644 --- a/client/ayon_core/tools/launcher/ui/projects_widget.py +++ b/client/ayon_core/tools/launcher/ui/projects_widget.py @@ -1,8 +1,9 @@ from qtpy import QtWidgets, QtCore from ayon_core.tools.flickcharm import FlickCharm -from ayon_core.tools.utils import PlaceholderLineEdit, RefreshButton -from ayon_core.tools.ayon_utils.widgets import ( +from ayon_core.tools.utils import ( + PlaceholderLineEdit, + RefreshButton, ProjectsQtModel, ProjectSortFilterProxy, ) diff --git a/client/ayon_core/tools/loader/ui/actions_utils.py b/client/ayon_core/tools/loader/ui/actions_utils.py index bf6ab6eeb5..5a988ef4c2 100644 --- a/client/ayon_core/tools/loader/ui/actions_utils.py +++ b/client/ayon_core/tools/loader/ui/actions_utils.py @@ -10,7 +10,7 @@ from ayon_core.tools.utils.widgets import ( OptionalAction, OptionDialog, ) -from ayon_core.tools.ayon_utils.widgets import get_qt_icon +from ayon_core.tools.utils import get_qt_icon def show_actions_menu(action_items, global_point, one_item_selected, parent): diff --git a/client/ayon_core/tools/loader/ui/folders_widget.py b/client/ayon_core/tools/loader/ui/folders_widget.py index 34881ab49d..7b146456da 100644 --- a/client/ayon_core/tools/loader/ui/folders_widget.py +++ b/client/ayon_core/tools/loader/ui/folders_widget.py @@ -7,11 +7,11 @@ from ayon_core.tools.utils import ( ) from ayon_core.style import get_objected_colors -from ayon_core.tools.ayon_utils.widgets import ( +from ayon_core.tools.utils import ( FoldersQtModel, FOLDERS_MODEL_SENDER_NAME, ) -from ayon_core.tools.ayon_utils.widgets.folders_widget import FOLDER_ID_ROLE +from ayon_core.tools.utils.folders_widget import FOLDER_ID_ROLE if qtpy.API == "pyside": from PySide.QtGui import QStyleOptionViewItemV4 diff --git a/client/ayon_core/tools/loader/ui/product_types_widget.py b/client/ayon_core/tools/loader/ui/product_types_widget.py index 26244517ec..180994fd7f 100644 --- a/client/ayon_core/tools/loader/ui/product_types_widget.py +++ b/client/ayon_core/tools/loader/ui/product_types_widget.py @@ -1,6 +1,6 @@ from qtpy import QtWidgets, QtGui, QtCore -from ayon_core.tools.ayon_utils.widgets import get_qt_icon +from ayon_core.tools.utils import get_qt_icon PRODUCT_TYPE_ROLE = QtCore.Qt.UserRole + 1 diff --git a/client/ayon_core/tools/loader/ui/products_model.py b/client/ayon_core/tools/loader/ui/products_model.py index 331efad68a..c51172849a 100644 --- a/client/ayon_core/tools/loader/ui/products_model.py +++ b/client/ayon_core/tools/loader/ui/products_model.py @@ -4,7 +4,7 @@ import qtawesome from qtpy import QtGui, QtCore from ayon_core.style import get_default_entity_icon_color -from ayon_core.tools.ayon_utils.widgets import get_qt_icon +from ayon_core.tools.utils import get_qt_icon PRODUCTS_MODEL_SENDER_NAME = "qt_products_model" diff --git a/client/ayon_core/tools/loader/ui/repres_widget.py b/client/ayon_core/tools/loader/ui/repres_widget.py index 27db8dda40..3b6b8f94bf 100644 --- a/client/ayon_core/tools/loader/ui/repres_widget.py +++ b/client/ayon_core/tools/loader/ui/repres_widget.py @@ -4,7 +4,7 @@ from qtpy import QtWidgets, QtGui, QtCore import qtawesome from ayon_core.style import get_default_entity_icon_color -from ayon_core.tools.ayon_utils.widgets import get_qt_icon +from ayon_core.tools.utils import get_qt_icon from ayon_core.tools.utils import DeselectableTreeView from .actions_utils import show_actions_menu diff --git a/client/ayon_core/tools/loader/ui/window.py b/client/ayon_core/tools/loader/ui/window.py index 104b64d81c..3a6f4679fa 100644 --- a/client/ayon_core/tools/loader/ui/window.py +++ b/client/ayon_core/tools/loader/ui/window.py @@ -10,7 +10,7 @@ from ayon_core.tools.utils import ( GoToCurrentButton, ) from ayon_core.tools.utils.lib import center_window -from ayon_core.tools.ayon_utils.widgets import ProjectsCombobox +from ayon_core.tools.utils import ProjectsCombobox from ayon_core.tools.loader.control import LoaderController from .folders_widget import LoaderFoldersWidget diff --git a/client/ayon_core/tools/publisher/widgets/create_context_widgets.py b/client/ayon_core/tools/publisher/widgets/create_context_widgets.py index d65a2ace8d..3ec1c491e8 100644 --- a/client/ayon_core/tools/publisher/widgets/create_context_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/create_context_widgets.py @@ -4,7 +4,7 @@ from ayon_core.lib.events import QueuedEventSystem from ayon_core.tools.utils import PlaceholderLineEdit, GoToCurrentButton from ayon_core.tools.ayon_utils.models import HierarchyExpectedSelection -from ayon_core.tools.ayon_utils.widgets import FoldersWidget, TasksWidget +from ayon_core.tools.utils import FoldersWidget, TasksWidget class CreateSelectionModel(object): diff --git a/client/ayon_core/tools/publisher/widgets/folders_dialog.py b/client/ayon_core/tools/publisher/widgets/folders_dialog.py index 8f93264b2e..03336e10a6 100644 --- a/client/ayon_core/tools/publisher/widgets/folders_dialog.py +++ b/client/ayon_core/tools/publisher/widgets/folders_dialog.py @@ -1,8 +1,7 @@ from qtpy import QtWidgets, QtCore, QtGui from ayon_core.lib.events import QueuedEventSystem -from ayon_core.tools.ayon_utils.widgets import FoldersWidget -from ayon_core.tools.utils import PlaceholderLineEdit +from ayon_core.tools.utils import PlaceholderLineEdit, FoldersWidget class FoldersDialogController: diff --git a/client/ayon_core/tools/publisher/widgets/tasks_model.py b/client/ayon_core/tools/publisher/widgets/tasks_model.py index 8f00dc37a2..e36de80fcf 100644 --- a/client/ayon_core/tools/publisher/widgets/tasks_model.py +++ b/client/ayon_core/tools/publisher/widgets/tasks_model.py @@ -1,6 +1,7 @@ from qtpy import QtWidgets, QtCore, QtGui -from ayon_core.tools.utils.lib import get_default_task_icon +from ayon_core.style import get_default_entity_icon_color +from ayon_core.tools.utils import get_qt_icon TASK_NAME_ROLE = QtCore.Qt.UserRole + 1 TASK_TYPE_ROLE = QtCore.Qt.UserRole + 2 @@ -121,6 +122,11 @@ class TasksModel(QtGui.QStandardItemModel): item = self._items_by_name.pop(task_name) root_item.removeRow(item.row()) + icon = get_qt_icon({ + "type": "awesome-font", + "name": "fa.male", + "color": get_default_entity_icon_color(), + }) new_items = [] for task_name in new_task_names: if task_name in self._items_by_name: @@ -129,7 +135,7 @@ class TasksModel(QtGui.QStandardItemModel): item = QtGui.QStandardItem(task_name) item.setData(task_name, TASK_NAME_ROLE) if task_name: - item.setData(get_default_task_icon(), QtCore.Qt.DecorationRole) + item.setData(icon, QtCore.Qt.DecorationRole) self._items_by_name[task_name] = item new_items.append(item) diff --git a/client/ayon_core/tools/push_to_project/ui/window.py b/client/ayon_core/tools/push_to_project/ui/window.py index bc2fc6bf96..4d64509afd 100644 --- a/client/ayon_core/tools/push_to_project/ui/window.py +++ b/client/ayon_core/tools/push_to_project/ui/window.py @@ -5,8 +5,6 @@ from ayon_core.tools.utils import ( PlaceholderLineEdit, SeparatorWidget, set_style_property, -) -from ayon_core.tools.ayon_utils.widgets import ( ProjectsCombobox, FoldersWidget, TasksWidget, diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index e53b6aa4c3..df0dea7a3d 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -13,8 +13,8 @@ from ayon_core.pipeline import ( HeroVersionType, ) from ayon_core.style import get_default_entity_icon_color +from ayon_core.tools.utils import get_qt_icon from ayon_core.tools.utils.models import TreeModel, Item -from ayon_core.tools.ayon_utils.widgets import get_qt_icon def walk_hierarchy(node): diff --git a/client/ayon_core/tools/sceneinventory/switch_dialog/folders_input.py b/client/ayon_core/tools/sceneinventory/switch_dialog/folders_input.py index e46c28474f..3137e70214 100644 --- a/client/ayon_core/tools/sceneinventory/switch_dialog/folders_input.py +++ b/client/ayon_core/tools/sceneinventory/switch_dialog/folders_input.py @@ -5,8 +5,8 @@ from ayon_core.tools.utils import ( PlaceholderLineEdit, BaseClickableFrame, set_style_property, + FoldersWidget, ) -from ayon_core.tools.ayon_utils.widgets import FoldersWidget NOT_SET = object() diff --git a/client/ayon_core/tools/traypublisher/window.py b/client/ayon_core/tools/traypublisher/window.py index 988c22819a..ffa5d5b606 100644 --- a/client/ayon_core/tools/traypublisher/window.py +++ b/client/ayon_core/tools/traypublisher/window.py @@ -16,9 +16,10 @@ from ayon_core.pipeline import install_host from ayon_core.hosts.traypublisher.api import TrayPublisherHost from ayon_core.tools.publisher.control_qt import QtPublisherController from ayon_core.tools.publisher.window import PublisherWindow -from ayon_core.tools.utils import PlaceholderLineEdit, get_ayon_qt_app from ayon_core.tools.ayon_utils.models import ProjectsModel -from ayon_core.tools.ayon_utils.widgets import ( +from ayon_core.tools.utils import ( + PlaceholderLineEdit, + get_ayon_qt_app, ProjectsQtModel, ProjectSortFilterProxy, PROJECT_NAME_ROLE, diff --git a/client/ayon_core/tools/workfiles/widgets/window.py b/client/ayon_core/tools/workfiles/widgets/window.py index 86a84b6195..8a2617d270 100644 --- a/client/ayon_core/tools/workfiles/widgets/window.py +++ b/client/ayon_core/tools/workfiles/widgets/window.py @@ -6,9 +6,13 @@ from ayon_core.tools.utils import ( MessageOverlayObject, ) -from ayon_core.tools.ayon_utils.widgets import FoldersWidget, TasksWidget from ayon_core.tools.workfiles.control import BaseWorkfileController -from ayon_core.tools.utils import GoToCurrentButton, RefreshButton +from ayon_core.tools.utils import ( + GoToCurrentButton, + RefreshButton, + FoldersWidget, + TasksWidget, +) from .side_panel import SidePanelWidget from .files_widget import FilesWidget From 20a9d4f9724c1f0a1ab79a43482741fc236e347c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Mar 2024 18:01:45 +0100 Subject: [PATCH 572/573] move models to 'common_models' in tools --- .../tools/{ayon_utils/models => common_models}/__init__.py | 0 .../ayon_core/tools/{ayon_utils/models => common_models}/cache.py | 0 .../tools/{ayon_utils/models => common_models}/hierarchy.py | 0 .../tools/{ayon_utils/models => common_models}/projects.py | 0 .../tools/{ayon_utils/models => common_models}/selection.py | 0 .../tools/{ayon_utils/models => common_models}/thumbnails.py | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename client/ayon_core/tools/{ayon_utils/models => common_models}/__init__.py (100%) rename client/ayon_core/tools/{ayon_utils/models => common_models}/cache.py (100%) rename client/ayon_core/tools/{ayon_utils/models => common_models}/hierarchy.py (100%) rename client/ayon_core/tools/{ayon_utils/models => common_models}/projects.py (100%) rename client/ayon_core/tools/{ayon_utils/models => common_models}/selection.py (100%) rename client/ayon_core/tools/{ayon_utils/models => common_models}/thumbnails.py (100%) diff --git a/client/ayon_core/tools/ayon_utils/models/__init__.py b/client/ayon_core/tools/common_models/__init__.py similarity index 100% rename from client/ayon_core/tools/ayon_utils/models/__init__.py rename to client/ayon_core/tools/common_models/__init__.py diff --git a/client/ayon_core/tools/ayon_utils/models/cache.py b/client/ayon_core/tools/common_models/cache.py similarity index 100% rename from client/ayon_core/tools/ayon_utils/models/cache.py rename to client/ayon_core/tools/common_models/cache.py diff --git a/client/ayon_core/tools/ayon_utils/models/hierarchy.py b/client/ayon_core/tools/common_models/hierarchy.py similarity index 100% rename from client/ayon_core/tools/ayon_utils/models/hierarchy.py rename to client/ayon_core/tools/common_models/hierarchy.py diff --git a/client/ayon_core/tools/ayon_utils/models/projects.py b/client/ayon_core/tools/common_models/projects.py similarity index 100% rename from client/ayon_core/tools/ayon_utils/models/projects.py rename to client/ayon_core/tools/common_models/projects.py diff --git a/client/ayon_core/tools/ayon_utils/models/selection.py b/client/ayon_core/tools/common_models/selection.py similarity index 100% rename from client/ayon_core/tools/ayon_utils/models/selection.py rename to client/ayon_core/tools/common_models/selection.py diff --git a/client/ayon_core/tools/ayon_utils/models/thumbnails.py b/client/ayon_core/tools/common_models/thumbnails.py similarity index 100% rename from client/ayon_core/tools/ayon_utils/models/thumbnails.py rename to client/ayon_core/tools/common_models/thumbnails.py From 9770d8ffa9263b515fc89f1d110472b6dac4ebe5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Mar 2024 18:02:13 +0100 Subject: [PATCH 573/573] use new location of tools models --- client/ayon_core/tools/context_dialog/window.py | 2 +- client/ayon_core/tools/launcher/control.py | 2 +- client/ayon_core/tools/launcher/ui/projects_widget.py | 2 +- client/ayon_core/tools/loader/control.py | 2 +- client/ayon_core/tools/loader/models/actions.py | 2 +- client/ayon_core/tools/loader/models/products.py | 2 +- client/ayon_core/tools/loader/models/site_sync.py | 2 +- client/ayon_core/tools/publisher/control.py | 2 +- .../ayon_core/tools/publisher/widgets/create_context_widgets.py | 2 +- client/ayon_core/tools/push_to_project/control.py | 2 +- client/ayon_core/tools/sceneinventory/control.py | 2 +- client/ayon_core/tools/traypublisher/window.py | 2 +- client/ayon_core/tools/utils/folders_widget.py | 2 +- client/ayon_core/tools/utils/projects_widget.py | 2 +- client/ayon_core/tools/workfiles/control.py | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/client/ayon_core/tools/context_dialog/window.py b/client/ayon_core/tools/context_dialog/window.py index df5dd868e8..828d771142 100644 --- a/client/ayon_core/tools/context_dialog/window.py +++ b/client/ayon_core/tools/context_dialog/window.py @@ -6,7 +6,7 @@ from qtpy import QtWidgets, QtCore, QtGui from ayon_core import style from ayon_core.lib.events import QueuedEventSystem -from ayon_core.tools.ayon_utils.models import ( +from ayon_core.tools.common_models import ( ProjectsModel, HierarchyModel, ) diff --git a/client/ayon_core/tools/launcher/control.py b/client/ayon_core/tools/launcher/control.py index 8780b211f1..abd0cd78d8 100644 --- a/client/ayon_core/tools/launcher/control.py +++ b/client/ayon_core/tools/launcher/control.py @@ -1,7 +1,7 @@ from ayon_core.lib import Logger from ayon_core.lib.events import QueuedEventSystem from ayon_core.settings import get_project_settings -from ayon_core.tools.ayon_utils.models import ProjectsModel, HierarchyModel +from ayon_core.tools.common_models import ProjectsModel, HierarchyModel from .abstract import AbstractLauncherFrontEnd, AbstractLauncherBackend from .models import LauncherSelectionModel, ActionsModel diff --git a/client/ayon_core/tools/launcher/ui/projects_widget.py b/client/ayon_core/tools/launcher/ui/projects_widget.py index 39fc67fc0f..e2af54b55d 100644 --- a/client/ayon_core/tools/launcher/ui/projects_widget.py +++ b/client/ayon_core/tools/launcher/ui/projects_widget.py @@ -7,7 +7,7 @@ from ayon_core.tools.utils import ( ProjectsQtModel, ProjectSortFilterProxy, ) -from ayon_core.tools.ayon_utils.models import PROJECTS_MODEL_SENDER +from ayon_core.tools.common_models import PROJECTS_MODEL_SENDER class ProjectIconView(QtWidgets.QListView): diff --git a/client/ayon_core/tools/loader/control.py b/client/ayon_core/tools/loader/control.py index 5995bd2cae..d8562f50ca 100644 --- a/client/ayon_core/tools/loader/control.py +++ b/client/ayon_core/tools/loader/control.py @@ -6,7 +6,7 @@ import ayon_api from ayon_core.lib.events import QueuedEventSystem from ayon_core.pipeline import Anatomy, get_current_context from ayon_core.host import ILoadHost -from ayon_core.tools.ayon_utils.models import ( +from ayon_core.tools.common_models import ( ProjectsModel, HierarchyModel, NestedCacheItem, diff --git a/client/ayon_core/tools/loader/models/actions.py b/client/ayon_core/tools/loader/models/actions.py index aab5ba49d1..ad2993af50 100644 --- a/client/ayon_core/tools/loader/models/actions.py +++ b/client/ayon_core/tools/loader/models/actions.py @@ -17,7 +17,7 @@ from ayon_core.pipeline.load import ( LoadError, IncompatibleLoaderError, ) -from ayon_core.tools.ayon_utils.models import NestedCacheItem +from ayon_core.tools.common_models import NestedCacheItem from ayon_core.tools.loader.abstract import ActionItem ACTIONS_MODEL_SENDER = "actions.model" diff --git a/client/ayon_core/tools/loader/models/products.py b/client/ayon_core/tools/loader/models/products.py index 63547bef8b..812446a012 100644 --- a/client/ayon_core/tools/loader/models/products.py +++ b/client/ayon_core/tools/loader/models/products.py @@ -6,7 +6,7 @@ import ayon_api from ayon_api.operations import OperationsSession from ayon_core.style import get_default_entity_icon_color -from ayon_core.tools.ayon_utils.models import NestedCacheItem +from ayon_core.tools.common_models import NestedCacheItem from ayon_core.tools.loader.abstract import ( ProductTypeItem, ProductItem, diff --git a/client/ayon_core/tools/loader/models/site_sync.py b/client/ayon_core/tools/loader/models/site_sync.py index daa9f7ba50..a589cf7fbe 100644 --- a/client/ayon_core/tools/loader/models/site_sync.py +++ b/client/ayon_core/tools/loader/models/site_sync.py @@ -4,7 +4,7 @@ from ayon_api import get_representations, get_versions_links from ayon_core.lib import Logger from ayon_core.addon import AddonsManager -from ayon_core.tools.ayon_utils.models import NestedCacheItem +from ayon_core.tools.common_models import NestedCacheItem from ayon_core.tools.loader.abstract import ActionItem DOWNLOAD_IDENTIFIER = "sitesync.download" diff --git a/client/ayon_core/tools/publisher/control.py b/client/ayon_core/tools/publisher/control.py index aaca0fea10..ede772b917 100644 --- a/client/ayon_core/tools/publisher/control.py +++ b/client/ayon_core/tools/publisher/control.py @@ -38,7 +38,7 @@ from ayon_core.pipeline.create.context import ( ConvertorsOperationFailed, ) from ayon_core.pipeline.publish import get_publish_instance_label -from ayon_core.tools.ayon_utils.models import HierarchyModel +from ayon_core.tools.common_models import HierarchyModel # Define constant for plugin orders offset PLUGIN_ORDER_OFFSET = 0.5 diff --git a/client/ayon_core/tools/publisher/widgets/create_context_widgets.py b/client/ayon_core/tools/publisher/widgets/create_context_widgets.py index 3ec1c491e8..61223bbe75 100644 --- a/client/ayon_core/tools/publisher/widgets/create_context_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/create_context_widgets.py @@ -3,7 +3,7 @@ from qtpy import QtWidgets, QtCore, QtGui from ayon_core.lib.events import QueuedEventSystem from ayon_core.tools.utils import PlaceholderLineEdit, GoToCurrentButton -from ayon_core.tools.ayon_utils.models import HierarchyExpectedSelection +from ayon_core.tools.common_models import HierarchyExpectedSelection from ayon_core.tools.utils import FoldersWidget, TasksWidget diff --git a/client/ayon_core/tools/push_to_project/control.py b/client/ayon_core/tools/push_to_project/control.py index d5acaadc2a..58447a8389 100644 --- a/client/ayon_core/tools/push_to_project/control.py +++ b/client/ayon_core/tools/push_to_project/control.py @@ -6,7 +6,7 @@ from ayon_core.settings import get_project_settings from ayon_core.lib import prepare_template_data from ayon_core.lib.events import QueuedEventSystem from ayon_core.pipeline.create import get_product_name_template -from ayon_core.tools.ayon_utils.models import ProjectsModel, HierarchyModel +from ayon_core.tools.common_models import ProjectsModel, HierarchyModel from .models import ( PushToProjectSelectionModel, diff --git a/client/ayon_core/tools/sceneinventory/control.py b/client/ayon_core/tools/sceneinventory/control.py index 16b889e855..77f4d60b22 100644 --- a/client/ayon_core/tools/sceneinventory/control.py +++ b/client/ayon_core/tools/sceneinventory/control.py @@ -6,7 +6,7 @@ from ayon_core.pipeline import ( registered_host, get_current_context, ) -from ayon_core.tools.ayon_utils.models import HierarchyModel +from ayon_core.tools.common_models import HierarchyModel from .models import SiteSyncModel diff --git a/client/ayon_core/tools/traypublisher/window.py b/client/ayon_core/tools/traypublisher/window.py index ffa5d5b606..4700e20531 100644 --- a/client/ayon_core/tools/traypublisher/window.py +++ b/client/ayon_core/tools/traypublisher/window.py @@ -16,7 +16,7 @@ from ayon_core.pipeline import install_host from ayon_core.hosts.traypublisher.api import TrayPublisherHost from ayon_core.tools.publisher.control_qt import QtPublisherController from ayon_core.tools.publisher.window import PublisherWindow -from ayon_core.tools.ayon_utils.models import ProjectsModel +from ayon_core.tools.common_models import ProjectsModel from ayon_core.tools.utils import ( PlaceholderLineEdit, get_ayon_qt_app, diff --git a/client/ayon_core/tools/utils/folders_widget.py b/client/ayon_core/tools/utils/folders_widget.py index a2519f90c2..2ad640de37 100644 --- a/client/ayon_core/tools/utils/folders_widget.py +++ b/client/ayon_core/tools/utils/folders_widget.py @@ -3,7 +3,7 @@ import collections from qtpy import QtWidgets, QtGui, QtCore from ayon_core.lib.events import QueuedEventSystem -from ayon_core.tools.ayon_utils.models import ( +from ayon_core.tools.common_models import ( HierarchyModel, HierarchyExpectedSelection, ) diff --git a/client/ayon_core/tools/utils/projects_widget.py b/client/ayon_core/tools/utils/projects_widget.py index e4ac69198a..fd361493ab 100644 --- a/client/ayon_core/tools/utils/projects_widget.py +++ b/client/ayon_core/tools/utils/projects_widget.py @@ -1,6 +1,6 @@ from qtpy import QtWidgets, QtCore, QtGui -from ayon_core.tools.ayon_utils.models import PROJECTS_MODEL_SENDER +from ayon_core.tools.common_models import PROJECTS_MODEL_SENDER from .lib import RefreshThread, get_qt_icon diff --git a/client/ayon_core/tools/workfiles/control.py b/client/ayon_core/tools/workfiles/control.py index 3111c4d443..7fa7af1662 100644 --- a/client/ayon_core/tools/workfiles/control.py +++ b/client/ayon_core/tools/workfiles/control.py @@ -15,7 +15,7 @@ from ayon_core.pipeline.context_tools import ( ) from ayon_core.pipeline.workfile import create_workdir_extra_folders -from ayon_core.tools.ayon_utils.models import ( +from ayon_core.tools.common_models import ( HierarchyModel, HierarchyExpectedSelection, ProjectsModel,