mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 08:24:53 +01:00
[Automated] Merged develop into main
This commit is contained in:
commit
4b3de45c0d
31 changed files with 892 additions and 577 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
import os
|
import os
|
||||||
from avalon import api
|
from avalon import api
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
from openpype.lib import get_subset_name_with_asset_doc
|
||||||
|
|
||||||
|
|
||||||
class CollectWorkfile(pyblish.api.ContextPlugin):
|
class CollectWorkfile(pyblish.api.ContextPlugin):
|
||||||
|
|
@ -38,7 +39,14 @@ class CollectWorkfile(pyblish.api.ContextPlugin):
|
||||||
|
|
||||||
# workfile instance
|
# workfile instance
|
||||||
family = "workfile"
|
family = "workfile"
|
||||||
subset = family + task.capitalize()
|
subset = get_subset_name_with_asset_doc(
|
||||||
|
family,
|
||||||
|
"",
|
||||||
|
context.data["anatomyData"]["task"]["name"],
|
||||||
|
context.data["assetEntity"],
|
||||||
|
context.data["anatomyData"]["project"]["name"],
|
||||||
|
host_name=context.data["hostName"]
|
||||||
|
)
|
||||||
# Create instance
|
# Create instance
|
||||||
instance = context.create_instance(subset)
|
instance = context.create_instance(subset)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from openpype.lib import get_subset_name_with_asset_doc
|
||||||
|
|
||||||
|
|
||||||
class CollectWorkfile(pyblish.api.ContextPlugin):
|
class CollectWorkfile(pyblish.api.ContextPlugin):
|
||||||
"""Collect current script for publish."""
|
"""Collect current script for publish."""
|
||||||
|
|
@ -14,10 +16,15 @@ class CollectWorkfile(pyblish.api.ContextPlugin):
|
||||||
def process(self, context):
|
def process(self, context):
|
||||||
"""Plugin entry point."""
|
"""Plugin entry point."""
|
||||||
family = "workfile"
|
family = "workfile"
|
||||||
task = os.getenv("AVALON_TASK", None)
|
|
||||||
sanitized_task_name = task[0].upper() + task[1:]
|
|
||||||
basename = os.path.basename(context.data["currentFile"])
|
basename = os.path.basename(context.data["currentFile"])
|
||||||
subset = "{}{}".format(family, sanitized_task_name)
|
subset = get_subset_name_with_asset_doc(
|
||||||
|
family,
|
||||||
|
"",
|
||||||
|
context.data["anatomyData"]["task"]["name"],
|
||||||
|
context.data["assetEntity"],
|
||||||
|
context.data["anatomyData"]["project"]["name"],
|
||||||
|
host_name=context.data["hostName"]
|
||||||
|
)
|
||||||
|
|
||||||
# Create instance
|
# Create instance
|
||||||
instance = context.create_instance(subset)
|
instance = context.create_instance(subset)
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,6 @@ import maya.api.OpenMaya as om
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
import avalon.api
|
import avalon.api
|
||||||
|
|
||||||
from avalon.lib import find_submodule
|
|
||||||
|
|
||||||
import openpype.hosts.maya
|
import openpype.hosts.maya
|
||||||
from openpype.tools.utils import host_tools
|
from openpype.tools.utils import host_tools
|
||||||
from openpype.lib import (
|
from openpype.lib import (
|
||||||
|
|
@ -20,7 +18,6 @@ from openpype.lib import (
|
||||||
)
|
)
|
||||||
from openpype.lib.path_tools import HostDirmap
|
from openpype.lib.path_tools import HostDirmap
|
||||||
from openpype.pipeline import (
|
from openpype.pipeline import (
|
||||||
LegacyCreator,
|
|
||||||
register_loader_plugin_path,
|
register_loader_plugin_path,
|
||||||
register_inventory_action_path,
|
register_inventory_action_path,
|
||||||
register_creator_plugin_path,
|
register_creator_plugin_path,
|
||||||
|
|
@ -270,21 +267,8 @@ def ls():
|
||||||
|
|
||||||
"""
|
"""
|
||||||
container_names = _ls()
|
container_names = _ls()
|
||||||
|
|
||||||
has_metadata_collector = False
|
|
||||||
config_host = find_submodule(avalon.api.registered_config(), "maya")
|
|
||||||
if hasattr(config_host, "collect_container_metadata"):
|
|
||||||
has_metadata_collector = True
|
|
||||||
|
|
||||||
for container in sorted(container_names):
|
for container in sorted(container_names):
|
||||||
data = parse_container(container)
|
yield parse_container(container)
|
||||||
|
|
||||||
# Collect custom data if attribute is present
|
|
||||||
if has_metadata_collector:
|
|
||||||
metadata = config_host.collect_container_metadata(container)
|
|
||||||
data.update(metadata)
|
|
||||||
|
|
||||||
yield data
|
|
||||||
|
|
||||||
|
|
||||||
def containerise(name,
|
def containerise(name,
|
||||||
|
|
|
||||||
|
|
@ -252,6 +252,7 @@ class CreateRender(plugin.Creator):
|
||||||
"""Create instance settings."""
|
"""Create instance settings."""
|
||||||
# get pools
|
# get pools
|
||||||
pool_names = []
|
pool_names = []
|
||||||
|
default_priority = 50
|
||||||
|
|
||||||
self.server_aliases = list(self.deadline_servers.keys())
|
self.server_aliases = list(self.deadline_servers.keys())
|
||||||
self.data["deadlineServers"] = self.server_aliases
|
self.data["deadlineServers"] = self.server_aliases
|
||||||
|
|
@ -260,7 +261,8 @@ class CreateRender(plugin.Creator):
|
||||||
self.data["extendFrames"] = False
|
self.data["extendFrames"] = False
|
||||||
self.data["overrideExistingFrame"] = True
|
self.data["overrideExistingFrame"] = True
|
||||||
# self.data["useLegacyRenderLayers"] = True
|
# self.data["useLegacyRenderLayers"] = True
|
||||||
self.data["priority"] = 50
|
self.data["priority"] = default_priority
|
||||||
|
self.data["tile_priority"] = default_priority
|
||||||
self.data["framesPerTask"] = 1
|
self.data["framesPerTask"] = 1
|
||||||
self.data["whitelist"] = False
|
self.data["whitelist"] = False
|
||||||
self.data["machineList"] = ""
|
self.data["machineList"] = ""
|
||||||
|
|
@ -294,6 +296,16 @@ class CreateRender(plugin.Creator):
|
||||||
deadline_url = next(iter(self.deadline_servers.values()))
|
deadline_url = next(iter(self.deadline_servers.values()))
|
||||||
|
|
||||||
pool_names = self._get_deadline_pools(deadline_url)
|
pool_names = self._get_deadline_pools(deadline_url)
|
||||||
|
maya_submit_dl = self._project_settings.get(
|
||||||
|
"deadline", {}).get(
|
||||||
|
"publish", {}).get(
|
||||||
|
"MayaSubmitDeadline", {})
|
||||||
|
priority = maya_submit_dl.get("priority", default_priority)
|
||||||
|
self.data["priority"] = priority
|
||||||
|
|
||||||
|
tile_priority = maya_submit_dl.get("tile_priority",
|
||||||
|
default_priority)
|
||||||
|
self.data["tile_priority"] = tile_priority
|
||||||
|
|
||||||
if muster_enabled:
|
if muster_enabled:
|
||||||
self.log.info(">>> Loading Muster credentials ...")
|
self.log.info(">>> Loading Muster credentials ...")
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ from bson.objectid import ObjectId
|
||||||
from openpype.pipeline import (
|
from openpype.pipeline import (
|
||||||
InventoryAction,
|
InventoryAction,
|
||||||
get_representation_context,
|
get_representation_context,
|
||||||
get_representation_path_from_context,
|
|
||||||
)
|
)
|
||||||
from openpype.hosts.maya.api.lib import (
|
from openpype.hosts.maya.api.lib import (
|
||||||
maintained_selection,
|
maintained_selection,
|
||||||
|
|
@ -80,10 +79,10 @@ class ImportModelRender(InventoryAction):
|
||||||
})
|
})
|
||||||
|
|
||||||
context = get_representation_context(look_repr["_id"])
|
context = get_representation_context(look_repr["_id"])
|
||||||
maya_file = get_representation_path_from_context(context)
|
maya_file = self.filepath_from_context(context)
|
||||||
|
|
||||||
context = get_representation_context(json_repr["_id"])
|
context = get_representation_context(json_repr["_id"])
|
||||||
json_file = get_representation_path_from_context(context)
|
json_file = self.filepath_from_context(context)
|
||||||
|
|
||||||
# Import the look file
|
# Import the look file
|
||||||
with maintained_selection():
|
with maintained_selection():
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,14 @@ class ValidateCameraContents(pyblish.api.InstancePlugin):
|
||||||
# list when there are no actual cameras results in
|
# list when there are no actual cameras results in
|
||||||
# still an empty 'invalid' list
|
# still an empty 'invalid' list
|
||||||
if len(cameras) < 1:
|
if len(cameras) < 1:
|
||||||
raise RuntimeError("No cameras in instance.")
|
if members:
|
||||||
|
# If there are members in the instance return all of
|
||||||
|
# them as 'invalid' so the user can still select invalid
|
||||||
|
cls.log.error("No cameras found in instance "
|
||||||
|
"members: {}".format(members))
|
||||||
|
return members
|
||||||
|
|
||||||
|
raise RuntimeError("No cameras found in empty instance.")
|
||||||
|
|
||||||
# non-camera shapes
|
# non-camera shapes
|
||||||
valid_shapes = cmds.ls(shapes, type=('camera', 'locator'), long=True)
|
valid_shapes = cmds.ls(shapes, type=('camera', 'locator'), long=True)
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@ class ExtractReviewDataMov(openpype.api.Extractor):
|
||||||
if generated_repres:
|
if generated_repres:
|
||||||
# assign to representations
|
# assign to representations
|
||||||
instance.data["representations"] += generated_repres
|
instance.data["representations"] += generated_repres
|
||||||
instance.data["hasReviewableRepresentations"] = True
|
instance.data["useSequenceForReview"] = False
|
||||||
else:
|
else:
|
||||||
instance.data["families"].remove("review")
|
instance.data["families"].remove("review")
|
||||||
self.log.info((
|
self.log.info((
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import os
|
||||||
|
|
||||||
import qargparse
|
import qargparse
|
||||||
|
|
||||||
from openpype.pipeline import get_representation_path_from_context
|
|
||||||
from openpype.hosts.photoshop import api as photoshop
|
from openpype.hosts.photoshop import api as photoshop
|
||||||
from openpype.hosts.photoshop.api import get_unique_layer_name
|
from openpype.hosts.photoshop.api import get_unique_layer_name
|
||||||
|
|
||||||
|
|
@ -63,7 +62,7 @@ class ImageFromSequenceLoader(photoshop.PhotoshopLoader):
|
||||||
"""
|
"""
|
||||||
files = []
|
files = []
|
||||||
for context in repre_contexts:
|
for context in repre_contexts:
|
||||||
fname = get_representation_path_from_context(context)
|
fname = cls.filepath_from_context(context)
|
||||||
_, file_extension = os.path.splitext(fname)
|
_, file_extension = os.path.splitext(fname)
|
||||||
|
|
||||||
for file_name in os.listdir(os.path.dirname(fname)):
|
for file_name in os.listdir(os.path.dirname(fname)):
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import pyblish.api
|
||||||
|
|
||||||
|
|
||||||
|
class CollectSAAppName(pyblish.api.ContextPlugin):
|
||||||
|
"""Collect app name and label."""
|
||||||
|
|
||||||
|
label = "Collect App Name/Label"
|
||||||
|
order = pyblish.api.CollectorOrder - 0.5
|
||||||
|
hosts = ["standalonepublisher"]
|
||||||
|
|
||||||
|
def process(self, context):
|
||||||
|
context.data["appName"] = "standalone publisher"
|
||||||
|
context.data["appLabel"] = "Standalone publisher"
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import pyblish.api
|
||||||
|
|
||||||
|
|
||||||
|
class CollectTrayPublisherAppName(pyblish.api.ContextPlugin):
|
||||||
|
"""Collect app name and label."""
|
||||||
|
|
||||||
|
label = "Collect App Name/Label"
|
||||||
|
order = pyblish.api.CollectorOrder - 0.5
|
||||||
|
hosts = ["traypublisher"]
|
||||||
|
|
||||||
|
def process(self, context):
|
||||||
|
context.data["appName"] = "tray publisher"
|
||||||
|
context.data["appLabel"] = "Tray publisher"
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import collections
|
import collections
|
||||||
import qargparse
|
import qargparse
|
||||||
from avalon.pipeline import get_representation_context
|
from openpype.pipeline import get_representation_context
|
||||||
from openpype.hosts.tvpaint.api import lib, pipeline, plugin
|
from openpype.hosts.tvpaint.api import lib, pipeline, plugin
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@ IGNORED_DEFAULT_FILENAMES = (
|
||||||
"__init__.py",
|
"__init__.py",
|
||||||
"base.py",
|
"base.py",
|
||||||
"interfaces.py",
|
"interfaces.py",
|
||||||
|
"example_addons",
|
||||||
|
"default_modules",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -303,7 +305,16 @@ def _load_modules():
|
||||||
fullpath = os.path.join(current_dir, filename)
|
fullpath = os.path.join(current_dir, filename)
|
||||||
basename, ext = os.path.splitext(filename)
|
basename, ext = os.path.splitext(filename)
|
||||||
|
|
||||||
if not os.path.isdir(fullpath) and ext not in (".py", ):
|
if os.path.isdir(fullpath):
|
||||||
|
# Check existence of init fil
|
||||||
|
init_path = os.path.join(fullpath, "__init__.py")
|
||||||
|
if not os.path.exists(init_path):
|
||||||
|
log.debug((
|
||||||
|
"Module directory does not contan __init__.py file {}"
|
||||||
|
).format(fullpath))
|
||||||
|
continue
|
||||||
|
|
||||||
|
elif ext not in (".py", ):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -341,7 +352,16 @@ def _load_modules():
|
||||||
fullpath = os.path.join(dirpath, filename)
|
fullpath = os.path.join(dirpath, filename)
|
||||||
basename, ext = os.path.splitext(filename)
|
basename, ext = os.path.splitext(filename)
|
||||||
|
|
||||||
if not os.path.isdir(fullpath) and ext not in (".py", ):
|
if os.path.isdir(fullpath):
|
||||||
|
# Check existence of init fil
|
||||||
|
init_path = os.path.join(fullpath, "__init__.py")
|
||||||
|
if not os.path.exists(init_path):
|
||||||
|
log.debug((
|
||||||
|
"Module directory does not contan __init__.py file {}"
|
||||||
|
).format(fullpath))
|
||||||
|
continue
|
||||||
|
|
||||||
|
elif ext not in (".py", ):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# TODO add more logic how to define if folder is module or not
|
# TODO add more logic how to define if folder is module or not
|
||||||
|
|
|
||||||
|
|
@ -254,7 +254,11 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin):
|
||||||
use_published = True
|
use_published = True
|
||||||
tile_assembler_plugin = "OpenPypeTileAssembler"
|
tile_assembler_plugin = "OpenPypeTileAssembler"
|
||||||
asset_dependencies = False
|
asset_dependencies = False
|
||||||
|
priority = 50
|
||||||
|
tile_priority = 50
|
||||||
limit_groups = []
|
limit_groups = []
|
||||||
|
jobInfo = {}
|
||||||
|
pluginInfo = {}
|
||||||
group = "none"
|
group = "none"
|
||||||
|
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
|
|
@ -272,37 +276,12 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin):
|
||||||
self.deadline_url = instance.data.get("deadlineUrl")
|
self.deadline_url = instance.data.get("deadlineUrl")
|
||||||
assert self.deadline_url, "Requires Deadline Webservice URL"
|
assert self.deadline_url, "Requires Deadline Webservice URL"
|
||||||
|
|
||||||
self._job_info = (
|
# just using existing names from Setting
|
||||||
context.data["project_settings"].get(
|
self._job_info = self.jobInfo
|
||||||
"deadline", {}).get(
|
|
||||||
"publish", {}).get(
|
|
||||||
"MayaSubmitDeadline", {}).get(
|
|
||||||
"jobInfo", {})
|
|
||||||
)
|
|
||||||
|
|
||||||
self._plugin_info = (
|
self._plugin_info = self.pluginInfo
|
||||||
context.data["project_settings"].get(
|
|
||||||
"deadline", {}).get(
|
|
||||||
"publish", {}).get(
|
|
||||||
"MayaSubmitDeadline", {}).get(
|
|
||||||
"pluginInfo", {})
|
|
||||||
)
|
|
||||||
|
|
||||||
self.limit_groups = (
|
self.limit_groups = self.limit
|
||||||
context.data["project_settings"].get(
|
|
||||||
"deadline", {}).get(
|
|
||||||
"publish", {}).get(
|
|
||||||
"MayaSubmitDeadline", {}).get(
|
|
||||||
"limit", [])
|
|
||||||
)
|
|
||||||
|
|
||||||
self.group = (
|
|
||||||
context.data["project_settings"].get(
|
|
||||||
"deadline", {}).get(
|
|
||||||
"publish", {}).get(
|
|
||||||
"MayaSubmitDeadline", {}).get(
|
|
||||||
"group", "none")
|
|
||||||
)
|
|
||||||
|
|
||||||
context = instance.context
|
context = instance.context
|
||||||
workspace = context.data["workspaceDir"]
|
workspace = context.data["workspaceDir"]
|
||||||
|
|
@ -465,7 +444,7 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin):
|
||||||
self.payload_skeleton["JobInfo"]["UserName"] = deadline_user
|
self.payload_skeleton["JobInfo"]["UserName"] = deadline_user
|
||||||
# Set job priority
|
# Set job priority
|
||||||
self.payload_skeleton["JobInfo"]["Priority"] = \
|
self.payload_skeleton["JobInfo"]["Priority"] = \
|
||||||
self._instance.data.get("priority", 50)
|
self._instance.data.get("priority", self.priority)
|
||||||
|
|
||||||
if self.group != "none" and self.group:
|
if self.group != "none" and self.group:
|
||||||
self.payload_skeleton["JobInfo"]["Group"] = self.group
|
self.payload_skeleton["JobInfo"]["Group"] = self.group
|
||||||
|
|
@ -635,7 +614,7 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin):
|
||||||
}
|
}
|
||||||
assembly_payload["JobInfo"].update(output_filenames)
|
assembly_payload["JobInfo"].update(output_filenames)
|
||||||
assembly_payload["JobInfo"]["Priority"] = self._instance.data.get(
|
assembly_payload["JobInfo"]["Priority"] = self._instance.data.get(
|
||||||
"priority", 50)
|
"tile_priority", self.tile_priority)
|
||||||
assembly_payload["JobInfo"]["UserName"] = deadline_user
|
assembly_payload["JobInfo"]["UserName"] = deadline_user
|
||||||
|
|
||||||
frame_payloads = []
|
frame_payloads = []
|
||||||
|
|
|
||||||
|
|
@ -235,6 +235,8 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin):
|
||||||
if mongo_url:
|
if mongo_url:
|
||||||
environment["OPENPYPE_MONGO"] = mongo_url
|
environment["OPENPYPE_MONGO"] = mongo_url
|
||||||
|
|
||||||
|
priority = self.deadline_priority or instance.data.get("priority", 50)
|
||||||
|
|
||||||
args = [
|
args = [
|
||||||
"--headless",
|
"--headless",
|
||||||
'publish',
|
'publish',
|
||||||
|
|
@ -254,7 +256,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin):
|
||||||
|
|
||||||
"Department": self.deadline_department,
|
"Department": self.deadline_department,
|
||||||
"ChunkSize": self.deadline_chunk_size,
|
"ChunkSize": self.deadline_chunk_size,
|
||||||
"Priority": job["Props"]["Pri"],
|
"Priority": priority,
|
||||||
|
|
||||||
"Group": self.deadline_group,
|
"Group": self.deadline_group,
|
||||||
"Pool": self.deadline_pool,
|
"Pool": self.deadline_pool,
|
||||||
|
|
@ -524,26 +526,28 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin):
|
||||||
for collection in collections:
|
for collection in collections:
|
||||||
ext = collection.tail.lstrip(".")
|
ext = collection.tail.lstrip(".")
|
||||||
preview = False
|
preview = False
|
||||||
# if filtered aov name is found in filename, toggle it for
|
# TODO 'useSequenceForReview' is temporary solution which does
|
||||||
# preview video rendering
|
# not work for 100% of cases. We must be able to tell what
|
||||||
for app in self.aov_filter.keys():
|
# expected files contains more explicitly and from what
|
||||||
if os.environ.get("AVALON_APP", "") == app:
|
# should be review made.
|
||||||
# no need to add review if `hasReviewableRepresentations`
|
# - "review" tag is never added when is set to 'False'
|
||||||
if instance.get("hasReviewableRepresentations"):
|
if instance["useSequenceForReview"]:
|
||||||
break
|
# if filtered aov name is found in filename, toggle it for
|
||||||
|
# preview video rendering
|
||||||
|
for app in self.aov_filter.keys():
|
||||||
|
if os.environ.get("AVALON_APP", "") == app:
|
||||||
|
# iteratre all aov filters
|
||||||
|
for aov in self.aov_filter[app]:
|
||||||
|
if re.match(
|
||||||
|
aov,
|
||||||
|
list(collection)[0]
|
||||||
|
):
|
||||||
|
preview = True
|
||||||
|
break
|
||||||
|
|
||||||
# iteratre all aov filters
|
# toggle preview on if multipart is on
|
||||||
for aov in self.aov_filter[app]:
|
if instance.get("multipartExr", False):
|
||||||
if re.match(
|
preview = True
|
||||||
aov,
|
|
||||||
list(collection)[0]
|
|
||||||
):
|
|
||||||
preview = True
|
|
||||||
break
|
|
||||||
|
|
||||||
# toggle preview on if multipart is on
|
|
||||||
if instance.get("multipartExr", False):
|
|
||||||
preview = True
|
|
||||||
|
|
||||||
staging = os.path.dirname(list(collection)[0])
|
staging = os.path.dirname(list(collection)[0])
|
||||||
success, rootless_staging_dir = (
|
success, rootless_staging_dir = (
|
||||||
|
|
@ -730,8 +734,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin):
|
||||||
"resolutionHeight": data.get("resolutionHeight", 1080),
|
"resolutionHeight": data.get("resolutionHeight", 1080),
|
||||||
"multipartExr": data.get("multipartExr", False),
|
"multipartExr": data.get("multipartExr", False),
|
||||||
"jobBatchName": data.get("jobBatchName", ""),
|
"jobBatchName": data.get("jobBatchName", ""),
|
||||||
"hasReviewableRepresentations": data.get(
|
"useSequenceForReview": data.get("useSequenceForReview", True)
|
||||||
"hasReviewableRepresentations")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if "prerender" in instance.data["families"]:
|
if "prerender" in instance.data["families"]:
|
||||||
|
|
@ -923,12 +926,6 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin):
|
||||||
# User is deadline user
|
# User is deadline user
|
||||||
render_job["Props"]["User"] = context.data.get(
|
render_job["Props"]["User"] = context.data.get(
|
||||||
"deadlineUser", getpass.getuser())
|
"deadlineUser", getpass.getuser())
|
||||||
# Priority is now not handled at all
|
|
||||||
|
|
||||||
if self.deadline_priority:
|
|
||||||
render_job["Props"]["Pri"] = self.deadline_priority
|
|
||||||
else:
|
|
||||||
render_job["Props"]["Pri"] = instance.data.get("priority")
|
|
||||||
|
|
||||||
render_job["Props"]["Env"] = {
|
render_job["Props"]["Env"] = {
|
||||||
"FTRACK_API_USER": os.environ.get("FTRACK_API_USER"),
|
"FTRACK_API_USER": os.environ.get("FTRACK_API_USER"),
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,6 @@
|
||||||
import os
|
import os
|
||||||
from openpype_modules.ftrack.lib import BaseAction, statics_icon
|
from openpype_modules.ftrack.lib import BaseAction, statics_icon
|
||||||
from avalon import lib as avalonlib
|
from openpype.api import Anatomy
|
||||||
from openpype.api import (
|
|
||||||
Anatomy,
|
|
||||||
get_project_settings
|
|
||||||
)
|
|
||||||
from openpype.lib import ApplicationManager
|
|
||||||
|
|
||||||
|
|
||||||
class CreateFolders(BaseAction):
|
class CreateFolders(BaseAction):
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,15 @@
|
||||||
|
"""Integrate components into ftrack
|
||||||
|
|
||||||
|
Requires:
|
||||||
|
context -> ftrackSession - connected ftrack.Session
|
||||||
|
instance -> ftrackComponentsList - list of components to integrate
|
||||||
|
|
||||||
|
Provides:
|
||||||
|
instance -> ftrackIntegratedAssetVersionsData
|
||||||
|
# legacy
|
||||||
|
instance -> ftrackIntegratedAssetVersions
|
||||||
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import six
|
import six
|
||||||
|
|
@ -54,6 +66,114 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin):
|
||||||
self.log.debug(query)
|
self.log.debug(query)
|
||||||
return query
|
return query
|
||||||
|
|
||||||
|
def process(self, instance):
|
||||||
|
session = instance.context.data["ftrackSession"]
|
||||||
|
context = instance.context
|
||||||
|
component_list = instance.data.get("ftrackComponentsList")
|
||||||
|
if not component_list:
|
||||||
|
self.log.info(
|
||||||
|
"Instance don't have components to integrate to Ftrack."
|
||||||
|
" Skipping."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
session = instance.context.data["ftrackSession"]
|
||||||
|
context = instance.context
|
||||||
|
|
||||||
|
parent_entity = None
|
||||||
|
default_asset_name = None
|
||||||
|
# If instance has set "ftrackEntity" or "ftrackTask" then use them from
|
||||||
|
# instance. Even if they are set to None. If they are set to None it
|
||||||
|
# has a reason. (like has different context)
|
||||||
|
if "ftrackEntity" in instance.data or "ftrackTask" in instance.data:
|
||||||
|
task_entity = instance.data.get("ftrackTask")
|
||||||
|
parent_entity = instance.data.get("ftrackEntity")
|
||||||
|
|
||||||
|
elif "ftrackEntity" in context.data or "ftrackTask" in context.data:
|
||||||
|
task_entity = context.data.get("ftrackTask")
|
||||||
|
parent_entity = context.data.get("ftrackEntity")
|
||||||
|
|
||||||
|
if task_entity:
|
||||||
|
default_asset_name = task_entity["name"]
|
||||||
|
parent_entity = task_entity["parent"]
|
||||||
|
|
||||||
|
if parent_entity is None:
|
||||||
|
self.log.info((
|
||||||
|
"Skipping ftrack integration. Instance \"{}\" does not"
|
||||||
|
" have specified ftrack entities."
|
||||||
|
).format(str(instance)))
|
||||||
|
return
|
||||||
|
|
||||||
|
if not default_asset_name:
|
||||||
|
default_asset_name = parent_entity["name"]
|
||||||
|
|
||||||
|
# Change status on task
|
||||||
|
self._set_task_status(instance, task_entity, session)
|
||||||
|
|
||||||
|
# Prepare AssetTypes
|
||||||
|
asset_types_by_short = self._ensure_asset_types_exists(
|
||||||
|
session, component_list
|
||||||
|
)
|
||||||
|
|
||||||
|
asset_versions_data_by_id = {}
|
||||||
|
used_asset_versions = []
|
||||||
|
# Iterate over components and publish
|
||||||
|
for data in component_list:
|
||||||
|
self.log.debug("data: {}".format(data))
|
||||||
|
|
||||||
|
# AssetType
|
||||||
|
asset_type_short = data["assettype_data"]["short"]
|
||||||
|
asset_type_entity = asset_types_by_short[asset_type_short]
|
||||||
|
|
||||||
|
# Asset
|
||||||
|
asset_data = data.get("asset_data") or {}
|
||||||
|
if "name" not in asset_data:
|
||||||
|
asset_data["name"] = default_asset_name
|
||||||
|
asset_entity = self._ensure_asset_exists(
|
||||||
|
session,
|
||||||
|
asset_data,
|
||||||
|
asset_type_entity["id"],
|
||||||
|
parent_entity["id"]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Asset Version
|
||||||
|
asset_version_data = data.get("assetversion_data") or {}
|
||||||
|
asset_version_entity = self._ensure_asset_version_exists(
|
||||||
|
session, asset_version_data, asset_entity["id"], task_entity
|
||||||
|
)
|
||||||
|
|
||||||
|
# Component
|
||||||
|
self.create_component(session, asset_version_entity, data)
|
||||||
|
|
||||||
|
# Store asset version and components items that were
|
||||||
|
version_id = asset_version_entity["id"]
|
||||||
|
if version_id not in asset_versions_data_by_id:
|
||||||
|
asset_versions_data_by_id[version_id] = {
|
||||||
|
"asset_version": asset_version_entity,
|
||||||
|
"component_items": []
|
||||||
|
}
|
||||||
|
|
||||||
|
asset_versions_data_by_id[version_id]["component_items"].append(
|
||||||
|
data
|
||||||
|
)
|
||||||
|
|
||||||
|
# Backwards compatibility
|
||||||
|
if asset_version_entity not in used_asset_versions:
|
||||||
|
used_asset_versions.append(asset_version_entity)
|
||||||
|
|
||||||
|
instance.data["ftrackIntegratedAssetVersionsData"] = (
|
||||||
|
asset_versions_data_by_id
|
||||||
|
)
|
||||||
|
|
||||||
|
# Backwards compatibility
|
||||||
|
asset_versions_key = "ftrackIntegratedAssetVersions"
|
||||||
|
if asset_versions_key not in instance.data:
|
||||||
|
instance.data[asset_versions_key] = []
|
||||||
|
|
||||||
|
for asset_version in used_asset_versions:
|
||||||
|
if asset_version not in instance.data[asset_versions_key]:
|
||||||
|
instance.data[asset_versions_key].append(asset_version)
|
||||||
|
|
||||||
def _set_task_status(self, instance, task_entity, session):
|
def _set_task_status(self, instance, task_entity, session):
|
||||||
project_entity = instance.context.data.get("ftrackProject")
|
project_entity = instance.context.data.get("ftrackProject")
|
||||||
if not project_entity:
|
if not project_entity:
|
||||||
|
|
@ -100,190 +220,222 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin):
|
||||||
session._configure_locations()
|
session._configure_locations()
|
||||||
six.reraise(tp, value, tb)
|
six.reraise(tp, value, tb)
|
||||||
|
|
||||||
def process(self, instance):
|
def _ensure_asset_types_exists(self, session, component_list):
|
||||||
session = instance.context.data["ftrackSession"]
|
"""Make sure that all AssetType entities exists for integration.
|
||||||
context = instance.context
|
|
||||||
|
|
||||||
name = None
|
Returns:
|
||||||
# If instance has set "ftrackEntity" or "ftrackTask" then use them from
|
dict: All asset types by short name.
|
||||||
# instance. Even if they are set to None. If they are set to None it
|
"""
|
||||||
# has a reason. (like has different context)
|
# Query existing asset types
|
||||||
if "ftrackEntity" in instance.data or "ftrackTask" in instance.data:
|
asset_types = session.query("select id, short from AssetType").all()
|
||||||
task = instance.data.get("ftrackTask")
|
# Stpore all existing short names
|
||||||
parent = instance.data.get("ftrackEntity")
|
asset_type_shorts = {asset_type["short"] for asset_type in asset_types}
|
||||||
|
# Check which asset types are missing and store them
|
||||||
|
asset_type_names_by_missing_shorts = {}
|
||||||
|
default_short_name = "upload"
|
||||||
|
for data in component_list:
|
||||||
|
asset_type_data = data.get("assettype_data") or {}
|
||||||
|
asset_type_short = asset_type_data.get("short")
|
||||||
|
if not asset_type_short:
|
||||||
|
# Use default asset type name if not set and change the
|
||||||
|
# input data
|
||||||
|
asset_type_short = default_short_name
|
||||||
|
asset_type_data["short"] = asset_type_short
|
||||||
|
data["assettype_data"] = asset_type_data
|
||||||
|
|
||||||
elif "ftrackEntity" in context.data or "ftrackTask" in context.data:
|
if (
|
||||||
task = context.data.get("ftrackTask")
|
# Skip if short name exists
|
||||||
parent = context.data.get("ftrackEntity")
|
asset_type_short in asset_type_shorts
|
||||||
|
# Skip if short name was already added to missing types
|
||||||
|
# and asset type name is filled
|
||||||
|
# - if asset type name is missing then try use name from other
|
||||||
|
# data
|
||||||
|
or asset_type_names_by_missing_shorts.get(asset_type_short)
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
|
||||||
if task:
|
asset_type_names_by_missing_shorts[asset_type_short] = (
|
||||||
parent = task["parent"]
|
asset_type_data.get("name")
|
||||||
name = task
|
)
|
||||||
elif parent:
|
|
||||||
name = parent["name"]
|
|
||||||
|
|
||||||
if not name:
|
# Create missing asset types if there are any
|
||||||
self.log.info((
|
if asset_type_names_by_missing_shorts:
|
||||||
"Skipping ftrack integration. Instance \"{}\" does not"
|
self.log.info("Creating asset types with short names: {}".format(
|
||||||
" have specified ftrack entities."
|
", ".join(asset_type_names_by_missing_shorts.keys())
|
||||||
).format(str(instance)))
|
))
|
||||||
return
|
for missing_short, type_name in asset_type_names_by_missing_shorts:
|
||||||
|
# Use short for name if name is not defined
|
||||||
|
if not type_name:
|
||||||
|
type_name = missing_short
|
||||||
|
# Use short name also for name
|
||||||
|
# - there is not other source for 'name'
|
||||||
|
session.create(
|
||||||
|
"AssetType",
|
||||||
|
{
|
||||||
|
"short": missing_short,
|
||||||
|
"name": type_name
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
info_msg = (
|
# Commit creation
|
||||||
"Created new {entity_type} with data: {data}"
|
session.commit()
|
||||||
", metadata: {metadata}."
|
# Requery asset types
|
||||||
|
asset_types = session.query(
|
||||||
|
"select id, short from AssetType"
|
||||||
|
).all()
|
||||||
|
|
||||||
|
return {asset_type["short"]: asset_type for asset_type in asset_types}
|
||||||
|
|
||||||
|
def _ensure_asset_exists(
|
||||||
|
self, session, asset_data, asset_type_id, parent_id
|
||||||
|
):
|
||||||
|
asset_name = asset_data["name"]
|
||||||
|
asset_entity = self._query_asset(
|
||||||
|
session, asset_name, asset_type_id, parent_id
|
||||||
|
)
|
||||||
|
if asset_entity is not None:
|
||||||
|
return asset_entity
|
||||||
|
|
||||||
|
asset_data = {
|
||||||
|
"name": asset_name,
|
||||||
|
"type_id": asset_type_id,
|
||||||
|
"context_id": parent_id
|
||||||
|
}
|
||||||
|
self.log.info("Created new Asset with data: {}.".format(asset_data))
|
||||||
|
session.create("Asset", asset_data)
|
||||||
|
session.commit()
|
||||||
|
return self._query_asset(session, asset_name, asset_type_id, parent_id)
|
||||||
|
|
||||||
|
def _query_asset(self, session, asset_name, asset_type_id, parent_id):
|
||||||
|
return session.query(
|
||||||
|
(
|
||||||
|
"select id from Asset"
|
||||||
|
" where name is \"{}\""
|
||||||
|
" and type_id is \"{}\""
|
||||||
|
" and context_id is \"{}\""
|
||||||
|
).format(asset_name, asset_type_id, parent_id)
|
||||||
|
).first()
|
||||||
|
|
||||||
|
def _ensure_asset_version_exists(
|
||||||
|
self, session, asset_version_data, asset_id, task_entity
|
||||||
|
):
|
||||||
|
task_id = None
|
||||||
|
if task_entity:
|
||||||
|
task_id = task_entity["id"]
|
||||||
|
|
||||||
|
# Try query asset version by criteria (asset id and version)
|
||||||
|
version = asset_version_data.get("version") or 0
|
||||||
|
asset_version_entity = self._query_asset_version(
|
||||||
|
session, version, asset_id
|
||||||
)
|
)
|
||||||
|
|
||||||
used_asset_versions = []
|
# Prepare comment value
|
||||||
|
comment = asset_version_data.get("comment") or ""
|
||||||
|
if asset_version_entity is not None:
|
||||||
|
changed = False
|
||||||
|
if comment != asset_version_entity["comment"]:
|
||||||
|
asset_version_entity["comment"] = comment
|
||||||
|
changed = True
|
||||||
|
|
||||||
self._set_task_status(instance, task, session)
|
if task_id != asset_version_entity["task_id"]:
|
||||||
|
asset_version_entity["task_id"] = task_id
|
||||||
|
changed = True
|
||||||
|
|
||||||
# Iterate over components and publish
|
if changed:
|
||||||
for data in instance.data.get("ftrackComponentsList", []):
|
session.commit()
|
||||||
# AssetType
|
|
||||||
# Get existing entity.
|
|
||||||
assettype_data = {"short": "upload"}
|
|
||||||
assettype_data.update(data.get("assettype_data", {}))
|
|
||||||
self.log.debug("data: {}".format(data))
|
|
||||||
|
|
||||||
assettype_entity = session.query(
|
else:
|
||||||
self.query("AssetType", assettype_data)
|
new_asset_version_data = {
|
||||||
).first()
|
"version": version,
|
||||||
|
"asset_id": asset_id
|
||||||
# Create a new entity if none exits.
|
|
||||||
if not assettype_entity:
|
|
||||||
assettype_entity = session.create("AssetType", assettype_data)
|
|
||||||
self.log.debug("Created new AssetType with data: {}".format(
|
|
||||||
assettype_data
|
|
||||||
))
|
|
||||||
|
|
||||||
# Asset
|
|
||||||
# Get existing entity.
|
|
||||||
asset_data = {
|
|
||||||
"name": name,
|
|
||||||
"type": assettype_entity,
|
|
||||||
"parent": parent,
|
|
||||||
}
|
}
|
||||||
asset_data.update(data.get("asset_data", {}))
|
if task_id:
|
||||||
|
new_asset_version_data["task_id"] = task_id
|
||||||
|
|
||||||
asset_entity = session.query(
|
if comment:
|
||||||
self.query("Asset", asset_data)
|
new_asset_version_data["comment"] = comment
|
||||||
).first()
|
|
||||||
|
|
||||||
self.log.info("asset entity: {}".format(asset_entity))
|
self.log.info("Created new AssetVersion with data {}".format(
|
||||||
|
new_asset_version_data
|
||||||
# Extracting metadata, and adding after entity creation. This is
|
))
|
||||||
# due to a ftrack_api bug where you can't add metadata on creation.
|
session.create("AssetVersion", new_asset_version_data)
|
||||||
asset_metadata = asset_data.pop("metadata", {})
|
session.commit()
|
||||||
|
asset_version_entity = self._query_asset_version(
|
||||||
# Create a new entity if none exits.
|
session, version, asset_id
|
||||||
if not asset_entity:
|
|
||||||
asset_entity = session.create("Asset", asset_data)
|
|
||||||
self.log.debug(
|
|
||||||
info_msg.format(
|
|
||||||
entity_type="Asset",
|
|
||||||
data=asset_data,
|
|
||||||
metadata=asset_metadata
|
|
||||||
)
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
session.commit()
|
|
||||||
except Exception:
|
|
||||||
tp, value, tb = sys.exc_info()
|
|
||||||
session.rollback()
|
|
||||||
session._configure_locations()
|
|
||||||
six.reraise(tp, value, tb)
|
|
||||||
|
|
||||||
# Adding metadata
|
|
||||||
existing_asset_metadata = asset_entity["metadata"]
|
|
||||||
existing_asset_metadata.update(asset_metadata)
|
|
||||||
asset_entity["metadata"] = existing_asset_metadata
|
|
||||||
|
|
||||||
# AssetVersion
|
|
||||||
# Get existing entity.
|
|
||||||
assetversion_data = {
|
|
||||||
"version": 0,
|
|
||||||
"asset": asset_entity,
|
|
||||||
}
|
|
||||||
_assetversion_data = data.get("assetversion_data", {})
|
|
||||||
assetversion_cust_attrs = _assetversion_data.pop(
|
|
||||||
"custom_attributes", {}
|
|
||||||
)
|
)
|
||||||
asset_version_comment = _assetversion_data.pop(
|
|
||||||
"comment", None
|
|
||||||
)
|
|
||||||
assetversion_data.update(_assetversion_data)
|
|
||||||
|
|
||||||
assetversion_entity = session.query(
|
# Set custom attributes if there were any set
|
||||||
self.query("AssetVersion", assetversion_data)
|
custom_attrs = asset_version_data.get("custom_attributes") or {}
|
||||||
).first()
|
for attr_key, attr_value in custom_attrs.items():
|
||||||
|
if attr_key in asset_version_entity["custom_attributes"]:
|
||||||
# Extracting metadata, and adding after entity creation. This is
|
try:
|
||||||
# due to a ftrack_api bug where you can't add metadata on creation.
|
asset_version_entity["custom_attributes"][attr_key] = (
|
||||||
assetversion_metadata = assetversion_data.pop("metadata", {})
|
attr_value
|
||||||
|
|
||||||
if task:
|
|
||||||
assetversion_data['task'] = task
|
|
||||||
|
|
||||||
# Create a new entity if none exits.
|
|
||||||
if not assetversion_entity:
|
|
||||||
assetversion_entity = session.create(
|
|
||||||
"AssetVersion", assetversion_data
|
|
||||||
)
|
|
||||||
self.log.debug(
|
|
||||||
info_msg.format(
|
|
||||||
entity_type="AssetVersion",
|
|
||||||
data=assetversion_data,
|
|
||||||
metadata=assetversion_metadata
|
|
||||||
)
|
)
|
||||||
|
session.commit()
|
||||||
|
continue
|
||||||
|
except Exception:
|
||||||
|
session.rollback()
|
||||||
|
session._configure_locations()
|
||||||
|
|
||||||
|
self.log.warning(
|
||||||
|
(
|
||||||
|
"Custom Attrubute \"{0}\" is not available for"
|
||||||
|
" AssetVersion <{1}>. Can't set it's value to: \"{2}\""
|
||||||
|
).format(
|
||||||
|
attr_key, asset_version_entity["id"], str(attr_value)
|
||||||
)
|
)
|
||||||
try:
|
)
|
||||||
session.commit()
|
|
||||||
except Exception:
|
|
||||||
tp, value, tb = sys.exc_info()
|
|
||||||
session.rollback()
|
|
||||||
session._configure_locations()
|
|
||||||
six.reraise(tp, value, tb)
|
|
||||||
|
|
||||||
# Adding metadata
|
return asset_version_entity
|
||||||
existing_assetversion_metadata = assetversion_entity["metadata"]
|
|
||||||
existing_assetversion_metadata.update(assetversion_metadata)
|
|
||||||
assetversion_entity["metadata"] = existing_assetversion_metadata
|
|
||||||
|
|
||||||
# Add comment
|
def _query_asset_version(self, session, version, asset_id):
|
||||||
if asset_version_comment:
|
return session.query(
|
||||||
assetversion_entity["comment"] = asset_version_comment
|
(
|
||||||
try:
|
"select id, task_id, comment from AssetVersion"
|
||||||
session.commit()
|
" where version is \"{}\" and asset_id is \"{}\""
|
||||||
except Exception:
|
).format(version, asset_id)
|
||||||
session.rollback()
|
).first()
|
||||||
session._configure_locations()
|
|
||||||
self.log.warning((
|
|
||||||
"Comment was not possible to set for AssetVersion"
|
|
||||||
"\"{0}\". Can't set it's value to: \"{1}\""
|
|
||||||
).format(
|
|
||||||
assetversion_entity["id"], str(asset_version_comment)
|
|
||||||
))
|
|
||||||
|
|
||||||
# Adding Custom Attributes
|
def create_component(self, session, asset_version_entity, data):
|
||||||
for attr, val in assetversion_cust_attrs.items():
|
component_data = data.get("component_data") or {}
|
||||||
if attr in assetversion_entity["custom_attributes"]:
|
|
||||||
try:
|
|
||||||
assetversion_entity["custom_attributes"][attr] = val
|
|
||||||
session.commit()
|
|
||||||
continue
|
|
||||||
except Exception:
|
|
||||||
session.rollback()
|
|
||||||
session._configure_locations()
|
|
||||||
|
|
||||||
self.log.warning((
|
if not component_data.get("name"):
|
||||||
"Custom Attrubute \"{0}\""
|
component_data["name"] = "main"
|
||||||
" is not available for AssetVersion <{1}>."
|
|
||||||
" Can't set it's value to: \"{2}\""
|
version_id = asset_version_entity["id"]
|
||||||
).format(attr, assetversion_entity["id"], str(val)))
|
component_data["version_id"] = version_id
|
||||||
|
component_entity = session.query(
|
||||||
|
(
|
||||||
|
"select id, name from Component where name is \"{}\""
|
||||||
|
" and version_id is \"{}\""
|
||||||
|
).format(component_data["name"], version_id)
|
||||||
|
).first()
|
||||||
|
|
||||||
|
component_overwrite = data.get("component_overwrite", False)
|
||||||
|
location = data.get("component_location", session.pick_location())
|
||||||
|
|
||||||
|
# Overwrite existing component data if requested.
|
||||||
|
if component_entity and component_overwrite:
|
||||||
|
origin_location = session.query(
|
||||||
|
"Location where name is \"ftrack.origin\""
|
||||||
|
).one()
|
||||||
|
|
||||||
|
# Removing existing members from location
|
||||||
|
components = list(component_entity.get("members", []))
|
||||||
|
components += [component_entity]
|
||||||
|
for component in components:
|
||||||
|
for loc in component["component_locations"]:
|
||||||
|
if location["id"] == loc["location_id"]:
|
||||||
|
location.remove_component(
|
||||||
|
component, recursive=False
|
||||||
|
)
|
||||||
|
|
||||||
|
# Deleting existing members on component entity
|
||||||
|
for member in component_entity.get("members", []):
|
||||||
|
session.delete(member)
|
||||||
|
del(member)
|
||||||
|
|
||||||
# Have to commit the version and asset, because location can't
|
|
||||||
# determine the final location without.
|
|
||||||
try:
|
try:
|
||||||
session.commit()
|
session.commit()
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
@ -292,175 +444,124 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin):
|
||||||
session._configure_locations()
|
session._configure_locations()
|
||||||
six.reraise(tp, value, tb)
|
six.reraise(tp, value, tb)
|
||||||
|
|
||||||
# Component
|
# Reset members in memory
|
||||||
# Get existing entity.
|
if "members" in component_entity.keys():
|
||||||
component_data = {
|
component_entity["members"] = []
|
||||||
"name": "main",
|
|
||||||
"version": assetversion_entity
|
|
||||||
}
|
|
||||||
component_data.update(data.get("component_data", {}))
|
|
||||||
|
|
||||||
component_entity = session.query(
|
# Add components to origin location
|
||||||
self.query("Component", component_data)
|
try:
|
||||||
).first()
|
collection = clique.parse(data["component_path"])
|
||||||
|
except ValueError:
|
||||||
|
# Assume its a single file
|
||||||
|
# Changing file type
|
||||||
|
name, ext = os.path.splitext(data["component_path"])
|
||||||
|
component_entity["file_type"] = ext
|
||||||
|
|
||||||
component_overwrite = data.get("component_overwrite", False)
|
origin_location.add_component(
|
||||||
location = data.get("component_location", session.pick_location())
|
component_entity, data["component_path"]
|
||||||
|
|
||||||
# Overwrite existing component data if requested.
|
|
||||||
if component_entity and component_overwrite:
|
|
||||||
|
|
||||||
origin_location = session.query(
|
|
||||||
"Location where name is \"ftrack.origin\""
|
|
||||||
).one()
|
|
||||||
|
|
||||||
# Removing existing members from location
|
|
||||||
components = list(component_entity.get("members", []))
|
|
||||||
components += [component_entity]
|
|
||||||
for component in components:
|
|
||||||
for loc in component["component_locations"]:
|
|
||||||
if location["id"] == loc["location_id"]:
|
|
||||||
location.remove_component(
|
|
||||||
component, recursive=False
|
|
||||||
)
|
|
||||||
|
|
||||||
# Deleting existing members on component entity
|
|
||||||
for member in component_entity.get("members", []):
|
|
||||||
session.delete(member)
|
|
||||||
del(member)
|
|
||||||
|
|
||||||
try:
|
|
||||||
session.commit()
|
|
||||||
except Exception:
|
|
||||||
tp, value, tb = sys.exc_info()
|
|
||||||
session.rollback()
|
|
||||||
session._configure_locations()
|
|
||||||
six.reraise(tp, value, tb)
|
|
||||||
|
|
||||||
# Reset members in memory
|
|
||||||
if "members" in component_entity.keys():
|
|
||||||
component_entity["members"] = []
|
|
||||||
|
|
||||||
# Add components to origin location
|
|
||||||
try:
|
|
||||||
collection = clique.parse(data["component_path"])
|
|
||||||
except ValueError:
|
|
||||||
# Assume its a single file
|
|
||||||
# Changing file type
|
|
||||||
name, ext = os.path.splitext(data["component_path"])
|
|
||||||
component_entity["file_type"] = ext
|
|
||||||
|
|
||||||
origin_location.add_component(
|
|
||||||
component_entity, data["component_path"]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# Changing file type
|
|
||||||
component_entity["file_type"] = collection.format("{tail}")
|
|
||||||
|
|
||||||
# Create member components for sequence.
|
|
||||||
for member_path in collection:
|
|
||||||
|
|
||||||
size = 0
|
|
||||||
try:
|
|
||||||
size = os.path.getsize(member_path)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
name = collection.match(member_path).group("index")
|
|
||||||
|
|
||||||
member_data = {
|
|
||||||
"name": name,
|
|
||||||
"container": component_entity,
|
|
||||||
"size": size,
|
|
||||||
"file_type": os.path.splitext(member_path)[-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
component = session.create(
|
|
||||||
"FileComponent", member_data
|
|
||||||
)
|
|
||||||
origin_location.add_component(
|
|
||||||
component, member_path, recursive=False
|
|
||||||
)
|
|
||||||
component_entity["members"].append(component)
|
|
||||||
|
|
||||||
# Add components to location.
|
|
||||||
location.add_component(
|
|
||||||
component_entity, origin_location, recursive=True
|
|
||||||
)
|
|
||||||
|
|
||||||
data["component"] = component_entity
|
|
||||||
msg = "Overwriting Component with path: {0}, data: {1}, "
|
|
||||||
msg += "location: {2}"
|
|
||||||
self.log.info(
|
|
||||||
msg.format(
|
|
||||||
data["component_path"],
|
|
||||||
component_data,
|
|
||||||
location
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Extracting metadata, and adding after entity creation. This is
|
|
||||||
# due to a ftrack_api bug where you can't add metadata on creation.
|
|
||||||
component_metadata = component_data.pop("metadata", {})
|
|
||||||
|
|
||||||
# Create new component if none exists.
|
|
||||||
new_component = False
|
|
||||||
if not component_entity:
|
|
||||||
component_entity = assetversion_entity.create_component(
|
|
||||||
data["component_path"],
|
|
||||||
data=component_data,
|
|
||||||
location=location
|
|
||||||
)
|
|
||||||
data["component"] = component_entity
|
|
||||||
msg = "Created new Component with path: {0}, data: {1}"
|
|
||||||
msg += ", metadata: {2}, location: {3}"
|
|
||||||
self.log.info(
|
|
||||||
msg.format(
|
|
||||||
data["component_path"],
|
|
||||||
component_data,
|
|
||||||
component_metadata,
|
|
||||||
location
|
|
||||||
)
|
|
||||||
)
|
|
||||||
new_component = True
|
|
||||||
|
|
||||||
# Adding metadata
|
|
||||||
existing_component_metadata = component_entity["metadata"]
|
|
||||||
existing_component_metadata.update(component_metadata)
|
|
||||||
component_entity["metadata"] = existing_component_metadata
|
|
||||||
|
|
||||||
# if component_data['name'] = 'ftrackreview-mp4-mp4':
|
|
||||||
# assetversion_entity["thumbnail_id"]
|
|
||||||
|
|
||||||
# Setting assetversion thumbnail
|
|
||||||
if data.get("thumbnail", False):
|
|
||||||
assetversion_entity["thumbnail_id"] = component_entity["id"]
|
|
||||||
|
|
||||||
# Inform user about no changes to the database.
|
|
||||||
if (component_entity and not component_overwrite and
|
|
||||||
not new_component):
|
|
||||||
data["component"] = component_entity
|
|
||||||
self.log.info(
|
|
||||||
"Found existing component, and no request to overwrite. "
|
|
||||||
"Nothing has been changed."
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# Commit changes.
|
# Changing file type
|
||||||
try:
|
component_entity["file_type"] = collection.format("{tail}")
|
||||||
session.commit()
|
|
||||||
except Exception:
|
|
||||||
tp, value, tb = sys.exc_info()
|
|
||||||
session.rollback()
|
|
||||||
session._configure_locations()
|
|
||||||
six.reraise(tp, value, tb)
|
|
||||||
|
|
||||||
if assetversion_entity not in used_asset_versions:
|
# Create member components for sequence.
|
||||||
used_asset_versions.append(assetversion_entity)
|
for member_path in collection:
|
||||||
|
|
||||||
asset_versions_key = "ftrackIntegratedAssetVersions"
|
size = 0
|
||||||
if asset_versions_key not in instance.data:
|
try:
|
||||||
instance.data[asset_versions_key] = []
|
size = os.path.getsize(member_path)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
for asset_version in used_asset_versions:
|
name = collection.match(member_path).group("index")
|
||||||
if asset_version not in instance.data[asset_versions_key]:
|
|
||||||
instance.data[asset_versions_key].append(asset_version)
|
member_data = {
|
||||||
|
"name": name,
|
||||||
|
"container": component_entity,
|
||||||
|
"size": size,
|
||||||
|
"file_type": os.path.splitext(member_path)[-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
component = session.create(
|
||||||
|
"FileComponent", member_data
|
||||||
|
)
|
||||||
|
origin_location.add_component(
|
||||||
|
component, member_path, recursive=False
|
||||||
|
)
|
||||||
|
component_entity["members"].append(component)
|
||||||
|
|
||||||
|
# Add components to location.
|
||||||
|
location.add_component(
|
||||||
|
component_entity, origin_location, recursive=True
|
||||||
|
)
|
||||||
|
|
||||||
|
data["component"] = component_entity
|
||||||
|
self.log.info(
|
||||||
|
(
|
||||||
|
"Overwriting Component with path: {0}, data: {1},"
|
||||||
|
" location: {2}"
|
||||||
|
).format(
|
||||||
|
data["component_path"],
|
||||||
|
component_data,
|
||||||
|
location
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Extracting metadata, and adding after entity creation. This is
|
||||||
|
# due to a ftrack_api bug where you can't add metadata on creation.
|
||||||
|
component_metadata = component_data.pop("metadata", {})
|
||||||
|
|
||||||
|
# Create new component if none exists.
|
||||||
|
new_component = False
|
||||||
|
if not component_entity:
|
||||||
|
component_entity = asset_version_entity.create_component(
|
||||||
|
data["component_path"],
|
||||||
|
data=component_data,
|
||||||
|
location=location
|
||||||
|
)
|
||||||
|
data["component"] = component_entity
|
||||||
|
self.log.info(
|
||||||
|
(
|
||||||
|
"Created new Component with path: {0}, data: {1},"
|
||||||
|
" metadata: {2}, location: {3}"
|
||||||
|
).format(
|
||||||
|
data["component_path"],
|
||||||
|
component_data,
|
||||||
|
component_metadata,
|
||||||
|
location
|
||||||
|
)
|
||||||
|
)
|
||||||
|
new_component = True
|
||||||
|
|
||||||
|
# Adding metadata
|
||||||
|
existing_component_metadata = component_entity["metadata"]
|
||||||
|
existing_component_metadata.update(component_metadata)
|
||||||
|
component_entity["metadata"] = existing_component_metadata
|
||||||
|
|
||||||
|
# if component_data['name'] = 'ftrackreview-mp4-mp4':
|
||||||
|
# assetversion_entity["thumbnail_id"]
|
||||||
|
|
||||||
|
# Setting assetversion thumbnail
|
||||||
|
if data.get("thumbnail"):
|
||||||
|
asset_version_entity["thumbnail_id"] = component_entity["id"]
|
||||||
|
|
||||||
|
# Inform user about no changes to the database.
|
||||||
|
if (
|
||||||
|
component_entity
|
||||||
|
and not component_overwrite
|
||||||
|
and not new_component
|
||||||
|
):
|
||||||
|
data["component"] = component_entity
|
||||||
|
self.log.info(
|
||||||
|
"Found existing component, and no request to overwrite. "
|
||||||
|
"Nothing has been changed."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Commit changes.
|
||||||
|
try:
|
||||||
|
session.commit()
|
||||||
|
except Exception:
|
||||||
|
tp, value, tb = sys.exc_info()
|
||||||
|
session.rollback()
|
||||||
|
session._configure_locations()
|
||||||
|
six.reraise(tp, value, tb)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
"""
|
||||||
|
Requires:
|
||||||
|
context > comment
|
||||||
|
context > ftrackSession
|
||||||
|
instance > ftrackIntegratedAssetVersionsData
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import six
|
||||||
|
import pyblish.api
|
||||||
|
|
||||||
|
|
||||||
|
class IntegrateFtrackDescription(pyblish.api.InstancePlugin):
|
||||||
|
"""Add description to AssetVersions in Ftrack."""
|
||||||
|
|
||||||
|
# Must be after integrate asset new
|
||||||
|
order = pyblish.api.IntegratorOrder + 0.4999
|
||||||
|
label = "Integrate Ftrack description"
|
||||||
|
families = ["ftrack"]
|
||||||
|
optional = True
|
||||||
|
|
||||||
|
# Can be set in settings:
|
||||||
|
# - Allows `intent` and `comment` keys
|
||||||
|
description_template = "{comment}"
|
||||||
|
|
||||||
|
def process(self, instance):
|
||||||
|
# Check if there are any integrated AssetVersion entities
|
||||||
|
asset_versions_key = "ftrackIntegratedAssetVersionsData"
|
||||||
|
asset_versions_data_by_id = instance.data.get(asset_versions_key)
|
||||||
|
if not asset_versions_data_by_id:
|
||||||
|
self.log.info("There are any integrated AssetVersions")
|
||||||
|
return
|
||||||
|
|
||||||
|
comment = (instance.context.data.get("comment") or "").strip()
|
||||||
|
if not comment:
|
||||||
|
self.log.info("Comment is not set.")
|
||||||
|
else:
|
||||||
|
self.log.debug("Comment is set to `{}`".format(comment))
|
||||||
|
|
||||||
|
session = instance.context.data["ftrackSession"]
|
||||||
|
|
||||||
|
intent = instance.context.data.get("intent")
|
||||||
|
intent_label = None
|
||||||
|
if intent and isinstance(intent, dict):
|
||||||
|
intent_val = intent.get("value")
|
||||||
|
intent_label = intent.get("label")
|
||||||
|
else:
|
||||||
|
intent_val = intent
|
||||||
|
|
||||||
|
if not intent_label:
|
||||||
|
intent_label = intent_val or ""
|
||||||
|
|
||||||
|
# if intent label is set then format comment
|
||||||
|
# - it is possible that intent_label is equal to "" (empty string)
|
||||||
|
if intent_label:
|
||||||
|
self.log.debug(
|
||||||
|
"Intent label is set to `{}`.".format(intent_label)
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.log.debug("Intent is not set.")
|
||||||
|
|
||||||
|
for asset_version_data in asset_versions_data_by_id.values():
|
||||||
|
asset_version = asset_version_data["asset_version"]
|
||||||
|
|
||||||
|
# Backwards compatibility for older settings using
|
||||||
|
# attribute 'note_with_intent_template'
|
||||||
|
comment = self.description_template.format(**{
|
||||||
|
"intent": intent_label,
|
||||||
|
"comment": comment
|
||||||
|
})
|
||||||
|
asset_version["comment"] = comment
|
||||||
|
|
||||||
|
try:
|
||||||
|
session.commit()
|
||||||
|
self.log.debug("Comment added to AssetVersion \"{}\"".format(
|
||||||
|
str(asset_version)
|
||||||
|
))
|
||||||
|
except Exception:
|
||||||
|
tp, value, tb = sys.exc_info()
|
||||||
|
session.rollback()
|
||||||
|
session._configure_locations()
|
||||||
|
six.reraise(tp, value, tb)
|
||||||
|
|
@ -40,6 +40,13 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin):
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
self.log.debug("instance {}".format(instance))
|
self.log.debug("instance {}".format(instance))
|
||||||
|
|
||||||
|
instance_repres = instance.data.get("representations")
|
||||||
|
if not instance_repres:
|
||||||
|
self.log.info((
|
||||||
|
"Skipping instance. Does not have any representations {}"
|
||||||
|
).format(str(instance)))
|
||||||
|
return
|
||||||
|
|
||||||
instance_version = instance.data.get("version")
|
instance_version = instance.data.get("version")
|
||||||
if instance_version is None:
|
if instance_version is None:
|
||||||
raise ValueError("Instance version not set")
|
raise ValueError("Instance version not set")
|
||||||
|
|
@ -53,8 +60,12 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin):
|
||||||
if not asset_type and family_low in self.family_mapping:
|
if not asset_type and family_low in self.family_mapping:
|
||||||
asset_type = self.family_mapping[family_low]
|
asset_type = self.family_mapping[family_low]
|
||||||
|
|
||||||
self.log.debug(self.family_mapping)
|
if not asset_type:
|
||||||
self.log.debug(family_low)
|
asset_type = "upload"
|
||||||
|
|
||||||
|
self.log.debug(
|
||||||
|
"Family: {}\nMapping: {}".format(family_low, self.family_mapping)
|
||||||
|
)
|
||||||
|
|
||||||
# Ignore this instance if neither "ftrackFamily" or a family mapping is
|
# Ignore this instance if neither "ftrackFamily" or a family mapping is
|
||||||
# found.
|
# found.
|
||||||
|
|
@ -64,13 +75,6 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin):
|
||||||
).format(family))
|
).format(family))
|
||||||
return
|
return
|
||||||
|
|
||||||
instance_repres = instance.data.get("representations")
|
|
||||||
if not instance_repres:
|
|
||||||
self.log.info((
|
|
||||||
"Skipping instance. Does not have any representations {}"
|
|
||||||
).format(str(instance)))
|
|
||||||
return
|
|
||||||
|
|
||||||
# Prepare FPS
|
# Prepare FPS
|
||||||
instance_fps = instance.data.get("fps")
|
instance_fps = instance.data.get("fps")
|
||||||
if instance_fps is None:
|
if instance_fps is None:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,17 @@
|
||||||
|
"""
|
||||||
|
Requires:
|
||||||
|
context > hostName
|
||||||
|
context > appName
|
||||||
|
context > appLabel
|
||||||
|
context > comment
|
||||||
|
context > ftrackSession
|
||||||
|
instance > ftrackIntegratedAssetVersionsData
|
||||||
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import json
|
|
||||||
import pyblish.api
|
|
||||||
import six
|
import six
|
||||||
|
import pyblish.api
|
||||||
|
|
||||||
|
|
||||||
class IntegrateFtrackNote(pyblish.api.InstancePlugin):
|
class IntegrateFtrackNote(pyblish.api.InstancePlugin):
|
||||||
|
|
@ -15,100 +25,52 @@ class IntegrateFtrackNote(pyblish.api.InstancePlugin):
|
||||||
|
|
||||||
# Can be set in presets:
|
# Can be set in presets:
|
||||||
# - Allows only `intent` and `comment` keys
|
# - Allows only `intent` and `comment` keys
|
||||||
|
note_template = None
|
||||||
|
# Backwards compatibility
|
||||||
note_with_intent_template = "{intent}: {comment}"
|
note_with_intent_template = "{intent}: {comment}"
|
||||||
# - note label must exist in Ftrack
|
# - note label must exist in Ftrack
|
||||||
note_labels = []
|
note_labels = []
|
||||||
|
|
||||||
def get_intent_label(self, session, intent_value):
|
|
||||||
if not intent_value:
|
|
||||||
return
|
|
||||||
|
|
||||||
intent_configurations = session.query(
|
|
||||||
"CustomAttributeConfiguration where key is intent"
|
|
||||||
).all()
|
|
||||||
if not intent_configurations:
|
|
||||||
return
|
|
||||||
|
|
||||||
intent_configuration = intent_configurations[0]
|
|
||||||
if len(intent_configuration) > 1:
|
|
||||||
self.log.warning((
|
|
||||||
"Found more than one `intent` custom attribute."
|
|
||||||
" Using first found."
|
|
||||||
))
|
|
||||||
|
|
||||||
config = intent_configuration.get("config")
|
|
||||||
if not config:
|
|
||||||
return
|
|
||||||
|
|
||||||
configuration = json.loads(config)
|
|
||||||
items = configuration.get("data")
|
|
||||||
if not items:
|
|
||||||
return
|
|
||||||
|
|
||||||
if sys.version_info[0] < 3:
|
|
||||||
string_type = basestring
|
|
||||||
else:
|
|
||||||
string_type = str
|
|
||||||
|
|
||||||
if isinstance(items, string_type):
|
|
||||||
items = json.loads(items)
|
|
||||||
|
|
||||||
intent_label = None
|
|
||||||
for item in items:
|
|
||||||
if item["value"] == intent_value:
|
|
||||||
intent_label = item["menu"]
|
|
||||||
break
|
|
||||||
|
|
||||||
return intent_label
|
|
||||||
|
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
comment = (instance.context.data.get("comment") or "").strip()
|
# Check if there are any integrated AssetVersion entities
|
||||||
|
asset_versions_key = "ftrackIntegratedAssetVersionsData"
|
||||||
|
asset_versions_data_by_id = instance.data.get(asset_versions_key)
|
||||||
|
if not asset_versions_data_by_id:
|
||||||
|
self.log.info("There are any integrated AssetVersions")
|
||||||
|
return
|
||||||
|
|
||||||
|
context = instance.context
|
||||||
|
host_name = context.data["hostName"]
|
||||||
|
app_name = context.data["appName"]
|
||||||
|
app_label = context.data["appLabel"]
|
||||||
|
comment = (context.data.get("comment") or "").strip()
|
||||||
if not comment:
|
if not comment:
|
||||||
self.log.info("Comment is not set.")
|
self.log.info("Comment is not set.")
|
||||||
return
|
else:
|
||||||
|
self.log.debug("Comment is set to `{}`".format(comment))
|
||||||
|
|
||||||
self.log.debug("Comment is set to `{}`".format(comment))
|
session = context.data["ftrackSession"]
|
||||||
|
|
||||||
session = instance.context.data["ftrackSession"]
|
|
||||||
|
|
||||||
intent = instance.context.data.get("intent")
|
intent = instance.context.data.get("intent")
|
||||||
|
intent_label = None
|
||||||
if intent and isinstance(intent, dict):
|
if intent and isinstance(intent, dict):
|
||||||
intent_val = intent.get("value")
|
intent_val = intent.get("value")
|
||||||
intent_label = intent.get("label")
|
intent_label = intent.get("label")
|
||||||
else:
|
else:
|
||||||
intent_val = intent_label = intent
|
intent_val = intent
|
||||||
|
|
||||||
final_label = None
|
if not intent_label:
|
||||||
if intent_val:
|
intent_label = intent_val or ""
|
||||||
final_label = self.get_intent_label(session, intent_val)
|
|
||||||
if final_label is None:
|
|
||||||
final_label = intent_label
|
|
||||||
|
|
||||||
# if intent label is set then format comment
|
# if intent label is set then format comment
|
||||||
# - it is possible that intent_label is equal to "" (empty string)
|
# - it is possible that intent_label is equal to "" (empty string)
|
||||||
if final_label:
|
if intent_label:
|
||||||
msg = "Intent label is set to `{}`.".format(final_label)
|
self.log.debug(
|
||||||
comment = self.note_with_intent_template.format(**{
|
"Intent label is set to `{}`.".format(intent_label)
|
||||||
"intent": final_label,
|
)
|
||||||
"comment": comment
|
|
||||||
})
|
|
||||||
|
|
||||||
elif intent_val:
|
|
||||||
msg = (
|
|
||||||
"Intent is set to `{}` and was not added"
|
|
||||||
" to comment because label is set to `{}`."
|
|
||||||
).format(intent_val, final_label)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
msg = "Intent is not set."
|
self.log.debug("Intent is not set.")
|
||||||
|
|
||||||
self.log.debug(msg)
|
|
||||||
|
|
||||||
asset_versions_key = "ftrackIntegratedAssetVersions"
|
|
||||||
asset_versions = instance.data.get(asset_versions_key)
|
|
||||||
if not asset_versions:
|
|
||||||
self.log.info("There are any integrated AssetVersions")
|
|
||||||
return
|
|
||||||
|
|
||||||
user = session.query(
|
user = session.query(
|
||||||
"User where username is \"{}\"".format(session.api_user)
|
"User where username is \"{}\"".format(session.api_user)
|
||||||
|
|
@ -122,7 +84,7 @@ class IntegrateFtrackNote(pyblish.api.InstancePlugin):
|
||||||
|
|
||||||
labels = []
|
labels = []
|
||||||
if self.note_labels:
|
if self.note_labels:
|
||||||
all_labels = session.query("NoteLabel").all()
|
all_labels = session.query("select id, name from NoteLabel").all()
|
||||||
labels_by_low_name = {lab["name"].lower(): lab for lab in all_labels}
|
labels_by_low_name = {lab["name"].lower(): lab for lab in all_labels}
|
||||||
for _label in self.note_labels:
|
for _label in self.note_labels:
|
||||||
label = labels_by_low_name.get(_label.lower())
|
label = labels_by_low_name.get(_label.lower())
|
||||||
|
|
@ -134,7 +96,34 @@ class IntegrateFtrackNote(pyblish.api.InstancePlugin):
|
||||||
|
|
||||||
labels.append(label)
|
labels.append(label)
|
||||||
|
|
||||||
for asset_version in asset_versions:
|
for asset_version_data in asset_versions_data_by_id.values():
|
||||||
|
asset_version = asset_version_data["asset_version"]
|
||||||
|
component_items = asset_version_data["component_items"]
|
||||||
|
|
||||||
|
published_paths = set()
|
||||||
|
for component_item in component_items:
|
||||||
|
published_paths.add(component_item["component_path"])
|
||||||
|
|
||||||
|
# Backwards compatibility for older settings using
|
||||||
|
# attribute 'note_with_intent_template'
|
||||||
|
template = self.note_template
|
||||||
|
if template is None:
|
||||||
|
template = self.note_with_intent_template
|
||||||
|
format_data = {
|
||||||
|
"intent": intent_label,
|
||||||
|
"comment": comment,
|
||||||
|
"host_name": host_name,
|
||||||
|
"app_name": app_name,
|
||||||
|
"app_label": app_label,
|
||||||
|
"published_paths": "<br/>".join(sorted(published_paths)),
|
||||||
|
}
|
||||||
|
comment = template.format(**format_data)
|
||||||
|
if not comment:
|
||||||
|
self.log.info((
|
||||||
|
"Note for AssetVersion {} would be empty. Skipping."
|
||||||
|
"\nTemplate: {}\nData: {}"
|
||||||
|
).format(asset_version["id"], template, format_data))
|
||||||
|
continue
|
||||||
asset_version.create_note(comment, author=user, labels=labels)
|
asset_version.create_note(comment, author=user, labels=labels)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -389,7 +389,8 @@ class PythonInterpreterWidget(QtWidgets.QWidget):
|
||||||
|
|
||||||
self._append_lines([openpype_art])
|
self._append_lines([openpype_art])
|
||||||
|
|
||||||
self.setStyleSheet(load_stylesheet())
|
self._first_show = True
|
||||||
|
self._splitter_size_ratio = None
|
||||||
|
|
||||||
self._init_from_registry()
|
self._init_from_registry()
|
||||||
|
|
||||||
|
|
@ -416,9 +417,9 @@ class PythonInterpreterWidget(QtWidgets.QWidget):
|
||||||
self.resize(width, height)
|
self.resize(width, height)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sizes = setting_registry.get_item("splitter_sizes")
|
self._splitter_size_ratio = (
|
||||||
if len(sizes) == len(self._widgets_splitter.sizes()):
|
setting_registry.get_item("splitter_sizes")
|
||||||
self._widgets_splitter.setSizes(sizes)
|
)
|
||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
@ -627,8 +628,29 @@ class PythonInterpreterWidget(QtWidgets.QWidget):
|
||||||
def showEvent(self, event):
|
def showEvent(self, event):
|
||||||
self._line_check_timer.start()
|
self._line_check_timer.start()
|
||||||
super(PythonInterpreterWidget, self).showEvent(event)
|
super(PythonInterpreterWidget, self).showEvent(event)
|
||||||
|
# First show setup
|
||||||
|
if self._first_show:
|
||||||
|
self._first_show = False
|
||||||
|
self._on_first_show()
|
||||||
|
|
||||||
self._output_widget.scroll_to_bottom()
|
self._output_widget.scroll_to_bottom()
|
||||||
|
|
||||||
|
def _on_first_show(self):
|
||||||
|
# Change stylesheet
|
||||||
|
self.setStyleSheet(load_stylesheet())
|
||||||
|
# Check if splitter size ratio is set
|
||||||
|
# - first store value to local variable and then unset it
|
||||||
|
splitter_size_ratio = self._splitter_size_ratio
|
||||||
|
self._splitter_size_ratio = None
|
||||||
|
# Skip if is not set
|
||||||
|
if not splitter_size_ratio:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Skip if number of size items does not match to splitter
|
||||||
|
splitters_count = len(self._widgets_splitter.sizes())
|
||||||
|
if len(splitter_size_ratio) == splitters_count:
|
||||||
|
self._widgets_splitter.setSizes(splitter_size_ratio)
|
||||||
|
|
||||||
def closeEvent(self, event):
|
def closeEvent(self, event):
|
||||||
self.save_registry()
|
self.save_registry()
|
||||||
super(PythonInterpreterWidget, self).closeEvent(event)
|
super(PythonInterpreterWidget, self).closeEvent(event)
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ from .load import (
|
||||||
|
|
||||||
loaders_from_representation,
|
loaders_from_representation,
|
||||||
get_representation_path,
|
get_representation_path,
|
||||||
|
get_representation_context,
|
||||||
get_repres_contexts,
|
get_repres_contexts,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -113,6 +114,7 @@ __all__ = (
|
||||||
|
|
||||||
"loaders_from_representation",
|
"loaders_from_representation",
|
||||||
"get_representation_path",
|
"get_representation_path",
|
||||||
|
"get_representation_context",
|
||||||
"get_repres_contexts",
|
"get_repres_contexts",
|
||||||
|
|
||||||
# --- Publish ---
|
# --- Publish ---
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,8 @@ class LoaderPlugin(list):
|
||||||
def get_representations(cls):
|
def get_representations(cls):
|
||||||
return cls.representations
|
return cls.representations
|
||||||
|
|
||||||
def filepath_from_context(self, context):
|
@classmethod
|
||||||
|
def filepath_from_context(cls, context):
|
||||||
return get_representation_path_from_context(context)
|
return get_representation_path_from_context(context)
|
||||||
|
|
||||||
def load(self, context, name=None, namespace=None, options=None):
|
def load(self, context, name=None, namespace=None, options=None):
|
||||||
|
|
|
||||||
|
|
@ -18,20 +18,30 @@ class CollectHostName(pyblish.api.ContextPlugin):
|
||||||
|
|
||||||
def process(self, context):
|
def process(self, context):
|
||||||
host_name = context.data.get("hostName")
|
host_name = context.data.get("hostName")
|
||||||
|
app_name = context.data.get("appName")
|
||||||
|
app_label = context.data.get("appLabel")
|
||||||
# Don't override value if is already set
|
# Don't override value if is already set
|
||||||
if host_name:
|
if host_name and app_name and app_label:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Use AVALON_APP as first if available it is the same as host name
|
# Use AVALON_APP to get host name if available
|
||||||
# - only if is not defined use AVALON_APP_NAME (e.g. on Farm) and
|
|
||||||
# set it back to AVALON_APP env variable
|
|
||||||
host_name = os.environ.get("AVALON_APP")
|
|
||||||
if not host_name:
|
if not host_name:
|
||||||
|
host_name = os.environ.get("AVALON_APP")
|
||||||
|
|
||||||
|
# Use AVALON_APP_NAME to get full app name
|
||||||
|
if not app_name:
|
||||||
app_name = os.environ.get("AVALON_APP_NAME")
|
app_name = os.environ.get("AVALON_APP_NAME")
|
||||||
if app_name:
|
|
||||||
app_manager = ApplicationManager()
|
# Fill missing values based on app full name
|
||||||
app = app_manager.applications.get(app_name)
|
if (not host_name or not app_label) and app_name:
|
||||||
if app:
|
app_manager = ApplicationManager()
|
||||||
|
app = app_manager.applications.get(app_name)
|
||||||
|
if app:
|
||||||
|
if not host_name:
|
||||||
host_name = app.host_name
|
host_name = app.host_name
|
||||||
|
if not app_label:
|
||||||
|
app_label = app.full_label
|
||||||
|
|
||||||
context.data["hostName"] = host_name
|
context.data["hostName"] = host_name
|
||||||
|
context.data["appName"] = app_name
|
||||||
|
context.data["appLabel"] = app_label
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ import pyblish.api
|
||||||
from openpype.pipeline import PublishValidationError
|
from openpype.pipeline import PublishValidationError
|
||||||
|
|
||||||
|
|
||||||
class ValidateContainers(pyblish.api.InstancePlugin):
|
class ValidateAssetDocs(pyblish.api.InstancePlugin):
|
||||||
"""Validate existence of asset asset documents on instances.
|
"""Validate existence of asset documents on instances.
|
||||||
|
|
||||||
Without asset document it is not possible to publish the instance.
|
Without asset document it is not possible to publish the instance.
|
||||||
|
|
||||||
|
|
@ -22,10 +22,10 @@ class ValidateContainers(pyblish.api.InstancePlugin):
|
||||||
return
|
return
|
||||||
|
|
||||||
if instance.data.get("assetEntity"):
|
if instance.data.get("assetEntity"):
|
||||||
self.log.info("Instance have set asset document in it's data.")
|
self.log.info("Instance has set asset document in its data.")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise PublishValidationError((
|
raise PublishValidationError((
|
||||||
"Instance \"{}\" don't have set asset"
|
"Instance \"{}\" doesn't have asset document "
|
||||||
" document which is needed for publishing."
|
"set which is needed for publishing."
|
||||||
).format(instance.data["name"]))
|
).format(instance.data["name"]))
|
||||||
|
|
@ -15,33 +15,6 @@
|
||||||
"deadline"
|
"deadline"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"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)(?:\\.|_).*"
|
|
||||||
],
|
|
||||||
"nuke": [
|
|
||||||
".*"
|
|
||||||
],
|
|
||||||
"aftereffects": [
|
|
||||||
".*"
|
|
||||||
],
|
|
||||||
"celaction": [
|
|
||||||
".*"
|
|
||||||
],
|
|
||||||
"harmony": [
|
|
||||||
".*"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"MayaSubmitDeadline": {
|
"MayaSubmitDeadline": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"optional": false,
|
"optional": false,
|
||||||
|
|
@ -49,6 +22,8 @@
|
||||||
"tile_assembler_plugin": "OpenPypeTileAssembler",
|
"tile_assembler_plugin": "OpenPypeTileAssembler",
|
||||||
"use_published": true,
|
"use_published": true,
|
||||||
"asset_dependencies": true,
|
"asset_dependencies": true,
|
||||||
|
"priority": 50,
|
||||||
|
"tile_priority": 50,
|
||||||
"group": "none",
|
"group": "none",
|
||||||
"limit": [],
|
"limit": [],
|
||||||
"jobInfo": {},
|
"jobInfo": {},
|
||||||
|
|
@ -96,6 +71,33 @@
|
||||||
"group": "",
|
"group": "",
|
||||||
"department": "",
|
"department": "",
|
||||||
"multiprocess": true
|
"multiprocess": true
|
||||||
|
},
|
||||||
|
"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)(?:\\.|_).*"
|
||||||
|
],
|
||||||
|
"nuke": [
|
||||||
|
".*"
|
||||||
|
],
|
||||||
|
"aftereffects": [
|
||||||
|
".*"
|
||||||
|
],
|
||||||
|
"celaction": [
|
||||||
|
".*"
|
||||||
|
],
|
||||||
|
"harmony": [
|
||||||
|
".*"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -354,9 +354,15 @@
|
||||||
},
|
},
|
||||||
"IntegrateFtrackNote": {
|
"IntegrateFtrackNote": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"note_with_intent_template": "{intent}: {comment}",
|
"note_template": "{intent}: {comment}",
|
||||||
"note_labels": []
|
"note_labels": []
|
||||||
},
|
},
|
||||||
|
"IntegrateFtrackDescription": {
|
||||||
|
"enabled": false,
|
||||||
|
"optional": true,
|
||||||
|
"active": true,
|
||||||
|
"description_template": "{comment}"
|
||||||
|
},
|
||||||
"ValidateFtrackAttributes": {
|
"ValidateFtrackAttributes": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"ftrack_custom_attributes": {}
|
"ftrack_custom_attributes": {}
|
||||||
|
|
|
||||||
|
|
@ -190,7 +190,7 @@
|
||||||
"tasks": [],
|
"tasks": [],
|
||||||
"template_name": "simpleUnrealTexture"
|
"template_name": "simpleUnrealTexture"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"families": [
|
"families": [
|
||||||
"staticMesh",
|
"staticMesh",
|
||||||
"skeletalMesh"
|
"skeletalMesh"
|
||||||
|
|
@ -279,6 +279,15 @@
|
||||||
"tasks": [],
|
"tasks": [],
|
||||||
"template": "{family}{variant}"
|
"template": "{family}{variant}"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"families": [
|
||||||
|
"workfile"
|
||||||
|
],
|
||||||
|
"hosts": [],
|
||||||
|
"task_types": [],
|
||||||
|
"tasks": [],
|
||||||
|
"template": "{family}{Task}"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"families": [
|
"families": [
|
||||||
"render"
|
"render"
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,16 @@
|
||||||
"key": "asset_dependencies",
|
"key": "asset_dependencies",
|
||||||
"label": "Use Asset dependencies"
|
"label": "Use Asset dependencies"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"key": "priority",
|
||||||
|
"label": "Priority"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"key": "tile_priority",
|
||||||
|
"label": "Tile Assembler Priority"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"key": "group",
|
"key": "group",
|
||||||
|
|
|
||||||
|
|
@ -738,10 +738,15 @@
|
||||||
"key": "enabled",
|
"key": "enabled",
|
||||||
"label": "Enabled"
|
"label": "Enabled"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "label",
|
||||||
|
"label": "Template may contain formatting keys <b>intent</b>, <b>comment</b>, <b>host_name</b>, <b>app_name</b>, <b>app_label</b> and <b>published_paths</b>."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"key": "note_with_intent_template",
|
"key": "note_template",
|
||||||
"label": "Note with intent template"
|
"label": "Note template",
|
||||||
|
"multiline": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "list",
|
"type": "list",
|
||||||
|
|
@ -751,6 +756,44 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "dict",
|
||||||
|
"collapsible": true,
|
||||||
|
"checkbox_key": "enabled",
|
||||||
|
"key": "IntegrateFtrackDescription",
|
||||||
|
"label": "Integrate Ftrack Description",
|
||||||
|
"is_group": true,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"key": "enabled",
|
||||||
|
"label": "Enabled"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "label",
|
||||||
|
"label": "Add description to integrated AssetVersion."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"key": "optional",
|
||||||
|
"label": "Optional"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"key": "active",
|
||||||
|
"label": "Active"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "label",
|
||||||
|
"label": "Template may contain formatting keys <b>intent</b> and <b>comment</b>."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"key": "description_template",
|
||||||
|
"label": "Description template"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "dict",
|
"type": "dict",
|
||||||
"collapsible": true,
|
"collapsible": true,
|
||||||
|
|
|
||||||
|
|
@ -216,7 +216,7 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
||||||
def create_ui(self):
|
def create_ui(self):
|
||||||
self.modify_defaults_checkbox = None
|
self.modify_defaults_checkbox = None
|
||||||
|
|
||||||
conf_wrapper_widget = QtWidgets.QWidget(self)
|
conf_wrapper_widget = QtWidgets.QSplitter(self)
|
||||||
configurations_widget = QtWidgets.QWidget(conf_wrapper_widget)
|
configurations_widget = QtWidgets.QWidget(conf_wrapper_widget)
|
||||||
|
|
||||||
# Breadcrumbs/Path widget
|
# Breadcrumbs/Path widget
|
||||||
|
|
@ -294,10 +294,7 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
||||||
|
|
||||||
configurations_layout.addWidget(scroll_widget, 1)
|
configurations_layout.addWidget(scroll_widget, 1)
|
||||||
|
|
||||||
conf_wrapper_layout = QtWidgets.QHBoxLayout(conf_wrapper_widget)
|
conf_wrapper_widget.addWidget(configurations_widget)
|
||||||
conf_wrapper_layout.setContentsMargins(0, 0, 0, 0)
|
|
||||||
conf_wrapper_layout.setSpacing(0)
|
|
||||||
conf_wrapper_layout.addWidget(configurations_widget, 1)
|
|
||||||
|
|
||||||
main_layout = QtWidgets.QVBoxLayout(self)
|
main_layout = QtWidgets.QVBoxLayout(self)
|
||||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
|
@ -327,7 +324,7 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
||||||
self.breadcrumbs_model = None
|
self.breadcrumbs_model = None
|
||||||
self.refresh_btn = refresh_btn
|
self.refresh_btn = refresh_btn
|
||||||
|
|
||||||
self.conf_wrapper_layout = conf_wrapper_layout
|
self.conf_wrapper_widget = conf_wrapper_widget
|
||||||
self.main_layout = main_layout
|
self.main_layout = main_layout
|
||||||
|
|
||||||
self.ui_tweaks()
|
self.ui_tweaks()
|
||||||
|
|
@ -818,7 +815,9 @@ class ProjectWidget(SettingsCategoryWidget):
|
||||||
|
|
||||||
project_list_widget = ProjectListWidget(self)
|
project_list_widget = ProjectListWidget(self)
|
||||||
|
|
||||||
self.conf_wrapper_layout.insertWidget(0, project_list_widget, 0)
|
self.conf_wrapper_widget.insertWidget(0, project_list_widget)
|
||||||
|
self.conf_wrapper_widget.setStretchFactor(0, 0)
|
||||||
|
self.conf_wrapper_widget.setStretchFactor(1, 1)
|
||||||
|
|
||||||
project_list_widget.project_changed.connect(self._on_project_change)
|
project_list_widget.project_changed.connect(self._on_project_change)
|
||||||
project_list_widget.version_change_requested.connect(
|
project_list_widget.version_change_requested.connect(
|
||||||
|
|
|
||||||
|
|
@ -24,16 +24,18 @@ class DBAssert:
|
||||||
else:
|
else:
|
||||||
args[key] = val
|
args[key] = val
|
||||||
|
|
||||||
msg = None
|
|
||||||
no_of_docs = dbcon.count_documents(args)
|
|
||||||
if expected != no_of_docs:
|
|
||||||
msg = "Not expected no of versions. "\
|
|
||||||
"Expected {}, found {}".format(expected, no_of_docs)
|
|
||||||
|
|
||||||
args.pop("type")
|
args.pop("type")
|
||||||
detail_str = " "
|
detail_str = " "
|
||||||
if args:
|
if args:
|
||||||
detail_str = " with {}".format(args)
|
detail_str = " with '{}'".format(args)
|
||||||
|
|
||||||
|
msg = None
|
||||||
|
no_of_docs = dbcon.count_documents(args)
|
||||||
|
if expected != no_of_docs:
|
||||||
|
msg = "Not expected no of '{}'{}."\
|
||||||
|
"Expected {}, found {}".format(queried_type,
|
||||||
|
detail_str,
|
||||||
|
expected, no_of_docs)
|
||||||
|
|
||||||
status = "successful"
|
status = "successful"
|
||||||
if msg:
|
if msg:
|
||||||
|
|
@ -42,7 +44,5 @@ class DBAssert:
|
||||||
print("Comparing count of {}{} {}".format(queried_type,
|
print("Comparing count of {}{} {}".format(queried_type,
|
||||||
detail_str,
|
detail_str,
|
||||||
status))
|
status))
|
||||||
if msg:
|
|
||||||
print(msg)
|
|
||||||
|
|
||||||
return msg
|
return msg
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue