resolved conflict

This commit is contained in:
Kayla Man 2024-04-05 19:32:15 +08:00
commit 5dfa20869f
36 changed files with 637 additions and 345 deletions

View file

@ -4,6 +4,7 @@ import os
import sys
import code
import traceback
from pathlib import Path
import click
import acre
@ -11,7 +12,7 @@ import acre
from ayon_core import AYON_CORE_ROOT
from ayon_core.addon import AddonsManager
from ayon_core.settings import get_general_environments
from ayon_core.lib import initialize_ayon_connection
from ayon_core.lib import initialize_ayon_connection, is_running_from_build
from .cli_commands import Commands
@ -167,16 +168,27 @@ def run(script):
if not script:
print("Error: missing path to script file.")
return
# Remove first argument if it is the same as AYON executable
# - Forward compatibility with future AYON versions.
# - Current AYON launcher keeps the arguments with first argument but
# future versions might remove it.
first_arg = sys.argv[0]
if is_running_from_build():
comp_path = os.path.join(os.environ["AYON_ROOT"], "start.py")
else:
comp_path = os.getenv("AYON_EXECUTABLE")
# Compare paths and remove first argument if it is the same as AYON
if Path(first_arg).resolve() == Path(comp_path).resolve():
sys.argv.pop(0)
args = sys.argv
args.remove("run")
args.remove(script)
sys.argv = args
# Remove 'run' command from sys.argv
sys.argv.remove("run")
args_string = " ".join(args[1:])
print(f"... running: {script} {args_string}")
runpy.run_path(script, run_name="__main__", )
args_string = " ".join(sys.argv[1:])
print(f"... running: {script} {args_string}")
runpy.run_path(script, run_name="__main__")
@main_cli.command()

View file

