mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-25 05:14:40 +01:00
Merge pull request #1387 from pypeclub/feature/ftrack_settings_changes
Ftrack handling of save settings
This commit is contained in:
commit
162e11fece
18 changed files with 442 additions and 130 deletions
|
|
@ -263,14 +263,32 @@ class Application:
|
|||
|
||||
|
||||
class ApplicationManager:
|
||||
def __init__(self):
|
||||
self.log = PypeLogger().get_logger(self.__class__.__name__)
|
||||
"""Load applications and tools and store them by their full name.
|
||||
|
||||
Args:
|
||||
system_settings (dict): Preloaded system settings. When passed manager
|
||||
will always use these values. Gives ability to create manager
|
||||
using different settings.
|
||||
"""
|
||||
def __init__(self, system_settings=None):
|
||||
self.log = PypeLogger.get_logger(self.__class__.__name__)
|
||||
|
||||
self.app_groups = {}
|
||||
self.applications = {}
|
||||
self.tool_groups = {}
|
||||
self.tools = {}
|
||||
|
||||
self._system_settings = system_settings
|
||||
|
||||
self.refresh()
|
||||
|
||||
def set_system_settings(self, system_settings):
|
||||
"""Ability to change init system settings.
|
||||
|
||||
This will trigger refresh of manager.
|
||||
"""
|
||||
self._system_settings = system_settings
|
||||
|
||||
self.refresh()
|
||||
|
||||
def refresh(self):
|
||||
|
|
@ -280,9 +298,12 @@ class ApplicationManager:
|
|||
self.tool_groups.clear()
|
||||
self.tools.clear()
|
||||
|
||||
settings = get_system_settings(
|
||||
clear_metadata=False, exclude_locals=False
|
||||
)
|
||||
if self._system_settings is not None:
|
||||
settings = copy.deepcopy(self._system_settings)
|
||||
else:
|
||||
settings = get_system_settings(
|
||||
clear_metadata=False, exclude_locals=False
|
||||
)
|
||||
|
||||
app_defs = settings["applications"]
|
||||
for group_name, variant_defs in app_defs.items():
|
||||
|
|
|
|||
|
|
@ -123,6 +123,8 @@ class PypeFormatter(logging.Formatter):
|
|||
|
||||
if record.exc_info is not None:
|
||||
line_len = len(str(record.exc_info[1]))
|
||||
if line_len > 30:
|
||||
line_len = 30
|
||||
out = "{}\n{}\n{}\n{}\n{}".format(
|
||||
out,
|
||||
line_len * "=",
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ import json
|
|||
|
||||
from openpype.api import ProjectSettings
|
||||
|
||||
from openpype.modules.ftrack.lib import ServerAction
|
||||
from openpype.modules.ftrack.lib.avalon_sync import (
|
||||
get_pype_attr,
|
||||
from openpype.modules.ftrack.lib import (
|
||||
ServerAction,
|
||||
get_openpype_attr,
|
||||
CUST_ATTR_AUTO_SYNC
|
||||
)
|
||||
|
||||
|
|
@ -159,7 +159,7 @@ class PrepareProjectServer(ServerAction):
|
|||
for key, entity in project_anatom_settings["attributes"].items():
|
||||
attribute_values_by_key[key] = entity.value
|
||||
|
||||
cust_attrs, hier_cust_attrs = get_pype_attr(self.session, True)
|
||||
cust_attrs, hier_cust_attrs = get_openpype_attr(self.session, True)
|
||||
|
||||
for attr in hier_cust_attrs:
|
||||
key = attr["key"]
|
||||
|
|
|
|||
|
|
@ -18,12 +18,15 @@ from avalon import schema
|
|||
from avalon.api import AvalonMongoDB
|
||||
|
||||
from openpype.modules.ftrack.lib import (
|
||||
get_openpype_attr,
|
||||
CUST_ATTR_ID_KEY,
|
||||
CUST_ATTR_AUTO_SYNC,
|
||||
|
||||
avalon_sync,
|
||||
|
||||
BaseEvent
|
||||
)
|
||||
from openpype.modules.ftrack.lib.avalon_sync import (
|
||||
CUST_ATTR_ID_KEY,
|
||||
CUST_ATTR_AUTO_SYNC,
|
||||
EntitySchemas
|
||||
)
|
||||
|
||||
|
|
@ -125,7 +128,7 @@ class SyncToAvalonEvent(BaseEvent):
|
|||
@property
|
||||
def avalon_cust_attrs(self):
|
||||
if self._avalon_cust_attrs is None:
|
||||
self._avalon_cust_attrs = avalon_sync.get_pype_attr(
|
||||
self._avalon_cust_attrs = get_openpype_attr(
|
||||
self.process_session, query_keys=self.cust_attr_query_keys
|
||||
)
|
||||
return self._avalon_cust_attrs
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import collections
|
||||
import ftrack_api
|
||||
from openpype.modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype.modules.ftrack.lib.avalon_sync import get_pype_attr
|
||||
from openpype.modules.ftrack.lib import (
|
||||
BaseAction,
|
||||
statics_icon,
|
||||
get_openpype_attr
|
||||
)
|
||||
|
||||
|
||||
class CleanHierarchicalAttrsAction(BaseAction):
|
||||
|
|
@ -52,7 +55,7 @@ class CleanHierarchicalAttrsAction(BaseAction):
|
|||
)
|
||||
entity_ids_joined = ", ".join(all_entities_ids)
|
||||
|
||||
attrs, hier_attrs = get_pype_attr(session)
|
||||
attrs, hier_attrs = get_openpype_attr(session)
|
||||
|
||||
for attr in hier_attrs:
|
||||
configuration_key = attr["key"]
|
||||
|
|
|
|||
|
|
@ -2,10 +2,20 @@ import collections
|
|||
import json
|
||||
import arrow
|
||||
import ftrack_api
|
||||
from openpype.modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype.modules.ftrack.lib.avalon_sync import (
|
||||
CUST_ATTR_ID_KEY, CUST_ATTR_GROUP, default_custom_attributes_definition
|
||||
from openpype.modules.ftrack.lib import (
|
||||
BaseAction,
|
||||
statics_icon,
|
||||
|
||||
CUST_ATTR_ID_KEY,
|
||||
CUST_ATTR_GROUP,
|
||||
CUST_ATTR_TOOLS,
|
||||
CUST_ATTR_APPLICATIONS,
|
||||
|
||||
default_custom_attributes_definition,
|
||||
app_definitions_from_app_manager,
|
||||
tool_definitions_from_app_manager
|
||||
)
|
||||
|
||||
from openpype.api import get_system_settings
|
||||
from openpype.lib import ApplicationManager
|
||||
|
||||
|
|
@ -370,24 +380,12 @@ class CustomAttributes(BaseAction):
|
|||
exc_info=True
|
||||
)
|
||||
|
||||
def app_defs_from_app_manager(self):
|
||||
app_definitions = []
|
||||
for app_name, app in self.app_manager.applications.items():
|
||||
if app.enabled and app.is_host:
|
||||
app_definitions.append({
|
||||
app_name: app.full_label
|
||||
})
|
||||
|
||||
if not app_definitions:
|
||||
app_definitions.append({"empty": "< Empty >"})
|
||||
return app_definitions
|
||||
|
||||
def applications_attribute(self, event):
|
||||
apps_data = self.app_defs_from_app_manager()
|
||||
apps_data = app_definitions_from_app_manager(self.app_manager)
|
||||
|
||||
applications_custom_attr_data = {
|
||||
"label": "Applications",
|
||||
"key": "applications",
|
||||
"key": CUST_ATTR_APPLICATIONS,
|
||||
"type": "enumerator",
|
||||
"entity_type": "show",
|
||||
"group": CUST_ATTR_GROUP,
|
||||
|
|
@ -399,19 +397,11 @@ class CustomAttributes(BaseAction):
|
|||
self.process_attr_data(applications_custom_attr_data, event)
|
||||
|
||||
def tools_attribute(self, event):
|
||||
tools_data = []
|
||||
for tool_name, tool in self.app_manager.tools.items():
|
||||
tools_data.append({
|
||||
tool_name: tool.label
|
||||
})
|
||||
|
||||
# Make sure there is at least one item
|
||||
if not tools_data:
|
||||
tools_data.append({"empty": "< Empty >"})
|
||||
tools_data = tool_definitions_from_app_manager(self.app_manager)
|
||||
|
||||
tools_custom_attr_data = {
|
||||
"label": "Tools",
|
||||
"key": "tools_env",
|
||||
"key": CUST_ATTR_TOOLS,
|
||||
"type": "enumerator",
|
||||
"is_hierarchical": True,
|
||||
"group": CUST_ATTR_GROUP,
|
||||
|
|
|
|||
|
|
@ -4,10 +4,8 @@ from openpype.api import ProjectSettings
|
|||
|
||||
from openpype.modules.ftrack.lib import (
|
||||
BaseAction,
|
||||
statics_icon
|
||||
)
|
||||
from openpype.modules.ftrack.lib.avalon_sync import (
|
||||
get_pype_attr,
|
||||
statics_icon,
|
||||
get_openpype_attr,
|
||||
CUST_ATTR_AUTO_SYNC
|
||||
)
|
||||
|
||||
|
|
@ -162,7 +160,7 @@ class PrepareProjectLocal(BaseAction):
|
|||
for key, entity in project_anatom_settings["attributes"].items():
|
||||
attribute_values_by_key[key] = entity.value
|
||||
|
||||
cust_attrs, hier_cust_attrs = get_pype_attr(self.session, True)
|
||||
cust_attrs, hier_cust_attrs = get_openpype_attr(self.session, True)
|
||||
|
||||
for attr in hier_cust_attrs:
|
||||
key = attr["key"]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import os
|
||||
import json
|
||||
import collections
|
||||
from abc import ABCMeta, abstractmethod
|
||||
import six
|
||||
|
|
@ -11,6 +12,7 @@ from openpype.modules import (
|
|||
ILaunchHookPaths,
|
||||
ISettingsChangeListener
|
||||
)
|
||||
from openpype.settings import SaveWarningExc
|
||||
|
||||
FTRACK_MODULE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
|
@ -121,10 +123,86 @@ class FtrackModule(
|
|||
if self.tray_module:
|
||||
self.tray_module.stop_timer_manager()
|
||||
|
||||
def on_system_settings_save(self, *_args, **_kwargs):
|
||||
def on_system_settings_save(
|
||||
self, old_value, new_value, changes, new_value_metadata
|
||||
):
|
||||
"""Implementation of ISettingsChangeListener interface."""
|
||||
# Ignore
|
||||
return
|
||||
try:
|
||||
session = self.create_ftrack_session()
|
||||
except Exception:
|
||||
self.log.warning("Couldn't create ftrack session.", exc_info=True)
|
||||
raise SaveWarningExc((
|
||||
"Saving of attributes to ftrack wasn't successful,"
|
||||
" try running Create/Update Avalon Attributes in ftrack."
|
||||
))
|
||||
|
||||
from .lib import (
|
||||
get_openpype_attr,
|
||||
CUST_ATTR_APPLICATIONS,
|
||||
CUST_ATTR_TOOLS,
|
||||
app_definitions_from_app_manager,
|
||||
tool_definitions_from_app_manager
|
||||
)
|
||||
from openpype.api import ApplicationManager
|
||||
query_keys = [
|
||||
"id",
|
||||
"key",
|
||||
"config"
|
||||
]
|
||||
custom_attributes = get_openpype_attr(
|
||||
session,
|
||||
split_hierarchical=False,
|
||||
query_keys=query_keys
|
||||
)
|
||||
app_attribute = None
|
||||
tool_attribute = None
|
||||
for custom_attribute in custom_attributes:
|
||||
key = custom_attribute["key"]
|
||||
if key == CUST_ATTR_APPLICATIONS:
|
||||
app_attribute = custom_attribute
|
||||
elif key == CUST_ATTR_TOOLS:
|
||||
tool_attribute = custom_attribute
|
||||
|
||||
app_manager = ApplicationManager(new_value_metadata)
|
||||
missing_attributes = []
|
||||
if not app_attribute:
|
||||
missing_attributes.append(CUST_ATTR_APPLICATIONS)
|
||||
else:
|
||||
config = json.loads(app_attribute["config"])
|
||||
new_data = app_definitions_from_app_manager(app_manager)
|
||||
prepared_data = []
|
||||
for item in new_data:
|
||||
for key, label in item.items():
|
||||
prepared_data.append({
|
||||
"menu": label,
|
||||
"value": key
|
||||
})
|
||||
|
||||
config["data"] = json.dumps(prepared_data)
|
||||
app_attribute["config"] = json.dumps(config)
|
||||
|
||||
if not tool_attribute:
|
||||
missing_attributes.append(CUST_ATTR_TOOLS)
|
||||
else:
|
||||
config = json.loads(tool_attribute["config"])
|
||||
new_data = tool_definitions_from_app_manager(app_manager)
|
||||
prepared_data = []
|
||||
for item in new_data:
|
||||
for key, label in item.items():
|
||||
prepared_data.append({
|
||||
"menu": label,
|
||||
"value": key
|
||||
})
|
||||
config["data"] = json.dumps(prepared_data)
|
||||
tool_attribute["config"] = json.dumps(config)
|
||||
|
||||
session.commit()
|
||||
|
||||
if missing_attributes:
|
||||
raise SaveWarningExc((
|
||||
"Couldn't find custom attribute/s ({}) to update."
|
||||
" Try running Create/Update Avalon Attributes in ftrack."
|
||||
).format(", ".join(missing_attributes)))
|
||||
|
||||
def on_project_settings_save(self, *_args, **_kwargs):
|
||||
"""Implementation of ISettingsChangeListener interface."""
|
||||
|
|
@ -132,7 +210,7 @@ class FtrackModule(
|
|||
return
|
||||
|
||||
def on_project_anatomy_save(
|
||||
self, old_value, new_value, changes, project_name
|
||||
self, old_value, new_value, changes, project_name, new_value_metadata
|
||||
):
|
||||
"""Implementation of ISettingsChangeListener interface."""
|
||||
if not project_name:
|
||||
|
|
@ -143,32 +221,49 @@ class FtrackModule(
|
|||
return
|
||||
|
||||
import ftrack_api
|
||||
from openpype.modules.ftrack.lib import avalon_sync
|
||||
from openpype.modules.ftrack.lib import get_openpype_attr
|
||||
|
||||
try:
|
||||
session = self.create_ftrack_session()
|
||||
except Exception:
|
||||
self.log.warning("Couldn't create ftrack session.", exc_info=True)
|
||||
raise SaveWarningExc((
|
||||
"Saving of attributes to ftrack wasn't successful,"
|
||||
" try running Create/Update Avalon Attributes in ftrack."
|
||||
))
|
||||
|
||||
session = self.create_ftrack_session()
|
||||
project_entity = session.query(
|
||||
"Project where full_name is \"{}\"".format(project_name)
|
||||
).first()
|
||||
|
||||
if not project_entity:
|
||||
self.log.warning((
|
||||
"Ftrack project with names \"{}\" was not found."
|
||||
" Skipping settings attributes change callback."
|
||||
))
|
||||
return
|
||||
msg = (
|
||||
"Ftrack project with name \"{}\" was not found in Ftrack."
|
||||
" Can't push attribute changes."
|
||||
).format(project_name)
|
||||
self.log.warning(msg)
|
||||
raise SaveWarningExc(msg)
|
||||
|
||||
project_id = project_entity["id"]
|
||||
|
||||
cust_attr, hier_attr = avalon_sync.get_pype_attr(session)
|
||||
cust_attr, hier_attr = get_openpype_attr(session)
|
||||
cust_attr_by_key = {attr["key"]: attr for attr in cust_attr}
|
||||
hier_attrs_by_key = {attr["key"]: attr for attr in hier_attr}
|
||||
|
||||
failed = {}
|
||||
missing = {}
|
||||
for key, value in attributes_changes.items():
|
||||
configuration = hier_attrs_by_key.get(key)
|
||||
if not configuration:
|
||||
configuration = cust_attr_by_key.get(key)
|
||||
if not configuration:
|
||||
self.log.warning(
|
||||
"Custom attribute \"{}\" was not found.".format(key)
|
||||
)
|
||||
missing[key] = value
|
||||
continue
|
||||
|
||||
# TODO add add permissions check
|
||||
# TODO add value validations
|
||||
# - value type and list items
|
||||
entity_key = collections.OrderedDict()
|
||||
|
|
@ -182,10 +277,45 @@ class FtrackModule(
|
|||
"value",
|
||||
ftrack_api.symbol.NOT_SET,
|
||||
value
|
||||
|
||||
)
|
||||
)
|
||||
session.commit()
|
||||
try:
|
||||
session.commit()
|
||||
self.log.debug(
|
||||
"Changed project custom attribute \"{}\" to \"{}\"".format(
|
||||
key, value
|
||||
)
|
||||
)
|
||||
except Exception:
|
||||
self.log.warning(
|
||||
"Failed to set \"{}\" to \"{}\"".format(key, value),
|
||||
exc_info=True
|
||||
)
|
||||
session.rollback()
|
||||
failed[key] = value
|
||||
|
||||
if not failed and not missing:
|
||||
return
|
||||
|
||||
error_msg = (
|
||||
"Values were not updated on Ftrack which may cause issues."
|
||||
" try running Create/Update Avalon Attributes in ftrack "
|
||||
" and resave project settings."
|
||||
)
|
||||
if missing:
|
||||
error_msg += "\nMissing Custom attributes on Ftrack: {}.".format(
|
||||
", ".join([
|
||||
'"{}"'.format(key)
|
||||
for key in missing.keys()
|
||||
])
|
||||
)
|
||||
if failed:
|
||||
joined_failed = ", ".join([
|
||||
'"{}": "{}"'.format(key, value)
|
||||
for key, value in failed.items()
|
||||
])
|
||||
error_msg += "\nFailed to set: {}".format(joined_failed)
|
||||
raise SaveWarningExc(error_msg)
|
||||
|
||||
def create_ftrack_session(self, **session_kwargs):
|
||||
import ftrack_api
|
||||
|
|
|
|||
|
|
@ -1,7 +1,21 @@
|
|||
from .constants import (
|
||||
CUST_ATTR_ID_KEY,
|
||||
CUST_ATTR_AUTO_SYNC,
|
||||
CUST_ATTR_GROUP,
|
||||
CUST_ATTR_TOOLS,
|
||||
CUST_ATTR_APPLICATIONS
|
||||
)
|
||||
from . settings import (
|
||||
get_ftrack_url_from_settings,
|
||||
get_ftrack_event_mongo_info
|
||||
)
|
||||
from .custom_attributes import (
|
||||
default_custom_attributes_definition,
|
||||
app_definitions_from_app_manager,
|
||||
tool_definitions_from_app_manager,
|
||||
get_openpype_attr
|
||||
)
|
||||
|
||||
from . import avalon_sync
|
||||
from . import credentials
|
||||
from .ftrack_base_handler import BaseHandler
|
||||
|
|
@ -10,9 +24,20 @@ from .ftrack_action_handler import BaseAction, ServerAction, statics_icon
|
|||
|
||||
|
||||
__all__ = (
|
||||
"CUST_ATTR_ID_KEY",
|
||||
"CUST_ATTR_AUTO_SYNC",
|
||||
"CUST_ATTR_GROUP",
|
||||
"CUST_ATTR_TOOLS",
|
||||
"CUST_ATTR_APPLICATIONS",
|
||||
|
||||
"get_ftrack_url_from_settings",
|
||||
"get_ftrack_event_mongo_info",
|
||||
|
||||
"default_custom_attributes_definition",
|
||||
"app_definitions_from_app_manager",
|
||||
"tool_definitions_from_app_manager",
|
||||
"get_openpype_attr",
|
||||
|
||||
"avalon_sync",
|
||||
|
||||
"credentials",
|
||||
|
|
|
|||
|
|
@ -14,17 +14,21 @@ else:
|
|||
from avalon.api import AvalonMongoDB
|
||||
|
||||
import avalon
|
||||
|
||||
from openpype.api import (
|
||||
Logger,
|
||||
Anatomy,
|
||||
get_anatomy_settings
|
||||
)
|
||||
from openpype.lib import ApplicationManager
|
||||
|
||||
from .constants import CUST_ATTR_ID_KEY
|
||||
from .custom_attributes import get_openpype_attr
|
||||
|
||||
from bson.objectid import ObjectId
|
||||
from bson.errors import InvalidId
|
||||
from pymongo import UpdateOne
|
||||
import ftrack_api
|
||||
from openpype.lib import ApplicationManager
|
||||
|
||||
log = Logger.get_logger(__name__)
|
||||
|
||||
|
|
@ -36,23 +40,6 @@ EntitySchemas = {
|
|||
"config": "openpype:config-2.0"
|
||||
}
|
||||
|
||||
# Group name of custom attributes
|
||||
CUST_ATTR_GROUP = "openpype"
|
||||
|
||||
# name of Custom attribute that stores mongo_id from avalon db
|
||||
CUST_ATTR_ID_KEY = "avalon_mongo_id"
|
||||
CUST_ATTR_AUTO_SYNC = "avalon_auto_sync"
|
||||
|
||||
|
||||
def default_custom_attributes_definition():
|
||||
json_file_path = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"custom_attributes.json"
|
||||
)
|
||||
with open(json_file_path, "r") as json_stream:
|
||||
data = json.load(json_stream)
|
||||
return data
|
||||
|
||||
|
||||
def check_regex(name, entity_type, in_schema=None, schema_patterns=None):
|
||||
schema_name = "asset-3.0"
|
||||
|
|
@ -91,39 +78,6 @@ def join_query_keys(keys):
|
|||
return ",".join(["\"{}\"".format(key) for key in keys])
|
||||
|
||||
|
||||
def get_pype_attr(session, split_hierarchical=True, query_keys=None):
|
||||
custom_attributes = []
|
||||
hier_custom_attributes = []
|
||||
if not query_keys:
|
||||
query_keys = [
|
||||
"id",
|
||||
"entity_type",
|
||||
"object_type_id",
|
||||
"is_hierarchical",
|
||||
"default"
|
||||
]
|
||||
# TODO remove deprecated "pype" group from query
|
||||
cust_attrs_query = (
|
||||
"select {}"
|
||||
" from CustomAttributeConfiguration"
|
||||
# Kept `pype` for Backwards Compatiblity
|
||||
" where group.name in (\"pype\", \"{}\")"
|
||||
).format(", ".join(query_keys), CUST_ATTR_GROUP)
|
||||
all_avalon_attr = session.query(cust_attrs_query).all()
|
||||
for cust_attr in all_avalon_attr:
|
||||
if split_hierarchical and cust_attr["is_hierarchical"]:
|
||||
hier_custom_attributes.append(cust_attr)
|
||||
continue
|
||||
|
||||
custom_attributes.append(cust_attr)
|
||||
|
||||
if split_hierarchical:
|
||||
# return tuple
|
||||
return custom_attributes, hier_custom_attributes
|
||||
|
||||
return custom_attributes
|
||||
|
||||
|
||||
def get_python_type_for_custom_attribute(cust_attr, cust_attr_type_name=None):
|
||||
"""Python type that should value of custom attribute have.
|
||||
|
||||
|
|
@ -921,7 +875,7 @@ class SyncEntitiesFactory:
|
|||
def set_cutom_attributes(self):
|
||||
self.log.debug("* Preparing custom attributes")
|
||||
# Get custom attributes and values
|
||||
custom_attrs, hier_attrs = get_pype_attr(
|
||||
custom_attrs, hier_attrs = get_openpype_attr(
|
||||
self.session, query_keys=self.cust_attr_query_keys
|
||||
)
|
||||
ent_types = self.session.query("select id, name from ObjectType").all()
|
||||
|
|
@ -2508,7 +2462,7 @@ class SyncEntitiesFactory:
|
|||
if new_entity_id not in p_chilren:
|
||||
self.entities_dict[parent_id]["children"].append(new_entity_id)
|
||||
|
||||
cust_attr, _ = get_pype_attr(self.session)
|
||||
cust_attr, _ = get_openpype_attr(self.session)
|
||||
for _attr in cust_attr:
|
||||
key = _attr["key"]
|
||||
if key not in av_entity["data"]:
|
||||
|
|
|
|||
12
openpype/modules/ftrack/lib/constants.py
Normal file
12
openpype/modules/ftrack/lib/constants.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# Group name of custom attributes
|
||||
CUST_ATTR_GROUP = "openpype"
|
||||
|
||||
# name of Custom attribute that stores mongo_id from avalon db
|
||||
CUST_ATTR_ID_KEY = "avalon_mongo_id"
|
||||
# Auto sync of project
|
||||
CUST_ATTR_AUTO_SYNC = "avalon_auto_sync"
|
||||
|
||||
# Applications custom attribute name
|
||||
CUST_ATTR_APPLICATIONS = "applications"
|
||||
# Environment tools custom attribute
|
||||
CUST_ATTR_TOOLS = "tools_env"
|
||||
73
openpype/modules/ftrack/lib/custom_attributes.py
Normal file
73
openpype/modules/ftrack/lib/custom_attributes.py
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import os
|
||||
import json
|
||||
|
||||
from .constants import CUST_ATTR_GROUP
|
||||
|
||||
|
||||
def default_custom_attributes_definition():
|
||||
json_file_path = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"custom_attributes.json"
|
||||
)
|
||||
with open(json_file_path, "r") as json_stream:
|
||||
data = json.load(json_stream)
|
||||
return data
|
||||
|
||||
|
||||
def app_definitions_from_app_manager(app_manager):
|
||||
app_definitions = []
|
||||
for app_name, app in app_manager.applications.items():
|
||||
if app.enabled and app.is_host:
|
||||
app_definitions.append({
|
||||
app_name: app.full_label
|
||||
})
|
||||
|
||||
if not app_definitions:
|
||||
app_definitions.append({"empty": "< Empty >"})
|
||||
return app_definitions
|
||||
|
||||
|
||||
def tool_definitions_from_app_manager(app_manager):
|
||||
tools_data = []
|
||||
for tool_name, tool in app_manager.tools.items():
|
||||
tools_data.append({
|
||||
tool_name: tool.label
|
||||
})
|
||||
|
||||
# Make sure there is at least one item
|
||||
if not tools_data:
|
||||
tools_data.append({"empty": "< Empty >"})
|
||||
return tools_data
|
||||
|
||||
|
||||
def get_openpype_attr(session, split_hierarchical=True, query_keys=None):
|
||||
custom_attributes = []
|
||||
hier_custom_attributes = []
|
||||
if not query_keys:
|
||||
query_keys = [
|
||||
"id",
|
||||
"entity_type",
|
||||
"object_type_id",
|
||||
"is_hierarchical",
|
||||
"default"
|
||||
]
|
||||
# TODO remove deprecated "pype" group from query
|
||||
cust_attrs_query = (
|
||||
"select {}"
|
||||
" from CustomAttributeConfiguration"
|
||||
# Kept `pype` for Backwards Compatiblity
|
||||
" where group.name in (\"pype\", \"{}\")"
|
||||
).format(", ".join(query_keys), CUST_ATTR_GROUP)
|
||||
all_avalon_attr = session.query(cust_attrs_query).all()
|
||||
for cust_attr in all_avalon_attr:
|
||||
if split_hierarchical and cust_attr["is_hierarchical"]:
|
||||
hier_custom_attributes.append(cust_attr)
|
||||
continue
|
||||
|
||||
custom_attributes.append(cust_attr)
|
||||
|
||||
if split_hierarchical:
|
||||
# return tuple
|
||||
return custom_attributes, hier_custom_attributes
|
||||
|
||||
return custom_attributes
|
||||
|
|
@ -16,18 +16,20 @@ class ISettingsChangeListener:
|
|||
}
|
||||
"""
|
||||
@abstractmethod
|
||||
def on_system_settings_save(self, old_value, new_value, changes):
|
||||
def on_system_settings_save(
|
||||
self, old_value, new_value, changes, new_value_metadata
|
||||
):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def on_project_settings_save(
|
||||
self, old_value, new_value, changes, project_name
|
||||
self, old_value, new_value, changes, project_name, new_value_metadata
|
||||
):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def on_project_anatomy_save(
|
||||
self, old_value, new_value, changes, project_name
|
||||
self, old_value, new_value, changes, project_name, new_value_metadata
|
||||
):
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
from .exceptions import (
|
||||
SaveWarningExc
|
||||
)
|
||||
from .lib import (
|
||||
get_system_settings,
|
||||
get_project_settings,
|
||||
|
|
@ -13,6 +16,8 @@ from .entities import (
|
|||
|
||||
|
||||
__all__ = (
|
||||
"SaveWarningExc",
|
||||
|
||||
"get_system_settings",
|
||||
"get_project_settings",
|
||||
"get_current_project_settings",
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ from openpype.settings.constants import (
|
|||
PROJECT_ANATOMY_KEY,
|
||||
KEY_REGEX
|
||||
)
|
||||
from openpype.settings.exceptions import SaveWarningExc
|
||||
|
||||
from openpype.settings.lib import (
|
||||
DEFAULTS_DIR,
|
||||
|
|
@ -724,8 +725,19 @@ class ProjectSettings(RootEntity):
|
|||
project_settings = settings_value.get(PROJECT_SETTINGS_KEY) or {}
|
||||
project_anatomy = settings_value.get(PROJECT_ANATOMY_KEY) or {}
|
||||
|
||||
save_project_settings(self.project_name, project_settings)
|
||||
save_project_anatomy(self.project_name, project_anatomy)
|
||||
warnings = []
|
||||
try:
|
||||
save_project_settings(self.project_name, project_settings)
|
||||
except SaveWarningExc as exc:
|
||||
warnings.extend(exc.warnings)
|
||||
|
||||
try:
|
||||
save_project_anatomy(self.project_name, project_anatomy)
|
||||
except SaveWarningExc as exc:
|
||||
warnings.extend(exc.warnings)
|
||||
|
||||
if warnings:
|
||||
raise SaveWarningExc(warnings)
|
||||
|
||||
def _validate_defaults_to_save(self, value):
|
||||
"""Valiations of default values before save."""
|
||||
|
|
|
|||
11
openpype/settings/exceptions.py
Normal file
11
openpype/settings/exceptions.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
class SaveSettingsValidation(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class SaveWarningExc(SaveSettingsValidation):
|
||||
def __init__(self, warnings):
|
||||
if isinstance(warnings, str):
|
||||
warnings = [warnings]
|
||||
self.warnings = warnings
|
||||
msg = " | ".join(warnings)
|
||||
super(SaveWarningExc, self).__init__(msg)
|
||||
|
|
@ -4,6 +4,9 @@ import functools
|
|||
import logging
|
||||
import platform
|
||||
import copy
|
||||
from .exceptions import (
|
||||
SaveWarningExc
|
||||
)
|
||||
from .constants import (
|
||||
M_OVERRIDEN_KEY,
|
||||
M_ENVIRONMENT_KEY,
|
||||
|
|
@ -101,8 +104,14 @@ def save_studio_settings(data):
|
|||
|
||||
For saving of data cares registered Settings handler.
|
||||
|
||||
Warning messages are not logged as module raising them should log it within
|
||||
it's logger.
|
||||
|
||||
Args:
|
||||
data(dict): Overrides data with metadata defying studio overrides.
|
||||
|
||||
Raises:
|
||||
SaveWarningExc: If any module raises the exception.
|
||||
"""
|
||||
# Notify Pype modules
|
||||
from openpype.modules import ModulesManager, ISettingsChangeListener
|
||||
|
|
@ -110,15 +119,25 @@ def save_studio_settings(data):
|
|||
old_data = get_system_settings()
|
||||
default_values = get_default_settings()[SYSTEM_SETTINGS_KEY]
|
||||
new_data = apply_overrides(default_values, copy.deepcopy(data))
|
||||
new_data_with_metadata = copy.deepcopy(new_data)
|
||||
clear_metadata_from_settings(new_data)
|
||||
|
||||
changes = calculate_changes(old_data, new_data)
|
||||
modules_manager = ModulesManager(_system_settings=new_data)
|
||||
|
||||
warnings = []
|
||||
for module in modules_manager.get_enabled_modules():
|
||||
if isinstance(module, ISettingsChangeListener):
|
||||
module.on_system_settings_save(old_data, new_data, changes)
|
||||
try:
|
||||
module.on_system_settings_save(
|
||||
old_data, new_data, changes, new_data_with_metadata
|
||||
)
|
||||
except SaveWarningExc as exc:
|
||||
warnings.extend(exc.warnings)
|
||||
|
||||
return _SETTINGS_HANDLER.save_studio_settings(data)
|
||||
_SETTINGS_HANDLER.save_studio_settings(data)
|
||||
if warnings:
|
||||
raise SaveWarningExc(warnings)
|
||||
|
||||
|
||||
@require_handler
|
||||
|
|
@ -130,10 +149,16 @@ def save_project_settings(project_name, overrides):
|
|||
|
||||
For saving of data cares registered Settings handler.
|
||||
|
||||
Warning messages are not logged as module raising them should log it within
|
||||
it's logger.
|
||||
|
||||
Args:
|
||||
project_name (str): Project name for which overrides are passed.
|
||||
Default project's value is None.
|
||||
overrides(dict): Overrides data with metadata defying studio overrides.
|
||||
|
||||
Raises:
|
||||
SaveWarningExc: If any module raises the exception.
|
||||
"""
|
||||
# Notify Pype modules
|
||||
from openpype.modules import ModulesManager, ISettingsChangeListener
|
||||
|
|
@ -151,17 +176,29 @@ def save_project_settings(project_name, overrides):
|
|||
old_data = get_default_project_settings(exclude_locals=True)
|
||||
new_data = apply_overrides(default_values, copy.deepcopy(overrides))
|
||||
|
||||
new_data_with_metadata = copy.deepcopy(new_data)
|
||||
clear_metadata_from_settings(new_data)
|
||||
|
||||
changes = calculate_changes(old_data, new_data)
|
||||
modules_manager = ModulesManager()
|
||||
warnings = []
|
||||
for module in modules_manager.get_enabled_modules():
|
||||
if isinstance(module, ISettingsChangeListener):
|
||||
module.on_project_settings_save(
|
||||
old_data, new_data, project_name, changes
|
||||
)
|
||||
try:
|
||||
module.on_project_settings_save(
|
||||
old_data,
|
||||
new_data,
|
||||
project_name,
|
||||
changes,
|
||||
new_data_with_metadata
|
||||
)
|
||||
except SaveWarningExc as exc:
|
||||
warnings.extend(exc.warnings)
|
||||
|
||||
return _SETTINGS_HANDLER.save_project_settings(project_name, overrides)
|
||||
_SETTINGS_HANDLER.save_project_settings(project_name, overrides)
|
||||
|
||||
if warnings:
|
||||
raise SaveWarningExc(warnings)
|
||||
|
||||
|
||||
@require_handler
|
||||
|
|
@ -173,10 +210,16 @@ def save_project_anatomy(project_name, anatomy_data):
|
|||
|
||||
For saving of data cares registered Settings handler.
|
||||
|
||||
Warning messages are not logged as module raising them should log it within
|
||||
it's logger.
|
||||
|
||||
Args:
|
||||
project_name (str): Project name for which overrides are passed.
|
||||
Default project's value is None.
|
||||
overrides(dict): Overrides data with metadata defying studio overrides.
|
||||
|
||||
Raises:
|
||||
SaveWarningExc: If any module raises the exception.
|
||||
"""
|
||||
# Notify Pype modules
|
||||
from openpype.modules import ModulesManager, ISettingsChangeListener
|
||||
|
|
@ -194,17 +237,29 @@ def save_project_anatomy(project_name, anatomy_data):
|
|||
old_data = get_default_anatomy_settings(exclude_locals=True)
|
||||
new_data = apply_overrides(default_values, copy.deepcopy(anatomy_data))
|
||||
|
||||
new_data_with_metadata = copy.deepcopy(new_data)
|
||||
clear_metadata_from_settings(new_data)
|
||||
|
||||
changes = calculate_changes(old_data, new_data)
|
||||
modules_manager = ModulesManager()
|
||||
warnings = []
|
||||
for module in modules_manager.get_enabled_modules():
|
||||
if isinstance(module, ISettingsChangeListener):
|
||||
module.on_project_anatomy_save(
|
||||
old_data, new_data, changes, project_name
|
||||
)
|
||||
try:
|
||||
module.on_project_anatomy_save(
|
||||
old_data,
|
||||
new_data,
|
||||
changes,
|
||||
project_name,
|
||||
new_data_with_metadata
|
||||
)
|
||||
except SaveWarningExc as exc:
|
||||
warnings.extend(exc.warnings)
|
||||
|
||||
return _SETTINGS_HANDLER.save_project_anatomy(project_name, anatomy_data)
|
||||
_SETTINGS_HANDLER.save_project_anatomy(project_name, anatomy_data)
|
||||
|
||||
if warnings:
|
||||
raise SaveWarningExc(warnings)
|
||||
|
||||
|
||||
@require_handler
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ from openpype.settings.entities import (
|
|||
SchemaError
|
||||
)
|
||||
|
||||
from openpype.settings.lib import get_system_settings
|
||||
from openpype.settings import SaveWarningExc
|
||||
from .widgets import ProjectListWidget
|
||||
|
||||
from . import lib
|
||||
|
|
@ -272,6 +272,22 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
|||
# not required.
|
||||
self.reset()
|
||||
|
||||
except SaveWarningExc as exc:
|
||||
warnings = [
|
||||
"<b>Settings were saved but few issues happened.</b>"
|
||||
]
|
||||
for item in exc.warnings:
|
||||
warnings.append(item.replace("\n", "<br>"))
|
||||
|
||||
msg = "<br><br>".join(warnings)
|
||||
|
||||
dialog = QtWidgets.QMessageBox(self)
|
||||
dialog.setText(msg)
|
||||
dialog.setIcon(QtWidgets.QMessageBox.Warning)
|
||||
dialog.exec_()
|
||||
|
||||
self.reset()
|
||||
|
||||
except Exception as exc:
|
||||
formatted_traceback = traceback.format_exception(*sys.exc_info())
|
||||
dialog = QtWidgets.QMessageBox(self)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue