refactored publish plugins

This commit is contained in:
aardschok 2017-07-04 15:22:56 +02:00
parent ed730c02e6
commit e0ecc72e49
9 changed files with 225 additions and 67 deletions

View file

@ -1,10 +1,13 @@
# absolute_import is needed to counter the `module has no cmds error` in Maya
from __future__ import absolute_import
import pyblish.api
import os
import uuid
from maya import cmds
import pyblish.api
def get_errored_instances_from_context(context):
@ -34,7 +37,7 @@ def get_errored_plugins_from_data(context):
plugins = list()
results = context.data.get("results", [])
for result in results:
if result["success"] == True:
if result["success"] is True:
continue
plugins.append(result["plugin"])
@ -150,8 +153,6 @@ class GenerateUUIDsOnInvalidAction(pyblish.api.Action):
icon = "wrench" # Icon from Awesome Icon
def process(self, context, plugin):
import cbra.lib
import cbra.utils.maya.node_uuid as id_utils
self.log.info("Finding bad nodes..")
@ -182,15 +183,73 @@ class GenerateUUIDsOnInvalidAction(pyblish.api.Action):
# Parse context from current file
self.log.info("Parsing current context..")
try:
current_file = context.data['currentFile']
context = cbra.lib.parse_context(current_file)
except RuntimeError, e:
self.log.error("Can't generate UUIDs because scene isn't "
"in new-style pipeline: ".format(current_file))
raise e
print(">>> DEBUG CONTEXT :", context)
print(">>> DEBUG CONTEXT DATA:", context.data)
# Generate and add the ids to the nodes
ids = id_utils.generate_ids(context, invalid)
id_utils.add_ids(ids)
# # Generate and add the ids to the nodes
node_ids = self.generate_ids(context, invalid)
self.apply_ids(node_ids)
self.log.info("Generated ids on nodes: {0}".format(invalid))
def get_context(self, instance=None):
PROJECT = os.environ["AVALON_PROJECT"]
ASSET = instance.data.get("asset") or os.environ["AVALON_ASSET"]
SILO = os.environ["AVALON_SILO"]
LOCATION = os.getenv("AVALON_LOCATION")
return {"project": PROJECT,
"asset": ASSET,
"silo": SILO,
"location": LOCATION}
def generate_ids(self, context, nodes):
"""Generate cb UUIDs for nodes.
The identifiers are formatted like:
assets:character/test:bluey:46D221D9-4150-8E49-6B17-43B04BFC26B6
This is a concatenation of:
- entity (shots or assets)
- folders (parent hierarchy)
- asset (the name of the asset)
- uuid (unique id for node in the scene)
Raises:
RuntimeError: When context can't be parsed of the current asset
Returns:
dict: node, uuid dictionary
"""
# Make a copy of the context
data = context.copy()
# Define folders
node_ids = dict()
for node in nodes:
# Generate a unique ID per node
data['uuid'] = uuid.uuid4()
unique_id = "{asset}:{item}:{uuid}".format(**data)
node_ids[node] = unique_id
return node_ids
def apply_ids(self, node_ids):
"""Apply the created unique IDs to the node
Args:
node_ids (dict): each node with a unique id
Returns:
None
"""
attribute = "mbId"
for node, id in node_ids.items():
# check if node has attribute
if not cmds.attributeQuery(attribute, node=node, exists=True):
cmds.addAttr(node, longName=attribute, dataType="string")
cmds.setAttr("{}.{}".format(node, attribute), id)

View file

