From 45bb933e801d703f527bc5813c649f29b3c5312e Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 25 Jan 2024 17:09:44 +0800 Subject: [PATCH 01/15] 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 @@ class PublishersModel(BaseSettingsModel): "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 1d83c6aaa8adcb77ba9b5395cceae064e4aa8e0a Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 29 Jan 2024 17:19:04 +0800 Subject: [PATCH 02/15] 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 03/15] 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 04/15] 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 @@ 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 @@ def get_invalid(cls, instance): @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 05/15] 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 @@ def get_invalid_model_name(cls, instance, name): 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 @@ class PublishersModel(BaseSettingsModel): "enabled": True, "optional": True, "active": False, - "regex": "(?P.*)_(GEO)" + "regex": "*_(?P.*)_(GEO)" }, "ValidateLoadedPlugin": { "enabled": False, From 6b9da9b1e83a1be30197d00d4aae198050590d33 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 2 Feb 2024 16:14:08 +0800 Subject: [PATCH 06/15] 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 @@ 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 12f78b17668c8f5034c60f8ba6921a5aa507c015 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 5 Feb 2024 20:12:22 +0800 Subject: [PATCH 07/15] 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 @@ class PublishersModel(BaseSettingsModel): "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 08/15] 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 @@ def process(self, context, plugin): 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 09/15] 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 @@ def process(self, context, plugin): 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 @@ def get_invalid_model_name(cls, instance, name): 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 10/15] 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 @@ def get_invalid_model_name(cls, instance, name): 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 05b97437b8f28bb43dc6ed303cbb6049f42c1316 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 8 Feb 2024 19:49:38 +0800 Subject: [PATCH 11/15] 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 @@ def process(self, context, plugin): 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 @@ def get_invalid_model_name(cls, instance, name): 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 12/15] 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 @@ def get_invalid_model_name(cls, 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((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 4a85b2ec45a05f066758b6be4e17588cdfd51598 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 21 Feb 2024 21:01:12 +0800 Subject: [PATCH 13/15] 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 @@ def process(self, context, plugin): 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 c25161d454ca05dc21abff0eacf885c2e6cddbd8 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 22 Feb 2024 18:26:54 +0800 Subject: [PATCH 14/15] 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 @@ def get_invalid_model_name(cls, instance, name): 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 fadf820fea6bcd4c40dd7a2ce43f43cf322a3b02 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Feb 2024 18:53:44 +0800 Subject: [PATCH 15/15] improve the code and the error message & docstring --- .../publish/help/validate_model_name.xml | 26 +++ .../plugins/publish/validate_model_name.py | 156 ++++++++++-------- 2 files changed, 114 insertions(+), 68 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 - - 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 + """Validate Model Name. + + 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 = "" + # defined by settings + regex = r"(.*)_(?P.*)_(GEO)" + # cache + regex_compiled = None + + def process(self, instance): + if not self.is_active(instance.data): + 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): - 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) + if not cls.regex: + cls.log.warning("No regex pattern set. Nothing to validate.") + return - return invalid + 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) - @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")) + for obj in members: + if cls.invalid_name(instance, obj): + invalid.append(obj) return invalid - def process(self, instance): - if not self.is_active(instance.data): - self.log.debug("Skipping Validate Model Name...") - return + @classmethod + def invalid_name(cls, instance, obj): + """Function to check the object has invalid name + regarding to the validation regex in the AYON setttings - invalid = self.get_invalid(instance) + 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