@ -36,23 +36,23 @@ class HostDirmap(object):
host_name,
project_name,
project_settings=None,
sync_module=None
sitesync_addon=None
):
self.host_name = host_name
self.project_name = project_name
self._project_settings = project_settings
self._sync_module = sync_module
self._sitesync_addon = sitesync_addon
# to limit reinit of Modules
self._sync_module_discovered = sync_module is not None
self._sitesync_addon_discovered = sitesync_addon is not None
self._log = None
@property
def sync_module(self):
if not self._sync_module_discovered:
self._sync_module_discovered = True
def sitesync_addon(self):
if not self._sitesync_addon_discovered:
self._sitesync_addon_discovered = True
manager = AddonsManager()
self._sync_module = manager.get("sync_server")
return self._sync_module
self._sitesync_addon = manager.get("sitesync")
return self._sitesync_addon
@property
def project_settings(self):
@ -158,25 +158,25 @@ class HostDirmap(object):
"""
project_name = self.project_name
sync_module = self.sync_module
sitesync_addon = self.sitesync_addon
mapping = {}
if (
sync_module is None
or not sync_module.enabled
or project_name not in sync_module.get_enabled_projects()
sitesync_addon is None
or not sitesync_addon.enabled
or project_name not in sitesync_addon.get_enabled_projects()
):
return mapping
active_site = sync_module.get_local_normalized_site(
sync_module.get_active_site(project_name))
remote_site = sync_module.get_local_normalized_site(
sync_module.get_remote_site(project_name))
active_site = sitesync_addon.get_local_normalized_site(
sitesync_addon.get_active_site(project_name))
remote_site = sitesync_addon.get_local_normalized_site(
sitesync_addon.get_remote_site(project_name))
self.log.debug(
"active {} - remote {}".format(active_site, remote_site)
)
if active_site == "local" and active_site != remote_site:
sync_settings = sync_module.get_sync_project_setting(
sync_settings = sitesync_addon.get_sync_project_setting(
project_name,
exclude_locals=False,
cached=False)
@ -194,7 +194,7 @@ class HostDirmap(object):
self.log.debug("remote overrides {}".format(remote_overrides))
current_platform = platform.system().lower()
remote_provider = sync_module.get_provider_for_site(
remote_provider = sitesync_addon.get_provider_for_site(
project_name, remote_site
)
# dirmap has sense only with regular disk provider, in the workfile

View file

@ -16,6 +16,12 @@ from ayon_core.pipeline import (
AVALON_INSTANCE_ID,
AYON_INSTANCE_ID,
)
from ayon_core.pipeline.workfile import get_workdir
from ayon_api import (
get_project,
get_folder_by_path,
get_task_by_name
)
class GenericCreateSaver(Creator):
@ -125,6 +131,8 @@ class GenericCreateSaver(Creator):
product_name = data["productName"]
if (
original_product_name != product_name
or tool.GetData("openpype.task") != data["task"]
or tool.GetData("openpype.folderPath") != data["folderPath"]
or original_format != data["creator_attributes"]["image_format"]
):
self._configure_saver_tool(data, tool, product_name)
@ -145,7 +153,30 @@ class GenericCreateSaver(Creator):
folder_path = formatting_data["folderPath"]
folder_name = folder_path.rsplit("/", 1)[-1]
workdir = os.path.normpath(os.getenv("AYON_WORKDIR"))
# If the folder path and task do not match the current context then the
# workdir is not just the `AYON_WORKDIR`. Hence, we need to actually
# compute the resulting workdir
if (
data["folderPath"] == self.create_context.get_current_folder_path()
and data["task"] == self.create_context.get_current_task_name()
):
workdir = os.path.normpath(os.getenv("AYON_WORKDIR"))
else:
# TODO: Optimize this logic
project_name = self.create_context.get_current_project_name()
project_entity = get_project(project_name)
folder_entity = get_folder_by_path(project_name,
data["folderPath"])
task_entity = get_task_by_name(project_name,
folder_id=folder_entity["id"],
task_name=data["task"])
workdir = get_workdir(
project_entity=project_entity,
folder_entity=folder_entity,
task_entity=task_entity,
host_name=self.create_context.host_name,
)
formatting_data.update({
"workdir": workdir,
"frame": "0" * frame_padding,

View file

@ -0,0 +1,36 @@
import os
from ayon_core.lib import PreLaunchHook
from ayon_core.hosts.fusion import FUSION_HOST_DIR
class FusionLaunchMenuHook(PreLaunchHook):
"""Launch AYON menu on start of Fusion"""
app_groups = ["fusion"]
order = 9
def execute(self):
# Prelaunch hook is optional
settings = self.data["project_settings"][self.host_name]
if not settings["hooks"]["FusionLaunchMenuHook"]["enabled"]:
return
variant = self.application.name
if variant.isnumeric():
version = int(variant)
if version < 18:
print("Skipping launch of OpenPype menu on Fusion start "
"because Fusion version below 18.0 does not support "
"/execute argument on launch. "
f"Version detected: {version}")
return
else:
print(f"Application variant is not numeric: {variant}. "
"Validation for Fusion version 18+ for /execute "
"prelaunch argument skipped.")
path = os.path.join(FUSION_HOST_DIR,
"deploy",
"MenuScripts",
"launch_menu.py").replace("\\", "/")
script = f"fusion:RunScript('{path}')"
self.launch_context.launch_args.extend(["/execute", script])

View file

@ -2115,22 +2115,6 @@ def get_related_sets(node):
"""
# Ignore specific suffices
ignore_suffices = ["out_SET", "controls_SET", "_INST", "_CON"]
# Default nodes to ignore
defaults = {"defaultLightSet", "defaultObjectSet"}
# Ids to ignore
ignored = {
AVALON_INSTANCE_ID,
AVALON_CONTAINER_ID,
AYON_INSTANCE_ID,
AYON_CONTAINER_ID,
}
view_sets = get_isolate_view_sets()
sets = cmds.listSets(object=node, extendToShape=False)
if not sets:
return []
@ -2141,6 +2125,14 @@ def get_related_sets(node):
# returned by `cmds.listSets(allSets=True)`
sets = cmds.ls(sets)
# Ids to ignore
ignored = {
AVALON_INSTANCE_ID,
AVALON_CONTAINER_ID,
AYON_INSTANCE_ID,
AYON_CONTAINER_ID,
}
# Ignore `avalon.container`
sets = [
s for s in sets
@ -2149,21 +2141,31 @@ def get_related_sets(node):
or cmds.getAttr(f"{s}.id") not in ignored
)
]
if not sets:
return sets
# Exclude deformer sets (`type=2` for `maya.cmds.listSets`)
deformer_sets = cmds.listSets(object=node,
extendToShape=False,
type=2) or []
deformer_sets = set(deformer_sets) # optimize lookup
sets = [s for s in sets if s not in deformer_sets]
exclude_sets = cmds.listSets(object=node,
extendToShape=False,
type=2) or []
exclude_sets = set(exclude_sets) # optimize lookup
# Default nodes to ignore
exclude_sets.update({"defaultLightSet", "defaultObjectSet"})
# Filter out the sets to exclude
sets = [s for s in sets if s not in exclude_sets]
# Ignore when the set has a specific suffix
sets = [s for s in sets if not any(s.endswith(x) for x in ignore_suffices)]
ignore_suffices = ("out_SET", "controls_SET", "_INST", "_CON")
sets = [s for s in sets if not s.endswith(ignore_suffices)]
if not sets:
return sets
# Ignore viewport filter view sets (from isolate select and
# viewports)
view_sets = get_isolate_view_sets()
sets = [s for s in sets if s not in view_sets]
sets = [s for s in sets if s not in defaults]
return sets
@ -2434,12 +2436,10 @@ def set_scene_fps(fps, update=True):
cmds.currentUnit(time=unit, updateAnimation=update)
# Set time slider data back to previous state
cmds.playbackOptions(edit=True, minTime=start_frame)
cmds.playbackOptions(edit=True, maxTime=end_frame)
# Set animation data
cmds.playbackOptions(edit=True, animationStartTime=animation_start)
cmds.playbackOptions(edit=True, animationEndTime=animation_end)
cmds.playbackOptions(minTime=start_frame,
maxTime=end_frame,
animationStartTime=animation_start,
animationEndTime=animation_end)
cmds.currentTime(current_frame, edit=True, update=True)
@ -2629,14 +2629,15 @@ def reset_frame_range(playback=True, render=True, fps=True):
def reset_scene_resolution():
"""Apply the scene resolution from the project definition
scene resolution can be overwritten by an folder if the folder.attrib
contains any information regarding scene resolution .
The scene resolution will be retrieved from the current task entity's
attributes.
Returns:
None
"""
task_attributes = get_current_task_entity(fields={"attrib"})["attrib"]
# Set resolution
width = task_attributes.get("resolutionWidth", 1920)
height = task_attributes.get("resolutionHeight", 1080)
@ -3128,7 +3129,7 @@ def load_capture_preset(data):
return options
def get_attr_in_layer(attr, layer):
def get_attr_in_layer(attr, layer, as_string=True):
"""Return attribute value in specified renderlayer.
Same as cmds.getAttr but this gets the attribute's value in a
@ -3146,6 +3147,7 @@ def get_attr_in_layer(attr, layer):
Args:
attr (str): attribute name, ex. "node.attribute"
layer (str): layer name
as_string (bool): whether attribute should convert to a string value
Returns:
The return value from `maya.cmds.getAttr`
@ -3155,7 +3157,8 @@ def get_attr_in_layer(attr, layer):
try:
if cmds.mayaHasRenderSetup():
from . import lib_rendersetup
return lib_rendersetup.get_attr_in_layer(attr, layer)
return lib_rendersetup.get_attr_in_layer(
attr, layer, as_string=as_string)
except AttributeError:
pass
@ -3163,7 +3166,7 @@ def get_attr_in_layer(attr, layer):
current_layer = cmds.editRenderLayerGlobals(query=True,
currentRenderLayer=True)
if layer == current_layer:
return cmds.getAttr(attr)
return cmds.getAttr(attr, asString=as_string)
connections = cmds.listConnections(attr,
plugs=True,
@ -3214,7 +3217,7 @@ def get_attr_in_layer(attr, layer):
value *= conversion
return value
return cmds.getAttr(attr)
return cmds.getAttr(attr, asString=as_string)
def fix_incompatible_containers():
@ -3243,33 +3246,46 @@ def update_content_on_context_change():
"""
This will update scene content to match new folder on context change
"""
scene_sets = cmds.listSets(allSets=True)
folder_entity = get_current_folder_entity()
folder_attributes = folder_entity["attrib"]
new_folder_path = folder_entity["path"]
for s in scene_sets:
try:
if cmds.getAttr("{}.id".format(s)) in {
AYON_INSTANCE_ID, AVALON_INSTANCE_ID
}:
attr = cmds.listAttr(s)
print(s)
if "folderPath" in attr:
print(
" - setting folder to: [ {} ]".format(new_folder_path)
)
cmds.setAttr(
"{}.folderPath".format(s),
new_folder_path, type="string"
)
if "frameStart" in attr:
cmds.setAttr("{}.frameStart".format(s),
folder_attributes["frameStart"])
if "frameEnd" in attr:
cmds.setAttr("{}.frameEnd".format(s),
folder_attributes["frameEnd"],)
except ValueError:
pass
host = registered_host()
create_context = CreateContext(host)
folder_entity = get_current_task_entity(fields={"attrib"})
instance_values = {
"folderPath": create_context.get_current_folder_path(),
"task": create_context.get_current_task_name(),
}
creator_attribute_values = {
"frameStart": folder_entity["attrib"]["frameStart"],
"frameEnd": folder_entity["attrib"]["frameEnd"],
}
has_changes = False
for instance in create_context.instances:
for key, value in instance_values.items():
if key not in instance or instance[key] == value:
continue
# Update instance value
print(f"Updating {instance.product_name} {key} to: {value}")
instance[key] = value
has_changes = True
creator_attributes = instance.creator_attributes
for key, value in creator_attribute_values.items():
if (
key not in creator_attributes
or creator_attributes[key] == value
):
continue
# Update instance creator attribute value
print(f"Updating {instance.product_name} {key} to: {value}")
instance[key] = value
has_changes = True
if has_changes:
create_context.save_changes()
def show_message(title, msg):
@ -4003,17 +4019,26 @@ def len_flattened(components):
return n
def get_all_children(nodes):
def get_all_children(nodes, ignore_intermediate_objects=False):
"""Return all children of `nodes` including each instanced child.
Using maya.cmds.listRelatives(allDescendents=True) includes only the first
instance. As such, this function acts as an optimal replacement with a
focus on a fast query.
Args:
nodes (iterable): List of nodes to get children for.
ignore_intermediate_objects (bool): Ignore any children that
are intermediate objects.
Returns:
set: Children of input nodes.
"""
sel = OpenMaya.MSelectionList()
traversed = set()
iterator = OpenMaya.MItDag(OpenMaya.MItDag.kDepthFirst)
fn_dag = OpenMaya.MFnDagNode()
for node in nodes:
if node in traversed:
@ -4030,6 +4055,13 @@ def get_all_children(nodes):
iterator.next() # noqa: B305
while not iterator.isDone():
if ignore_intermediate_objects:
fn_dag.setObject(iterator.currentItem())
if fn_dag.isIntermediateObject:
iterator.prune()
iterator.next() # noqa: B305
continue
path = iterator.fullPathName()
if path in traversed:
@ -4040,7 +4072,7 @@ def get_all_children(nodes):
traversed.add(path)
iterator.next() # noqa: B305
return list(traversed)
return traversed
def get_capture_preset(

View file

@ -297,7 +297,7 @@ class ARenderProducts:
"""
return self._get_attr("defaultRenderGlobals", attribute)
def _get_attr(self, node_attr, attribute=None):
def _get_attr(self, node_attr, attribute=None, as_string=True):
"""Return the value of the attribute in the renderlayer
For readability this allows passing in the attribute in two ways.
@ -317,7 +317,7 @@ class ARenderProducts:
else:
plug = "{}.{}".format(node_attr, attribute)
return lib.get_attr_in_layer(plug, layer=self.layer)
return lib.get_attr_in_layer(plug, layer=self.layer, as_string=as_string)
@staticmethod
def extract_separator(file_prefix):
@ -1133,9 +1133,24 @@ class RenderProductsRedshift(ARenderProducts):
aovs = list(set(aovs) - set(ref_aovs))
products = []
global_aov_enabled = bool(
self._get_attr("redshiftOptions.aovGlobalEnableMode", as_string=False)
)
colorspace = lib.get_color_management_output_transform()
if not global_aov_enabled:
# only beauty output
for camera in cameras:
products.insert(0,
RenderProduct(productName="",
ext=ext,
multipart=self.multipart,
camera=camera,
colorspace=colorspace))
return products
light_groups_enabled = False
has_beauty_aov = False
colorspace = lib.get_color_management_output_transform()
for aov in aovs:
enabled = self._get_attr(aov, "enabled")
if not enabled:

View file

@ -77,7 +77,7 @@ def get_rendersetup_layer(layer):
if conn.endswith(".legacyRenderLayer")), None)
def get_attr_in_layer(node_attr, layer):
def get_attr_in_layer(node_attr, layer, as_string=True):
"""Return attribute value in Render Setup layer.
This will only work for attributes which can be
@ -124,7 +124,7 @@ def get_attr_in_layer(node_attr, layer):
node = history_overrides[-1] if history_overrides else override
node_attr_ = node + ".original"
return get_attribute(node_attr_, asString=True)
return get_attribute(node_attr_, asString=as_string)
layer = get_rendersetup_layer(layer)
rs = renderSetup.instance()
@ -144,7 +144,7 @@ def get_attr_in_layer(node_attr, layer):
# we will let it error out.
rs.switchToLayer(current_layer)
return get_attribute(node_attr, asString=True)
return get_attribute(node_attr, asString=as_string)
overrides = get_attr_overrides(node_attr, layer)
default_layer_value = get_default_layer_value(node_attr)