@ -456,7 +456,7 @@ def extract_alembic(file,
for key, value in options.items():
if isinstance(value, (list, tuple)):
for entry in value:
job_args.append("-{0} {1}".format(key=key, value=entry))
job_args.append("-{} {}".format(key, entry))
elif isinstance(value, bool):
job_args.append("-{0}".format(key))
else:

View file

@ -37,8 +37,8 @@ def get_look_attrs(node):
valid = [attr for attr in attrs if attr in SHAPE_ATTRS]
result.extend(valid)
if "cbId" in result:
result.remove("cbId")
if "mbID" in result:
result.remove("mbID")
return result
@ -87,7 +87,7 @@ class CollectLook(pyblish.api.InstancePlugin):
# Discover related object sets
self.log.info("Gathering sets..")
self.gather_sets(instance)
sets = self.gather_sets(instance)
# Lookup with absolute names (from root namespace)
instance_lookup = set([str(x) for x in cmds.ls(instance,
@ -95,9 +95,7 @@ class CollectLook(pyblish.api.InstancePlugin):
absoluteName=True)])
self.log.info("Gathering set relations..")
sets = self.gather_sets(instance)
for objset in sets:
self.log.debug("From %s.." % objset)
content = cmds.sets(objset, query=True)
objset_members = sets[objset]["members"]
@ -108,16 +106,14 @@ class CollectLook(pyblish.api.InstancePlugin):
verbose)
if not member_data:
continue
sets[objset]["members"].append(member_data)
# Remove sets that didn't have any members assigned in the end
sets = self.clean_sets(sets)
# Member attributes (shapes + transforms)
sets = self.remove_sets_without_members(sets)
self.log.info("Gathering attribute changes to instance members..")
attributes = self.collect_attributes_changes(instance)
attributes = self.collect_attributes_changed(instance)
looksets = cmds.ls(sets.keys(), absoluteName=True, long=True)
# Store data on the instance
@ -133,41 +129,75 @@ class CollectLook(pyblish.api.InstancePlugin):
self.log.info("Collected look for %s" % instance)
def gather_sets(self, instance):
"""Gather all objectSets which are of importance for publishing
It checks if all nodes in the instance are related to any objectSet
which need to be
Args:
instance (list): all nodes to be published
Returns:
dict
"""
# Get view sets (so we can ignore those sets later)
sets = dict()
view_sets = set()
model_panels = cmds.getPanel(type="modelPanel")
for panel in model_panels:
for panel in cmds.getPanel(type="modelPanel"):
view_set = cmds.modelEditor(panel, query=True, viewObjects=True)
if view_set:
view_sets.add(view_set)
for node in instance:
node_sets = self.filter_sets(node, view_sets)
if not node_sets:
related_sets = self.get_related_sets(node, view_sets)
if not related_sets:
continue
for objset in node_sets:
for objset in related_sets:
if objset in sets:
continue
unique_id = cmds.getAttr("%s.mbID" % objset)
sets[objset] = {"name": objset,
"uuid": id_utils.get_id(objset),
"uuid": unique_id,
"members": list()}
return sets
def filter_sets(self, node, view_sets):
def get_related_sets(self, node, view_sets):
"""Get the sets which do not belong to any specific group
node_sets = cmds.listSets(object=node, extendToShape=False) or []
if not node_sets:
return
Filters out based on:
- id attribute is NOT `pyblish.avalon.container`
- 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)
Args:
node (str): name of the current not to check
"""
ignored = ["pyblish.avalon.instance", "pyblish.avalon.container"]
related_sets = cmds.listSets(object=node, extendToShape=False)
if not related_sets:
return []
# Ignore `avalon.container`
sets = [s for s in related_sets if
not cmds.attributeQuery("id", node=s, exists=True) or
not cmds.getAttr("%s.id" % s) in ignored]
# Exclude deformer sets
# Autodesk documentation on listSets command:
# type(uint) : Returns all sets in the scene of the given
# >>> type:
# >>> 1 - all rendering sets
# >>> 2 - all deformer sets
deformer_sets = cmds.listSets(object=node,
extendToShape=False,
type=2) or []
deformer_sets = set(deformer_sets) # optimize lookup
sets = [s for s in node_sets if s not in deformer_sets]
sets = [s for s in sets if s not in deformer_sets]
# Ignore specifically named sets
sets = [s for s in sets if not any(s.endswith(x) for x in self.IGNORE)]
@ -176,16 +206,24 @@ class CollectLook(pyblish.api.InstancePlugin):
# viewports)
sets = [s for s in sets if s not in view_sets]
self.log.info("Found sets {0} for {1}".format(node_sets, node))
self.log.info("Found sets %s for %s" % (related_sets, node))
return sets
def clean_sets(self, sets):
def remove_sets_without_members(self, sets):
"""Remove any set which does not have any members
Args:
sets (dict): collection if sets with data as value
Returns:
dict
"""
for objset, data in sets.items():
if not data['members']:
self.log.debug("Removing redundant set "
"information: %s" % objset)
self.log.debug("Removing redundant set information: "
"%s" % objset)
sets.pop(objset)
return sets
@ -218,7 +256,8 @@ class CollectLook(pyblish.api.InstancePlugin):
if verbose:
self.log.debug("Such as %s.." % member)
member_data = {"name": node, "uuid": id_utils.get_id(node)}
member_data = {"name": node,
"uuid": cmds.getAttr("{}.mbID".format(node, ))}
# Include components information when components are assigned
if components:
@ -226,7 +265,22 @@ class CollectLook(pyblish.api.InstancePlugin):
return member_data
def collect_attributes_changes(self, instance):
def collect_attributes_changed(self, instance):
"""Collect all userDefined attributes which have changed
Each node gets checked for user defined attributes which have been
altered during development. Each changes gets logged in a dictionary
[{name: node,
uuid: uuid,
attributes: {attribute: value}}]
Args:
instance (list): all nodes which will be published
Returns:
list
"""
attributes = []
for node in instance:

