[Automated] Merged develop into main

This commit is contained in:
pypebot 2022-04-07 14:08:34 +02:00 committed by GitHub
commit 4b3de45c0d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 892 additions and 577 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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,

View file

@ -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 ...")

View file

@ -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():

View file

@ -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)

View file

@ -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((

View file

@ -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)):

View file

@ -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"

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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 = []

View file

@ -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"),

View file

@ -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):

View file

@ -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)

View file

@ -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)

View file

@ -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:

View file

@ -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:

View file

@ -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)

View file

@ -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 ---

View file

@ -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):

View file

@ -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

View file

@ -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"]))

View file

@ -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": [
".*"
]
}
} }
} }
} }

View file

@ -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": {}

View file

@ -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"

View file

@ -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",

View file

@ -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,

View file

@ -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(

View file

@ -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