View file

@ -40,8 +40,15 @@ class CreateRenderlayer(plugin.RenderlayerCreator):
def create(self, product_name, instance_data, pre_create_data):
# Only allow a single render instance to exist
if self._get_singleton_node():
raise CreatorError("A Render instance already exists - only "
"one can be configured.")
raise CreatorError(
"A Render instance already exists - only one can be "
"configured.\n\n"
"To render multiple render layers, create extra Render Setup "
"Layers via Maya's Render Setup UI.\n"
"Then refresh the publisher to detect the new layers for "
"rendering.\n\n"
"With a render instance present all Render Setup layers in "
"your workfile are renderable instances.")
# Apply default project render settings on create
if self.render_settings.get("apply_render_settings"):

View file

@ -9,7 +9,9 @@ instance.
import json
import sys
import six
import contextlib
from ayon_core.lib import BoolDef, EnumDef
from ayon_core.pipeline import (
load,
get_representation_path
@ -21,6 +23,31 @@ from maya import cmds
import maya.app.renderSetup.model.renderSetup as renderSetup
@contextlib.contextmanager
def mark_all_imported(enabled):
"""Mark all imported nodes accepted by removing the `imported` attribute"""
if not enabled:
yield
return
node_types = cmds.pluginInfo("renderSetup", query=True, dependNode=True)
# Get node before load, then we can disable `imported`
# attribute on all new render setup layers after import
before = cmds.ls(type=node_types, long=True)
try:
yield
finally:
after = cmds.ls(type=node_types, long=True)
for node in (node for node in after if node not in before):
if cmds.attributeQuery("imported",
node=node,
exists=True):
plug = "{}.imported".format(node)
if cmds.getAttr(plug):
cmds.deleteAttr(plug)
class RenderSetupLoader(load.LoaderPlugin):
"""Load json preset for RenderSetup overwriting current one."""
@ -32,48 +59,79 @@ class RenderSetupLoader(load.LoaderPlugin):
icon = "tablet"
color = "orange"
options = [
BoolDef("accept_import",
label="Accept import on load",
tooltip=(
"By default importing or pasting Render Setup collections "
"will display them italic in the Render Setup list.\nWith "
"this enabled the load will directly mark the import "
"'accepted' and remove the italic view."
),
default=True),
BoolDef("load_managed",
label="Load Managed",
tooltip=(
"Containerize the rendersetup on load so it can be "
"'updated' later."
),
default=True),
EnumDef("import_mode",
label="Import mode",
items={
renderSetup.DECODE_AND_OVERWRITE: (
"Flush existing render setup and "
"add without any namespace"
),
renderSetup.DECODE_AND_MERGE: (
"Merge with the existing render setup objects and "
"rename the unexpected objects"
),
renderSetup.DECODE_AND_RENAME: (
"Renaming all decoded render setup objects to not "
"conflict with the existing render setup"
),
},
default=renderSetup.DECODE_AND_OVERWRITE)
]
def load(self, context, name, namespace, data):
"""Load RenderSetup settings."""
# from ayon_core.hosts.maya.api.lib import namespaced
folder_name = context["folder"]["name"]
namespace = namespace or lib.unique_namespace(
folder_name + "_",
prefix="_" if folder_name[0].isdigit() else "",
suffix="_",
)
path = self.filepath_from_context(context)
accept_import = data.get("accept_import", True)
import_mode = data.get("import_mode", renderSetup.DECODE_AND_OVERWRITE)
self.log.info(">>> loading json [ {} ]".format(path))
with open(path, "r") as file:
renderSetup.instance().decode(
json.load(file), renderSetup.DECODE_AND_OVERWRITE, None)
with mark_all_imported(accept_import):
with open(path, "r") as file:
renderSetup.instance().decode(
json.load(file), import_mode, None)
nodes = []
null = cmds.sets(name="null_SET", empty=True)
nodes.append(null)
if data.get("load_managed", True):
self.log.info(">>> containerising [ {} ]".format(name))
folder_name = context["folder"]["name"]
namespace = namespace or lib.unique_namespace(
folder_name + "_",
prefix="_" if folder_name[0].isdigit() else "",
suffix="_",
)
self[:] = nodes
if not nodes:
return
self.log.info(">>> containerising [ {} ]".format(name))
return containerise(
name=name,
namespace=namespace,
nodes=nodes,
context=context,
loader=self.__class__.__name__)
return containerise(
name=name,
namespace=namespace,
nodes=[],
context=context,
loader=self.__class__.__name__)
def remove(self, container):
"""Remove RenderSetup settings instance."""
from maya import cmds
container_name = container["objectName"]
self.log.info("Removing '%s' from Maya.." % container["name"])
container_content = cmds.sets(container_name, query=True)
container_content = cmds.sets(container_name, query=True) or []
nodes = cmds.ls(container_content, long=True)
nodes.append(container_name)

View file

@ -46,11 +46,18 @@ class CollectArnoldSceneSource(pyblish.api.InstancePlugin):
self.log.debug("data: {}".format(instance.data))
def get_hierarchy(self, nodes):
"""Return nodes with all their children"""
"""Return nodes with all their children.
Arguments:
nodes (List[str]): List of nodes to collect children hierarchy for
Returns:
list: Input nodes with their children hierarchy
"""
nodes = cmds.ls(nodes, long=True)
if not nodes:
return []
children = get_all_children(nodes)
# Make sure nodes merged with children only
# contains unique entries
return list(set(nodes + children))
children = get_all_children(nodes, ignore_intermediate_objects=True)
return list(children.union(nodes))

View file

@ -48,15 +48,15 @@ class CollectNewInstances(pyblish.api.InstancePlugin):
# Collect members
members = cmds.ls(members, long=True) or []
# Collect full hierarchy
dag_members = cmds.ls(members, type="dagNode", long=True)
children = get_all_children(dag_members)
children = cmds.ls(children, noIntermediate=True, long=True)
parents = (
self.get_all_parents(members)
if creator_attributes.get("includeParentHierarchy", True)
else []
)
members_hierarchy = list(set(members + children + parents))
children = get_all_children(dag_members,
ignore_intermediate_objects=True)
members_hierarchy = set(members)
members_hierarchy.update(children)
if creator_attributes.get("includeParentHierarchy", True):
members_hierarchy.update(self.get_all_parents(dag_members))
instance[:] = members_hierarchy
@ -97,16 +97,16 @@ class CollectNewInstances(pyblish.api.InstancePlugin):
"""Get all parents by using string operations (optimization)
Args:
nodes (list): the nodes which are found in the objectSet
nodes (iterable): the nodes which are found in the objectSet
Returns:
list
set
"""
parents = []
parents = set()
for node in nodes:
splitted = node.split("|")
items = ["|".join(splitted[0:i]) for i in range(2, len(splitted))]
parents.extend(items)
parents.update(items)
return list(set(parents))
return parents

View file

@ -49,8 +49,9 @@ def get_selected_nodes():
"""Get information from current selection"""
selection = cmds.ls(selection=True, long=True)
hierarchy = lib.get_all_children(selection)
return list(set(selection + hierarchy))
hierarchy = lib.get_all_children(selection,
ignore_intermediate_objects=True)
return list(hierarchy.union(selection))
def get_all_asset_nodes():

View file

@ -2627,11 +2627,11 @@ class NukeDirmap(HostDirmap):
class DirmapCache:
"""Caching class to get settings and sync_module easily and only once."""
"""Caching class to get settings and sitesync easily and only once."""
_project_name = None
_project_settings = None
_sync_module_discovered = False
_sync_module = None
_sitesync_addon_discovered = False
_sitesync_addon = None
_mapping = None
@classmethod
@ -2647,11 +2647,11 @@ class DirmapCache:
return cls._project_settings
@classmethod
def sync_module(cls):
if not cls._sync_module_discovered:
cls._sync_module_discovered = True
cls._sync_module = AddonsManager().get("sync_server")
return cls._sync_module
def sitesync_addon(cls):
if not cls._sitesync_addon_discovered:
cls._sitesync_addon_discovered = True
cls._sitesync_addon = AddonsManager().get("sitesync")
return cls._sitesync_addon
@classmethod
def mapping(cls):
@ -2673,7 +2673,7 @@ def dirmap_file_name_filter(file_name):
"nuke",
DirmapCache.project_name(),
DirmapCache.project_settings(),
DirmapCache.sync_module(),
DirmapCache.sitesync_addon(),
)
if not DirmapCache.mapping():
DirmapCache.set_mapping(dirmap_processor.get_mappings())