View file

@ -37,7 +37,8 @@ class ExtractLook(colorbleed.api.Extractor):
# Remove all members of the sets so they are not included in the
# exported file by accident
self.log.info("Extract sets (Maya ASCII)..")
sets = instance.data["lookSets"]
lookdata = instance.data["lookData"]
sets = lookdata["sets"]
# Define the texture file node remapping
resource_remap = dict()
@ -71,8 +72,8 @@ class ExtractLook(colorbleed.api.Extractor):
# Write the JSON data
self.log.info("Extract json..")
data = {"attributes": instance.data["lookAttributes"],
"sets": instance.data["lookSetRelations"]}
data = {"attributes": lookdata["attributes"],
"sets": lookdata["relationships"]}
with open(json_path, "w") as f:
json.dump(data, f)

View file

@ -1,8 +1,8 @@
import maya.cmds as cmds
import pyblish.api
import colorbleed.api
import cbra.utils.maya.node_uuid as id_utils
class ValidateLookMembersNodeIds(pyblish.api.InstancePlugin):
"""Validate look members have colorbleed id attributes
@ -20,7 +20,8 @@ class ValidateLookMembersNodeIds(pyblish.api.InstancePlugin):
families = ['colorbleed.look']
hosts = ['maya']
label = 'Look Members Id Attributes'
actions = [colorbleed.api.SelectInvalidAction]
actions = [colorbleed.api.SelectInvalidAction,
colorbleed.api.GenerateUUIDsOnInvalidAction]
@staticmethod
def get_invalid(instance):
@ -40,7 +41,7 @@ class ValidateLookMembersNodeIds(pyblish.api.InstancePlugin):
# Ensure all nodes have a cbId
invalid = list()
for node in members:
if not id_utils.has_id(node):
if not cmds.getAttr("{}.mbID".format(node)):
invalid.append(node)
return invalid

View file

@ -5,7 +5,14 @@ from maya import cmds
import pyblish.api
import colorbleed.api
import cbra.utils.maya.node_uuid as id_utils
def get_unique_id(node):
attr = 'cbId'
unique_id = None
has_attribute = cmds.attributeQuery(attr, node=node, exists=True)
if has_attribute:
unique_id = cmds.getAttr("{}.{}".format(node, attr))
return unique_id
class ValidateLookMembersUnique(pyblish.api.InstancePlugin):
@ -25,15 +32,16 @@ class ValidateLookMembersUnique(pyblish.api.InstancePlugin):
families = ['colorbleed.look']
hosts = ['maya']
label = 'Look Members Unique'
actions = [colorbleed.api.SelectInvalidAction]
actions = [colorbleed.api.SelectInvalidAction,
colorbleed.api.GenerateUUIDsOnInvalidAction]
@staticmethod
def get_invalid(instance):
# Get all members from the sets
members = []
relations = instance.data["lookData"]["sets"]
for sg in relations:
relationships = instance.data["lookData"]["relationships"]
for sg in relationships:
sg_members = sg['members']
sg_members = [member['name'] for member in sg_members]
members.extend(sg_members)
@ -45,10 +53,9 @@ class ValidateLookMembersUnique(pyblish.api.InstancePlugin):
# Group members per id
id_nodes = defaultdict(set)
for node in members:
node_id = id_utils.get_id(node)
node_id = get_unique_id(node)
if not node_id:
continue
id_nodes[node_id].add(node)
invalid = list()
@ -61,8 +68,9 @@ class ValidateLookMembersUnique(pyblish.api.InstancePlugin):
def process(self, instance):
"""Process all meshes"""
invalid = self.get_invalid(instance)
print self.actions
invalid = self.get_invalid(instance)
if invalid:
raise RuntimeError("Members found without "
"asset IDs: {0}".format(invalid))

View file

@ -1,9 +1,10 @@
from collections import defaultdict
import maya.cmds as cmds
import pyblish.api
import colorbleed.api
import cbra.utils.maya.node_uuid as id_utils
class ValidateLookNodeUniqueIds(pyblish.api.InstancePlugin):
"""Validate look sets have unique colorbleed id attributes
@ -15,7 +16,7 @@ class ValidateLookNodeUniqueIds(pyblish.api.InstancePlugin):
hosts = ['maya']
label = 'Look Id Unique Attributes'
actions = [colorbleed.api.SelectInvalidAction,
colorbleed.api.GenerateUUIDsOnInvalidAction]
colorbleed.api.RepairAction]
@staticmethod
def get_invalid(instance):
@ -26,13 +27,15 @@ class ValidateLookNodeUniqueIds(pyblish.api.InstancePlugin):
id_sets = defaultdict(list)
invalid = list()
for node in nodes:
id = id_utils.get_id(node)
if not id:
unique_id = None
if cmds.attributeQuery("mbId", node=node, exists=True):
unique_id = cmds.getAttr("{}.mbId".format(node))
if not unique_id:
continue
id_sets[id].append(node)
id_sets[unique_id].append(node)
for id, nodes in id_sets.iteritems():
for unique_id, nodes in id_sets.iteritems():
if len(nodes) > 1:
invalid.extend(nodes)
@ -42,7 +45,6 @@ class ValidateLookNodeUniqueIds(pyblish.api.InstancePlugin):
"""Process all meshes"""
invalid = self.get_invalid(instance)
if invalid:
raise RuntimeError("Nodes found without "
"asset IDs: {0}".format(invalid))