View file

@ -447,7 +447,7 @@ class CacheItem:
class Anatomy(BaseAnatomy):
_sync_server_addon_cache = CacheItem()
_sitesync_addon_cache = CacheItem()
_project_cache = collections.defaultdict(CacheItem)
_default_site_id_cache = collections.defaultdict(CacheItem)
_root_overrides_cache = collections.defaultdict(
@ -482,13 +482,13 @@ class Anatomy(BaseAnatomy):
return copy.deepcopy(project_cache.data)
@classmethod
def get_sync_server_addon(cls):
if cls._sync_server_addon_cache.is_outdated:
def get_sitesync_addon(cls):
if cls._sitesync_addon_cache.is_outdated:
manager = AddonsManager()
cls._sync_server_addon_cache.update_data(
manager.get_enabled_addon("sync_server")
cls._sitesync_addon_cache.update_data(
manager.get_enabled_addon("sitesync")
)
return cls._sync_server_addon_cache.data
return cls._sitesync_addon_cache.data
@classmethod
def _get_studio_roots_overrides(cls, project_name):
@ -525,8 +525,8 @@ class Anatomy(BaseAnatomy):
"""
# First check if sync server is available and enabled
sync_server = cls.get_sync_server_addon()
if sync_server is None or not sync_server.enabled:
sitesync_addon = cls.get_sitesync_addon()
if sitesync_addon is None or not sitesync_addon.enabled:
# QUESTION is ok to force 'studio' when site sync is not enabled?
site_name = "studio"
@ -535,7 +535,7 @@ class Anatomy(BaseAnatomy):
project_cache = cls._default_site_id_cache[project_name]
if project_cache.is_outdated:
project_cache.update_data(
sync_server.get_active_site_type(project_name)
sitesync_addon.get_active_site_type(project_name)
)
site_name = project_cache.data
@ -549,7 +549,7 @@ class Anatomy(BaseAnatomy):
)
else:
# Ask sync server to get roots overrides
roots_overrides = sync_server.get_site_root_overrides(
roots_overrides = sitesync.get_site_root_overrides(
project_name, site_name
)
site_cache.update_data(roots_overrides)

View file

@ -1,5 +1,5 @@
from pyblish import api
from ayon_core.settings import get_current_project_settings
from ayon_core.settings import get_project_settings
class CollectSettings(api.ContextPlugin):
@ -9,4 +9,9 @@ class CollectSettings(api.ContextPlugin):
label = "Collect Settings"
def process(self, context):
context.data["project_settings"] = get_current_project_settings()
project_name = context.data["projectName"]
self.log.debug(
"Collecting settings for project: {}".format(project_name)
)
project_settings = get_project_settings(project_name)
context.data["project_settings"] = project_settings

View file

@ -27,7 +27,7 @@ class ExtractBurnin(publish.Extractor):
Extractor to create video with pre-defined burnins from
existing extracted video representation.
It will work only on represenations having `burnin = True` or
It will work only on representations having `burnin = True` or
`tags` including `burnin`
"""
@ -125,7 +125,7 @@ class ExtractBurnin(publish.Extractor):
burnin_defs = copy.deepcopy(src_burnin_defs)
# Filter output definition by `burnin` represetation key
# Filter output definition by `burnin` representation key
repre_linked_burnins = [
burnin_def
for burnin_def in burnin_defs
@ -378,6 +378,7 @@ class ExtractBurnin(publish.Extractor):
# Prepare subprocess arguments
args = list(executable_args)
args.append(temporary_json_filepath)
args.append("--headless")
self.log.debug("Executing: {}".format(" ".join(args)))
# Run burnin script
@ -547,7 +548,7 @@ class ExtractBurnin(publish.Extractor):
return burnin_data, temp_data
def repres_is_valid(self, repre):
"""Validation if representaion should be processed.
"""Validation if representation should be processed.
Args:
repre (dict): Representation which should be checked.
@ -579,7 +580,7 @@ class ExtractBurnin(publish.Extractor):
tags (list): Tags of processed representation.
Returns:
list: Containg all burnin definitions matching entered tags.
list: Contain all burnin definitions matching entered tags.
"""
filtered_burnins = []
@ -604,7 +605,7 @@ class ExtractBurnin(publish.Extractor):
Store data to `temp_data` for keys "full_input_path" which is full path
to source files optionally with sequence formatting,
"full_output_path" full path to otput with optionally with sequence
"full_output_path" full path to output with optionally with sequence
formatting, "full_input_paths" list of all source files which will be
deleted when burnin script ends, "repre_files" list of output
filenames.
@ -754,7 +755,7 @@ class ExtractBurnin(publish.Extractor):
profile (dict): Profile from presets matching current context.
Returns:
list: Containg all valid output definitions.
list: Contain all valid output definitions.
"""
filtered_burnin_defs = []
@ -775,7 +776,7 @@ class ExtractBurnin(publish.Extractor):
):
self.log.debug((
"Skipped burnin definition \"{}\". Family"
" fiters ({}) does not match current instance families: {}"
" filters ({}) does not match current instance families: {}"
).format(
filename_suffix, str(families_filters), str(families)
))

View file

@ -871,7 +871,7 @@ class FrontendLoaderController(_BaseLoaderController):
# Site sync functions
@abstractmethod
def is_site_sync_enabled(self, project_name=None):
def is_sitesync_enabled(self, project_name=None):
"""Is site sync enabled.
Site sync addon can be enabled but can be disabled per project.

View file

@ -113,7 +113,7 @@ class LoaderController(BackendLoaderController, FrontendLoaderController):
self._products_model = ProductsModel(self)
self._loader_actions_model = LoaderActionsModel(self)
self._thumbnails_model = ThumbnailsModel()
self._site_sync_model = SiteSyncModel(self)
self._sitesync_model = SiteSyncModel(self)
@property
def log(self):
@ -149,7 +149,7 @@ class LoaderController(BackendLoaderController, FrontendLoaderController):
self._loader_actions_model.reset()
self._projects_model.reset()
self._thumbnails_model.reset()
self._site_sync_model.reset()
self._sitesync_model.reset()
self._projects_model.refresh()
@ -240,7 +240,7 @@ class LoaderController(BackendLoaderController, FrontendLoaderController):
project_name, representation_ids)
)
action_items.extend(self._site_sync_model.get_site_sync_action_items(
action_items.extend(self._sitesync_model.get_sitesync_action_items(
project_name, representation_ids)
)
@ -254,8 +254,8 @@ class LoaderController(BackendLoaderController, FrontendLoaderController):
version_ids,
representation_ids
):
if self._site_sync_model.is_site_sync_action(identifier):
self._site_sync_model.trigger_action_item(
if self._sitesync_model.is_sitesync_action(identifier):
self._sitesync_model.trigger_action_item(
identifier,
project_name,
representation_ids
@ -368,24 +368,24 @@ class LoaderController(BackendLoaderController, FrontendLoaderController):
self._loaded_products_cache.update_data(product_ids)
return self._loaded_products_cache.get_data()
def is_site_sync_enabled(self, project_name=None):
return self._site_sync_model.is_site_sync_enabled(project_name)
def is_sitesync_enabled(self, project_name=None):
return self._sitesync_model.is_sitesync_enabled(project_name)
def get_active_site_icon_def(self, project_name):
return self._site_sync_model.get_active_site_icon_def(project_name)
return self._sitesync_model.get_active_site_icon_def(project_name)
def get_remote_site_icon_def(self, project_name):
return self._site_sync_model.get_remote_site_icon_def(project_name)
return self._sitesync_model.get_remote_site_icon_def(project_name)
def get_version_sync_availability(self, project_name, version_ids):
return self._site_sync_model.get_version_sync_availability(
return self._sitesync_model.get_version_sync_availability(
project_name, version_ids
)
def get_representations_sync_status(
self, project_name, representation_ids
):
return self._site_sync_model.get_representations_sync_status(
return self._sitesync_model.get_representations_sync_status(
project_name, representation_ids
)

View file

@ -1,7 +1,7 @@
from .selection import SelectionModel
from .products import ProductsModel
from .actions import LoaderActionsModel
from .site_sync import SiteSyncModel
from .sitesync import SiteSyncModel
__all__ = (

View file

@ -36,7 +36,7 @@ class SiteSyncModel:
self._controller = controller
self._site_icons = None
self._site_sync_enabled_cache = NestedCacheItem(
self._sitesync_enabled_cache = NestedCacheItem(
levels=1, lifetime=self.lifetime
)
self._active_site_cache = NestedCacheItem(
@ -57,17 +57,17 @@ class SiteSyncModel:
)
manager = AddonsManager()
self._site_sync_addon = manager.get("sync_server")
self._sitesync_addon = manager.get("sitesync")
def reset(self):
self._site_icons = None
self._site_sync_enabled_cache.reset()
self._sitesync_enabled_cache.reset()
self._active_site_cache.reset()
self._remote_site_cache.reset()
self._version_availability_cache.reset()
self._repre_status_cache.reset()
def is_site_sync_enabled(self, project_name=None):
def is_sitesync_enabled(self, project_name=None):
"""Site sync is enabled for a project.
Returns false if site sync addon is not available or enabled
@ -82,13 +82,13 @@ class SiteSyncModel:
bool: Site sync is enabled.
"""
if not self._is_site_sync_addon_enabled():
if not self._is_sitesync_addon_enabled():
return False
cache = self._site_sync_enabled_cache[project_name]
cache = self._sitesync_enabled_cache[project_name]
if not cache.is_valid:
enabled = True
if project_name:
enabled = self._site_sync_addon.is_project_enabled(
enabled = self._sitesync_addon.is_project_enabled(
project_name, single=True
)
cache.update_data(enabled)
@ -107,8 +107,8 @@ class SiteSyncModel:
cache = self._active_site_cache[project_name]
if not cache.is_valid:
site_name = None
if project_name and self._is_site_sync_addon_enabled():
site_name = self._site_sync_addon.get_active_site(project_name)
if project_name and self._is_sitesync_addon_enabled():
site_name = self._sitesync_addon.get_active_site(project_name)
cache.update_data(site_name)
return cache.get_data()
@ -125,8 +125,8 @@ class SiteSyncModel:
cache = self._remote_site_cache[project_name]
if not cache.is_valid:
site_name = None
if project_name and self._is_site_sync_addon_enabled():
site_name = self._site_sync_addon.get_remote_site(project_name)
if project_name and self._is_sitesync_addon_enabled():
site_name = self._sitesync_addon.get_remote_site(project_name)
cache.update_data(site_name)
return cache.get_data()
@ -140,7 +140,7 @@ class SiteSyncModel:
Union[dict[str, Any], None]: Site icon definition.
"""
if not project_name or not self.is_site_sync_enabled(project_name):
if not project_name or not self.is_sitesync_enabled(project_name):
return None
active_site = self.get_active_site(project_name)
return self._get_site_icon_def(project_name, active_site)
@ -155,14 +155,14 @@ class SiteSyncModel:
Union[dict[str, Any], None]: Site icon definition.
"""
if not project_name or not self.is_site_sync_enabled(project_name):
if not project_name or not self.is_sitesync_enabled(project_name):
return None
remote_site = self.get_remote_site(project_name)
return self._get_site_icon_def(project_name, remote_site)
def _get_site_icon_def(self, project_name, site_name):
# use different icon for studio even if provider is 'local_drive'
if site_name == self._site_sync_addon.DEFAULT_SITE:
if site_name == self._sitesync_addon.DEFAULT_SITE:
provider = "studio"
else:
provider = self._get_provider_for_site(project_name, site_name)
@ -179,7 +179,7 @@ class SiteSyncModel:
dict[str, tuple[int, int]]
"""
if not self.is_site_sync_enabled(project_name):
if not self.is_sitesync_enabled(project_name):
return {
version_id: _default_version_availability()
for version_id in version_ids
@ -217,7 +217,7 @@ class SiteSyncModel:
dict[str, tuple[float, float]]
"""
if not self.is_site_sync_enabled(project_name):
if not self.is_sitesync_enabled(project_name):
return {
repre_id: _default_repre_status()
for repre_id in representation_ids
@ -242,7 +242,7 @@ class SiteSyncModel:
output[repre_id] = repre_cache.get_data()
return output
def get_site_sync_action_items(self, project_name, representation_ids):
def get_sitesync_action_items(self, project_name, representation_ids):
"""
Args:
@ -253,7 +253,7 @@ class SiteSyncModel:
list[ActionItem]: Actions that can be shown in loader.
"""
if not self.is_site_sync_enabled(project_name):
if not self.is_sitesync_enabled(project_name):
return []
repres_status = self.get_representations_sync_status(
@ -289,7 +289,7 @@ class SiteSyncModel:
return action_items
def is_site_sync_action(self, identifier):
def is_sitesync_action(self, identifier):
"""Should be `identifier` handled by SiteSync.
Args:
@ -353,22 +353,22 @@ class SiteSyncModel:
)
elif identifier == REMOVE_IDENTIFIER:
self._site_sync_addon.remove_site(
self._sitesync_addon.remove_site(
project_name,
repre_id,
active_site,
remove_local_files=True
)
def _is_site_sync_addon_enabled(self):
def _is_sitesync_addon_enabled(self):
"""
Returns:
bool: Site sync addon is enabled.
"""
if self._site_sync_addon is None:
if self._sitesync_addon is None:
return False
return self._site_sync_addon.enabled
return self._sitesync_addon.enabled
def _get_provider_for_site(self, project_name, site_name):
"""Provider for a site.
@ -381,9 +381,9 @@ class SiteSyncModel:
Union[str, None]: Provider name.
"""
if not self._is_site_sync_addon_enabled():
if not self._is_sitesync_addon_enabled():
return None
return self._site_sync_addon.get_provider_for_site(
return self._sitesync_addon.get_provider_for_site(
project_name, site_name
)
@ -398,7 +398,7 @@ class SiteSyncModel:
return None
if self._site_icons is None:
self._site_icons = self._site_sync_addon.get_site_icons()
self._site_icons = self._sitesync_addon.get_site_icons()
return self._site_icons.get(provider)
def _refresh_version_availability(self, project_name, version_ids):
@ -406,7 +406,7 @@ class SiteSyncModel:
return
project_cache = self._version_availability_cache[project_name]
avail_by_id = self._site_sync_addon.get_version_availability(
avail_by_id = self._sitesync_addon.get_version_availability(
project_name,
version_ids,
self.get_active_site(project_name),
@ -425,7 +425,7 @@ class SiteSyncModel:
return
project_cache = self._repre_status_cache[project_name]
status_by_repre_id = (
self._site_sync_addon.get_representations_sync_state(
self._sitesync_addon.get_representations_sync_state(
project_name,
representation_ids,
self.get_active_site(project_name),
@ -496,7 +496,7 @@ class SiteSyncModel:
)
def _add_site(self, project_name, repre_entity, site_name, product_type):
self._site_sync_addon.add_site(
self._sitesync_addon.add_site(
project_name, repre_entity["id"], site_name, force=True
)
@ -513,7 +513,7 @@ class SiteSyncModel:
try:
print("Adding {} to linked representation: {}".format(
site_name, link_repre_id))
self._site_sync_addon.add_site(
self._sitesync_addon.add_site(
project_name,
link_repre_id,
site_name,

View file

@ -73,7 +73,7 @@ class ProductsModel(QtGui.QStandardItemModel):
published_time_col = column_labels.index("Time")
folders_label_col = column_labels.index("Folder")
in_scene_col = column_labels.index("In scene")
site_sync_avail_col = column_labels.index("Availability")
sitesync_avail_col = column_labels.index("Availability")
def __init__(self, controller):
super(ProductsModel, self).__init__()

View file

@ -139,9 +139,9 @@ class ProductsWidget(QtWidgets.QWidget):
products_view.setItemDelegateForColumn(
products_model.in_scene_col, in_scene_delegate)
site_sync_delegate = SiteSyncDelegate()
sitesync_delegate = SiteSyncDelegate()
products_view.setItemDelegateForColumn(
products_model.site_sync_avail_col, site_sync_delegate)
products_model.sitesync_avail_col, sitesync_delegate)
main_layout = QtWidgets.QHBoxLayout(self)
main_layout.setContentsMargins(0, 0, 0, 0)
@ -176,7 +176,7 @@ class ProductsWidget(QtWidgets.QWidget):
self._version_delegate = version_delegate
self._time_delegate = time_delegate
self._in_scene_delegate = in_scene_delegate
self._site_sync_delegate = site_sync_delegate
self._sitesync_delegate = sitesync_delegate
self._selected_project_name = None
self._selected_folder_ids = set()
@ -192,8 +192,8 @@ class ProductsWidget(QtWidgets.QWidget):
products_model.in_scene_col,
not controller.is_loaded_products_supported()
)
self._set_site_sync_visibility(
self._controller.is_site_sync_enabled()
self._set_sitesync_visibility(
self._controller.is_sitesync_enabled()
)
def set_name_filter(self, name):
@ -229,10 +229,10 @@ class ProductsWidget(QtWidgets.QWidget):
def refresh(self):
self._refresh_model()
def _set_site_sync_visibility(self, site_sync_enabled):
def _set_sitesync_visibility(self, sitesync_enabled):
self._products_view.setColumnHidden(
self._products_model.site_sync_avail_col,
not site_sync_enabled
self._products_model.sitesync_avail_col,
not sitesync_enabled
)
def _fill_version_editor(self):
@ -395,10 +395,10 @@ class ProductsWidget(QtWidgets.QWidget):
def _on_folders_selection_change(self, event):
project_name = event["project_name"]
site_sync_enabled = self._controller.is_site_sync_enabled(
sitesync_enabled = self._controller.is_sitesync_enabled(
project_name
)
self._set_site_sync_visibility(site_sync_enabled)
self._set_sitesync_visibility(sitesync_enabled)
self._selected_project_name = project_name
self._selected_folder_ids = event["folder_ids"]
self._refresh_model()

View file

@ -307,8 +307,8 @@ class RepresentationsWidget(QtWidgets.QWidget):
self._repre_model = repre_model
self._repre_proxy_model = repre_proxy_model
self._set_site_sync_visibility(
self._controller.is_site_sync_enabled()
self._set_sitesync_visibility(
self._controller.is_sitesync_enabled()
)
self._set_multiple_folders_selected(False)
@ -320,19 +320,19 @@ class RepresentationsWidget(QtWidgets.QWidget):
def _on_project_change(self, event):
self._selected_project_name = event["project_name"]
site_sync_enabled = self._controller.is_site_sync_enabled(
sitesync_enabled = self._controller.is_sitesync_enabled(
self._selected_project_name
)
self._set_site_sync_visibility(site_sync_enabled)
self._set_sitesync_visibility(sitesync_enabled)
def _set_site_sync_visibility(self, site_sync_enabled):
def _set_sitesync_visibility(self, sitesync_enabled):
self._repre_view.setColumnHidden(
self._repre_model.active_site_column,
not site_sync_enabled
not sitesync_enabled
)
self._repre_view.setColumnHidden(
self._repre_model.remote_site_column,
not site_sync_enabled
not sitesync_enabled
)
def _set_multiple_folders_selected(self, selected_multiple_folders):

View file

@ -28,7 +28,7 @@ class SceneInventoryController:
self._current_folder_id = None
self._current_folder_set = False
self._site_sync_model = SiteSyncModel(self)
self._sitesync_model = SiteSyncModel(self)
# Switch dialog requirements
self._hierarchy_model = HierarchyModel(self)
self._event_system = self._create_event_system()
@ -47,7 +47,7 @@ class SceneInventoryController:
self._current_folder_id = None
self._current_folder_set = False
self._site_sync_model.reset()
self._sitesync_model.reset()
self._hierarchy_model.reset()
def get_current_context(self):
@ -89,22 +89,22 @@ class SceneInventoryController:
return []
# Site Sync methods
def is_sync_server_enabled(self):
return self._site_sync_model.is_sync_server_enabled()
def is_sitesync_enabled(self):
return self._sitesync_model.is_sitesync_enabled()
def get_sites_information(self):
return self._site_sync_model.get_sites_information()
return self._sitesync_model.get_sites_information()
def get_site_provider_icons(self):
return self._site_sync_model.get_site_provider_icons()
return self._sitesync_model.get_site_provider_icons()
def get_representations_site_progress(self, representation_ids):
return self._site_sync_model.get_representations_site_progress(
return self._sitesync_model.get_representations_site_progress(
representation_ids
)
def resync_representations(self, representation_ids, site_type):
return self._site_sync_model.resync_representations(
return self._sitesync_model.resync_representations(
representation_ids, site_type
)

View file

@ -1,4 +1,4 @@
from .site_sync import SiteSyncModel
from .sitesync import SiteSyncModel
__all__ = (

View file

@ -9,30 +9,30 @@ class SiteSyncModel:
def __init__(self, controller):
self._controller = controller
self._sync_server_module = NOT_SET
self._sync_server_enabled = None
self._sitesync_addon = NOT_SET
self._sitesync_enabled = None
self._active_site = NOT_SET
self._remote_site = NOT_SET
self._active_site_provider = NOT_SET
self._remote_site_provider = NOT_SET
def reset(self):
self._sync_server_module = NOT_SET
self._sync_server_enabled = None
self._sitesync_addon = NOT_SET
self._sitesync_enabled = None
self._active_site = NOT_SET
self._remote_site = NOT_SET
self._active_site_provider = NOT_SET
self._remote_site_provider = NOT_SET
def is_sync_server_enabled(self):
def is_sitesync_enabled(self):
"""Site sync is enabled.
Returns:
bool: Is enabled or not.
"""
self._cache_sync_server_module()
return self._sync_server_enabled
self._cache_sitesync_addon()
return self._sitesync_enabled
def get_site_provider_icons(self):
"""Icon paths per provider.
@ -41,10 +41,10 @@ class SiteSyncModel:
dict[str, str]: Path by provider name.
"""
if not self.is_sync_server_enabled():
if not self.is_sitesync_enabled():
return {}
site_sync_addon = self._get_sync_server_module()
return site_sync_addon.get_site_icons()
sitesync_addon = self._get_sitesync_addon()
return sitesync_addon.get_site_icons()
def get_sites_information(self):
return {
@ -65,11 +65,11 @@ class SiteSyncModel:
}
for repre_id in representation_ids
}
if not self.is_sync_server_enabled():
if not self.is_sitesync_enabled():
return output
project_name = self._controller.get_current_project_name()
site_sync = self._get_sync_server_module()
sitesync_addon = self._get_sitesync_addon()
repre_entities = ayon_api.get_representations(
project_name, representation_ids
)
@ -78,7 +78,7 @@ class SiteSyncModel:
for repre_entity in repre_entities:
repre_output = output[repre_entity["id"]]
result = site_sync.get_progress_for_repre(
result = sitesync_addon.get_progress_for_repre(
repre_entity, active_site, remote_site
)
repre_output["active_site"] = result[active_site]
@ -95,7 +95,7 @@ class SiteSyncModel:
"""
project_name = self._controller.get_current_project_name()
site_sync = self._get_sync_server_module()
sitesync_addon = self._get_sitesync_addon()
active_site = self._get_active_site()
remote_site = self._get_remote_site()
progress = self.get_representations_site_progress(
@ -115,22 +115,22 @@ class SiteSyncModel:
site = remote_site
if check_progress == 1:
site_sync.add_site(
sitesync_addon.add_site(
project_name, repre_id, site, force=True
)
def _get_sync_server_module(self):
self._cache_sync_server_module()
return self._sync_server_module
def _get_sitesync_addon(self):
self._cache_sitesync_addon()
return self._sitesync_addon
def _cache_sync_server_module(self):
if self._sync_server_module is not NOT_SET:
return self._sync_server_module
def _cache_sitesync_addon(self):
if self._sitesync_addon is not NOT_SET:
return self._sitesync_addon
manager = AddonsManager()
site_sync = manager.get("sync_server")
sync_enabled = site_sync is not None and site_sync.enabled
self._sync_server_module = site_sync
self._sync_server_enabled = sync_enabled
sitesync_addon = manager.get("sitesync")
sync_enabled = sitesync_addon is not None and sitesync_addon.enabled
self._sitesync_addon = sitesync_addon
self._sitesync_enabled = sync_enabled
def _get_active_site(self):
if self._active_site is NOT_SET:
@ -157,19 +157,19 @@ class SiteSyncModel:
remote_site = None
active_site_provider = None
remote_site_provider = None
if self.is_sync_server_enabled():
site_sync = self._get_sync_server_module()
if self.is_sitesync_enabled():
sitesync_addon = self._get_sitesync_addon()
project_name = self._controller.get_current_project_name()
active_site = site_sync.get_active_site(project_name)
remote_site = site_sync.get_remote_site(project_name)
active_site = sitesync_addon.get_active_site(project_name)
remote_site = sitesync_addon.get_remote_site(project_name)
active_site_provider = "studio"
remote_site_provider = "studio"
if active_site != "studio":
active_site_provider = site_sync.get_provider_for_site(
active_site_provider = sitesync_addon.get_provider_for_site(
project_name, active_site
)
if remote_site != "studio":
remote_site_provider = site_sync.get_provider_for_site(
remote_site_provider = sitesync_addon.get_provider_for_site(
project_name, remote_site
)

View file

@ -311,9 +311,9 @@ class SceneInventoryView(QtWidgets.QTreeView):
menu.addAction(remove_action)
self._handle_sync_server(menu, repre_ids)
self._handle_sitesync(menu, repre_ids)
def _handle_sync_server(self, menu, repre_ids):
def _handle_sitesync(self, menu, repre_ids):
"""Adds actions for download/upload when SyncServer is enabled
Args:
@ -324,7 +324,7 @@ class SceneInventoryView(QtWidgets.QTreeView):
(OptionMenu)
"""
if not self._controller.is_sync_server_enabled():
if not self._controller.is_sitesync_enabled():
return
menu.addSeparator()

View file

@ -70,7 +70,7 @@ class SceneInventoryWindow(QtWidgets.QDialog):
view = SceneInventoryView(controller, self)
view.setModel(proxy)
sync_enabled = controller.is_sync_server_enabled()
sync_enabled = controller.is_sitesync_enabled()
view.setColumnHidden(model.active_site_col, not sync_enabled)
view.setColumnHidden(model.remote_site_col, not sync_enabled)

View file

@ -659,16 +659,7 @@ class BaseWorkfileController(
folder_id != self.get_current_folder_id()
or task_name != self.get_current_task_name()
):
folder_entity = ayon_api.get_folder_by_id(
event_data["project_name"],
event_data["folder_id"],
)
task_entity = ayon_api.get_task_by_name(
event_data["project_name"],
event_data["folder_id"],
event_data["task_name"]
)
change_current_context(folder_entity, task_entity)
self._change_current_context(project_name, folder_id, task_id)
self._host_open_workfile(filepath)
@ -710,16 +701,8 @@ class BaseWorkfileController(
folder_id != self.get_current_folder_id()
or task_name != self.get_current_task_name()
):
folder_entity = ayon_api.get_folder_by_id(
project_name, folder["id"]
)
task_entity = ayon_api.get_task_by_name(
project_name, folder["id"], task_name
)
change_current_context(
folder_entity,
task_entity,
template_key=template_key
self._change_current_context(
project_name, folder_id, task_id, template_key
)
# Save workfile
@ -744,4 +727,18 @@ class BaseWorkfileController(
# Trigger after save events
emit_event("workfile.save.after", event_data, source="workfiles.tool")
self.reset()
def _change_current_context(
self, project_name, folder_id, task_id, template_key=None
):
# Change current context
folder_entity = self.get_folder_entity(project_name, folder_id)
task_entity = self.get_task_entity(project_name, task_id)
change_current_context(
folder_entity,
task_entity,
template_key=template_key
)
self._current_folder_id = folder_entity["id"]
self._current_folder_path = folder_entity["path"]
self._current_task_name = task_entity["name"]

View file

@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
"""Package declaring AYON core addon version."""
__version__ = "0.3.0-dev.1"
__version__ = "0.3.1-dev.1"

View file

@ -1,6 +1,6 @@
name = "core"
title = "Core"
version = "0.3.0-dev.1"
version = "0.3.1-dev.1"
client_dir = "ayon_core"

View file

@ -5,7 +5,7 @@
[tool.poetry]
name = "ayon-core"
version = "0.3.0"
version = "0.3.1"
description = ""
authors = ["Ynput Team <team@ynput.io>"]
readme = "README.md"

View file

@ -3,6 +3,7 @@ import json
import copy
from ayon_server.addons import BaseServerAddon, AddonLibrary
from ayon_server.entities.core import attribute_library
from ayon_server.lib.postgres import Postgres
from .version import __version__
@ -118,9 +119,28 @@ class ApplicationsAddon(BaseServerAddon):
)
async def setup(self):
need_restart = await self.create_applications_attribute()
need_restart = await self.create_required_attributes()
if need_restart:
self.request_server_restart()
await self._update_enums()
def _get_applications_def(self):
return {
"name": "applications",
"type": "list_of_strings",
"title": "Applications",
"scope": ["project"],
"enum":[],
}
def _get_tools_def(self):
return {
"name": "tools",
"type": "list_of_strings",
"title": "Tools",
"scope": ["project", "folder", "task"],
"enum":[],
}
async def create_applications_attribute(self) -> bool:
"""Make sure there are required attributes which ftrack addon needs.
@ -129,6 +149,73 @@ class ApplicationsAddon(BaseServerAddon):
bool: 'True' if an attribute was created or updated.
"""
need_restart = await self.create_required_attributes()
await self._update_enums()
return need_restart
async def create_required_attributes(self) -> bool:
"""Make sure there are required 'applications' and 'tools' attributes.
This only checks for the existence of the attributes, it does not populate
them with any data. When an attribute is added, server needs to be restarted,
while adding enum data to the attribute does not require a restart.
Returns:
bool: 'True' if an attribute was created or updated.
"""
# keep track of the last attribute position (for adding new attributes)
apps_attribute_data = self._get_applications_def()
tools_attribute_data = self._get_tools_def()
apps_attrib_name = apps_attribute_data["name"]
tools_attrib_name = tools_attribute_data["name"]
async with Postgres.acquire() as conn, conn.transaction():
query = "SELECT BOOL_OR(name = 'applications') AS has_applications, BOOL_OR(name = 'tools') AS has_tools FROM attributes;"
result = (await conn.fetch(query))[0]
attributes_to_create = {}
if not result["has_applications"]:
attributes_to_create[apps_attrib_name] = {
"scope": apps_attribute_data["scope"],
"data": {
"title": apps_attribute_data["title"],
"type": apps_attribute_data["type"],
"enum": [],
}
}
if not result["has_tools"]:
attributes_to_create[tools_attrib_name] = {
"scope": tools_attribute_data["scope"],
"data": {
"title": tools_attribute_data["title"],
"type": tools_attribute_data["type"],
"enum": [],
},
}
needs_restart = False
# when any of the required attributes are not present, add them
# and return 'True' to indicate that server needs to be restarted
for name, payload in attributes_to_create.items():
insert_query = "INSERT INTO attributes (name, scope, data, position) VALUES ($1, $2, $3, (SELECT COALESCE(MAX(position), 0) + 1 FROM attributes)) ON CONFLICT DO NOTHING"
await conn.execute(
insert_query,
name,
payload["scope"],
payload["data"],
)
needs_restart = True
return needs_restart
async def _update_enums(self):
"""Updates applications and tools enums based on the addon settings.
This method is called when the addon is started (after we are sure that the
'applications' and 'tools' attributes exist) and when the addon settings are
updated (using on_settings_updated method).
"""
instance = AddonLibrary.getinstance()
app_defs = instance.data.get(self.name)
all_applications = []
@ -148,33 +235,32 @@ class ApplicationsAddon(BaseServerAddon):
merge_groups(all_applications, app_groups)
merge_groups(all_tools, studio_settings["tool_groups"])
query = "SELECT name, position, scope, data from public.attributes"
apps_attrib_name = "applications"
tools_attrib_name = "tools"
apps_enum = get_enum_items_from_groups(all_applications)
tools_enum = get_enum_items_from_groups(all_tools)
apps_attribute_data = {
"type": "list_of_strings",
"title": "Applications",
"enum": apps_enum
"enum": apps_enum,
}
tools_attribute_data = {
"type": "list_of_strings",
"title": "Tools",
"enum": tools_enum
"enum": tools_enum,
}
apps_scope = ["project"]
tools_scope = ["project", "folder", "task"]
apps_match_position = None
apps_matches = False
tools_match_position = None
tools_matches = False
position = 1
async for row in Postgres.iterate(query):
position += 1
async for row in Postgres.iterate(
"SELECT name, position, scope, data from public.attributes"
):
if row["name"] == apps_attrib_name:
# Check if scope is matching ftrack addon requirements
if (
@ -182,7 +268,6 @@ class ApplicationsAddon(BaseServerAddon):
and row["data"].get("enum") == apps_enum
):
apps_matches = True
apps_match_position = row["position"]
elif row["name"] == tools_attrib_name:
if (
@ -190,45 +275,41 @@ class ApplicationsAddon(BaseServerAddon):
and row["data"].get("enum") == tools_enum
):
tools_matches = True
tools_match_position = row["position"]
if apps_matches and tools_matches:
return False
return
postgre_query = "\n".join((
"INSERT INTO public.attributes",
" (name, position, scope, data)",
"VALUES",
" ($1, $2, $3, $4)",
"ON CONFLICT (name)",
"DO UPDATE SET",
" scope = $3,",
" data = $4",
))
if not apps_matches:
# Reuse position from found attribute
if apps_match_position is None:
apps_match_position = position
position += 1
await Postgres.execute(
postgre_query,
apps_attrib_name,
apps_match_position,
"""
UPDATE attributes SET
scope = $1,
data = $2
WHERE
name = $3
""",
apps_scope,
apps_attribute_data,
apps_attrib_name,
)
if not tools_matches:
if tools_match_position is None:
tools_match_position = position
position += 1
await Postgres.execute(
postgre_query,
tools_attrib_name,
tools_match_position,
"""
UPDATE attributes SET
scope = $1,
data = $2
WHERE
name = $3
""",
tools_scope,
tools_attribute_data,
tools_attrib_name,
)
return True
# Reset attributes cache on server
await attribute_library.load()
async def on_settings_changed(self, *args, **kwargs):
_ = args, kwargs
await self._update_enums()

View file

@ -1 +1 @@
__version__ = "0.1.8"
__version__ = "0.1.9"

View file

@ -75,6 +75,12 @@ class HooksModel(BaseSettingsModel):
default_factory=HookOptionalModel,
title="Install PySide2"
)
FusionLaunchMenuHook: HookOptionalModel = SettingsField(
default_factory=HookOptionalModel,
title="Launch AYON Menu on Fusion Start",
description="Launch the AYON menu on Fusion application startup. "
"This is only supported for Fusion 18+"
)
class CreateSaverModel(CreateSaverPluginModel):
@ -143,6 +149,9 @@ DEFAULT_VALUES = {
"hooks": {
"InstallPySideToFusion": {
"enabled": True
},
"FusionLaunchMenuHook": {
"enabled": False
}
},
"create": {

View file

@ -1 +1 @@
__version__ = "0.1.4"
__version__ = "0.1.5"