View file

@ -0,0 +1,33 @@
import re
import pyblish.api
import colorbleed.api
class ValidateNamingConvention(pyblish.api.InstancePlugin):
label = ""
host = ["maya"]
actions = [colorbleed.api.SelectInvalidAction]
@staticmethod
def get_invalid(instance):
invalid = []
# todo: change pattern to company standard
pattern = re.compile("[a-zA-Z]+_[A-Z]{3}")
nodes = list(instance)
for node in nodes:
match = pattern.match(node)
if not match:
invalid.append(node)
return invalid
def process(self, instance):
invalid = self.get_invalid(instance)
if invalid:
self.log.error("Found invalid naming convention. Failed noted :\n"
"%s" % invalid)

View file

@ -1,3 +1,7 @@
from collections import defaultdict
import maya.cmds as cmds
import pyblish.api
import colorbleed.api
@ -16,12 +20,9 @@ class ValidateUniqueNodeIds(pyblish.api.InstancePlugin):
def get_invalid_dict(instance):
"""Return a dictionary mapping of id key to list of member nodes"""
import maya.cmds as cmds
uuid_attr = "cbId"
# Collect each id with their members
from collections import defaultdict
ids = defaultdict(list)
for member in instance:
has_attr = cmds.attributeQuery(uuid_attr, node=member, exists=True)
@ -60,4 +61,3 @@ class ValidateUniqueNodeIds(pyblish.api.InstancePlugin):
if invalid:
raise RuntimeError("Nodes found with non-unique "
"asset IDs: {0}".format(invalid))