diff --git a/.gitignore b/.gitignore index 101c1e6224..044efb4617 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,8 @@ __pycache__/ Icon # Thumbnails ._* +# rope project dir +.ropeproject # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd diff --git a/pype/__init__.py b/pype/__init__.py index 2a922547e8..a2840e271b 100644 --- a/pype/__init__.py +++ b/pype/__init__.py @@ -2,7 +2,7 @@ import os from pyblish import api as pyblish from avalon import api as avalon -from .api import config, Anatomy +from .api import get_project_settings, Anatomy from .lib import filter_pyblish_plugins @@ -48,16 +48,19 @@ def patched_discover(superclass): elif superclass.__name__.split(".")[-1] == "Creator": plugin_type = "create" - print(">>> trying to find presets for {}:{} ...".format(host, plugin_type)) + print(">>> Finding presets for {}:{} ...".format(host, plugin_type)) try: - config_data = config.get_presets()['plugins'][host][plugin_type] + settings = ( + get_project_settings(os.environ['AVALON_PROJECT']) + [host][plugin_type] + ) except KeyError: print("*** no presets found.") else: for plugin in plugins: - if plugin.__name__ in config_data: + if plugin.__name__ in settings: print(">>> We have preset for {}".format(plugin.__name__)) - for option, value in config_data[plugin.__name__].items(): + for option, value in settings[plugin.__name__].items(): if option == "enabled" and value is False: setattr(plugin, "active", False) print(" - is disabled by preset") @@ -104,6 +107,7 @@ def install(): anatomy.set_root_environments() avalon.register_root(anatomy.roots) # apply monkey patched discover to original one + log.info("Patching discovery") avalon.discover = patched_discover diff --git a/pype/hooks/global/post_ftrack_changes.py b/pype/hooks/global/post_ftrack_changes.py index 144f618620..4dc45f5419 100644 --- a/pype/hooks/global/post_ftrack_changes.py +++ b/pype/hooks/global/post_ftrack_changes.py @@ -1,7 +1,7 @@ import os import ftrack_api -from pype.api import config +from pype.api import get_project_settings from pype.lib import PostLaunchHook @@ -101,10 +101,23 @@ class PostFtrackHook(PostLaunchHook): return filtered_entities[0] def ftrack_status_change(self, session, entity, project_name): - # TODO use settings - presets = config.get_presets(project_name)["ftrack"]["ftrack_config"] - statuses = presets.get("status_update") - if not statuses: + project_settings = get_project_settings(project_name) + status_update = project_settings["ftrack"]["events"]["status_update"] + if not status_update["enabled"]: + self.log.debug( + "Status changes are disabled for project \"{}\"".format( + project_name + ) + ) + return + + status_mapping = status_update["mapping"] + if not status_mapping: + self.log.warning( + "Project \"{}\" does not have set status changes.".format( + project_name + ) + ) return actual_status = entity["status"]["name"].lower() @@ -114,11 +127,11 @@ class PostFtrackHook(PostLaunchHook): ) while True: next_status_name = None - for key, value in statuses.items(): + for key, value in status_mapping.items(): if key in already_tested: continue - if actual_status in value or "_any_" in value: - if key != "_ignore_": + if actual_status in value or "__any__" in value: + if key != "__ignore__": next_status_name = key already_tested.add(key) break diff --git a/pype/hooks/global/pre_global_host_data.py b/pype/hooks/global/pre_global_host_data.py index 3f403b43f5..4910d08010 100644 --- a/pype/hooks/global/pre_global_host_data.py +++ b/pype/hooks/global/pre_global_host_data.py @@ -6,7 +6,7 @@ import copy from pype.api import ( Anatomy, - config + get_project_settings ) from pype.lib import ( env_value_to_bool, @@ -284,20 +284,11 @@ class GlobalHostDataHook(PreLaunchHook): bool: True if host should start workfile. """ - default_output = env_value_to_bool( - "AVALON_OPEN_LAST_WORKFILE", default=False - ) - # TODO convert to settings - try: - startup_presets = ( - config.get_presets(project_name) - .get("tools", {}) - .get("workfiles", {}) - .get("last_workfile_on_startup") - ) - except Exception: - startup_presets = None - self.log.warning("Couldn't load pype's presets", exc_info=True) + + project_settings = ( + get_project_settings(project_name)['global']['tools']) + startup_presets = ( + project_settings['Workfiles']['last_workfile_on_startup']) if not startup_presets: return default_output diff --git a/pype/hosts/harmony/__init__.py b/pype/hosts/harmony/__init__.py index 7ea261292e..b91c0ad4b1 100644 --- a/pype/hosts/harmony/__init__.py +++ b/pype/hosts/harmony/__init__.py @@ -9,7 +9,7 @@ import avalon.tools.sceneinventory import pyblish.api from pype import lib -from pype.api import config +from pype.api import get_current_project_settings def set_scene_settings(settings): @@ -50,10 +50,18 @@ def get_asset_settings(): } try: - skip_resolution_check = \ - config.get_presets()["harmony"]["general"]["skip_resolution_check"] - skip_timelines_check = \ - config.get_presets()["harmony"]["general"]["skip_timelines_check"] + skip_resolution_check = ( + get_current_project_settings() + ["harmony"] + ["general"] + ["skip_resolution_check"] + ) + skip_timelines_check = ( + get_current_project_settings() + ["harmony"] + ["general"] + ["skip_timelines_check"] + ) except KeyError: skip_resolution_check = [] skip_timelines_check = [] diff --git a/pype/hosts/nuke/plugin.py b/pype/hosts/nuke/plugin.py index 652c0396a8..5d00b19ec5 100644 --- a/pype/hosts/nuke/plugin.py +++ b/pype/hosts/nuke/plugin.py @@ -1,13 +1,13 @@ import re import avalon.api import avalon.nuke -from pype.api import config +from pype.api import get_current_project_settings class PypeCreator(avalon.nuke.pipeline.Creator): """Pype Nuke Creator class wrapper """ def __init__(self, *args, **kwargs): super(PypeCreator, self).__init__(*args, **kwargs) - self.presets = config.get_presets()['plugins']["nuke"]["create"].get( + self.presets = get_current_project_settings()["nuke"]["create"].get( self.__class__.__name__, {} ) diff --git a/pype/hosts/unreal/lib.py b/pype/hosts/unreal/lib.py index d6bfba436e..02b1ae5bf5 100644 --- a/pype/hosts/unreal/lib.py +++ b/pype/hosts/unreal/lib.py @@ -4,7 +4,7 @@ import platform import json from distutils import dir_util import subprocess -from pype.api import config +from pype.api import get_project_settings def get_engine_versions(): @@ -150,7 +150,7 @@ def create_unreal_project(project_name: str, :type dev_mode: bool :returns: None """ - preset = config.get_presets()["unreal"]["project_setup"] + preset = get_project_settings(project_name)["unreal"]["project_setup"] if os.path.isdir(os.environ.get("AVALON_UNREAL_PLUGIN", "")): # copy plugin to correct path under project diff --git a/pype/lib/__init__.py b/pype/lib/__init__.py index ecdd155c99..d02d20ad4d 100644 --- a/pype/lib/__init__.py +++ b/pype/lib/__init__.py @@ -1,11 +1,6 @@ # -*- coding: utf-8 -*- """Pype lib module.""" -from .deprecated import ( - get_avalon_database, - set_io_database -) - from .env_tools import ( env_value_to_bool, get_paths_from_environ @@ -36,8 +31,6 @@ from .applications import ( ApplicationManager, PreLaunchHook, PostLaunchHook, - launch_application, - ApplicationAction, _subprocess ) @@ -55,9 +48,6 @@ from .ffmpeg_utils import ( ) __all__ = [ - "get_avalon_database", - "set_io_database", - "env_value_to_bool", "get_paths_from_environ", @@ -82,8 +72,6 @@ __all__ = [ "ApplicationManager", "PreLaunchHook", "PostLaunchHook", - "launch_application", - "ApplicationAction", "filter_pyblish_plugins", diff --git a/pype/lib/applications.py b/pype/lib/applications.py index 1a5b70f339..bee51d0570 100644 --- a/pype/lib/applications.py +++ b/pype/lib/applications.py @@ -1,36 +1,20 @@ import os -import sys -import getpass import copy import platform import inspect -import logging import subprocess import distutils.spawn from abc import ABCMeta, abstractmethod import six -import acre -import avalon.lib -import avalon.api +from pype.settings import get_system_settings, get_environments +from ..api import Logger -from ..api import ( - Anatomy, - Logger, - config, - get_system_settings, - get_environments -) from .python_module_tools import ( modules_from_path, classes_from_module ) -from .hooks import execute_hook -from .deprecated import get_avalon_database - - -log = logging.getLogger(__name__) class ApplicationNotFound(Exception): @@ -79,377 +63,6 @@ class ApplicationLaunchFailed(Exception): pass -def launch_application(project_name, asset_name, task_name, app_name): - """Launch host application with filling required environments. - - TODO(iLLiCiT): This should be split into more parts. - """ - # `get_avalon_database` is in Pype 3 replaced with using `AvalonMongoDB` - database = get_avalon_database() - project_document = database[project_name].find_one({"type": "project"}) - asset_document = database[project_name].find_one({ - "type": "asset", - "name": asset_name - }) - - asset_doc_parents = asset_document["data"].get("parents") - hierarchy = "/".join(asset_doc_parents) - - app_def = avalon.lib.get_application(app_name) - app_label = app_def.get("ftrack_label", app_def.get("label", app_name)) - - host_name = app_def["application_dir"] - # Workfile data collection may be special function? - data = { - "project": { - "name": project_document["name"], - "code": project_document["data"].get("code") - }, - "task": task_name, - "asset": asset_name, - "app": host_name, - "hierarchy": hierarchy - } - - try: - anatomy = Anatomy(project_name) - anatomy_filled = anatomy.format(data) - workdir = os.path.normpath(anatomy_filled["work"]["folder"]) - - except Exception as exc: - raise ApplicationLaunchFailed( - "Error in anatomy.format: {}".format(str(exc)) - ) - - try: - os.makedirs(workdir) - except FileExistsError: - pass - - last_workfile_path = None - extensions = avalon.api.HOST_WORKFILE_EXTENSIONS.get(host_name) - if extensions: - # Find last workfile - file_template = anatomy.templates["work"]["file"] - data.update({ - "version": 1, - "user": os.environ.get("PYPE_USERNAME") or getpass.getuser(), - "ext": extensions[0] - }) - - last_workfile_path = avalon.api.last_workfile( - workdir, file_template, data, extensions, True - ) - - # set environments for Avalon - prep_env = copy.deepcopy(os.environ) - prep_env.update({ - "AVALON_PROJECT": project_name, - "AVALON_ASSET": asset_name, - "AVALON_TASK": task_name, - "AVALON_APP": host_name, - "AVALON_APP_NAME": app_name, - "AVALON_HIERARCHY": hierarchy, - "AVALON_WORKDIR": workdir - }) - - start_last_workfile = avalon.api.should_start_last_workfile( - project_name, host_name, task_name - ) - # Store boolean as "0"(False) or "1"(True) - prep_env["AVALON_OPEN_LAST_WORKFILE"] = ( - str(int(bool(start_last_workfile))) - ) - - if ( - start_last_workfile - and last_workfile_path - and os.path.exists(last_workfile_path) - ): - prep_env["AVALON_LAST_WORKFILE"] = last_workfile_path - - prep_env.update(anatomy.roots_obj.root_environments()) - - # collect all the 'environment' attributes from parents - tools_attr = [prep_env["AVALON_APP"], prep_env["AVALON_APP_NAME"]] - tools_env = asset_document["data"].get("tools_env") or [] - tools_attr.extend(tools_env) - - tools_env = acre.get_tools(tools_attr) - env = acre.compute(tools_env) - env = acre.merge(env, current_env=dict(prep_env)) - - # Get path to execute - st_temp_path = os.environ["PYPE_CONFIG"] - os_plat = platform.system().lower() - - # Path to folder with launchers - path = os.path.join(st_temp_path, "launchers", os_plat) - - # Full path to executable launcher - execfile = None - - launch_hook = app_def.get("launch_hook") - if launch_hook: - log.info("launching hook: {}".format(launch_hook)) - ret_val = execute_hook(launch_hook, env=env) - if not ret_val: - raise ApplicationLaunchFailed( - "Hook didn't finish successfully {}".format(app_label) - ) - - if sys.platform == "win32": - for ext in os.environ["PATHEXT"].split(os.pathsep): - fpath = os.path.join(path.strip('"'), app_def["executable"] + ext) - if os.path.isfile(fpath) and os.access(fpath, os.X_OK): - execfile = fpath - break - - # Run SW if was found executable - if execfile is None: - raise ApplicationLaunchFailed( - "We didn't find launcher for {}".format(app_label) - ) - - popen = avalon.lib.launch( - executable=execfile, args=[], environment=env - ) - - elif ( - sys.platform.startswith("linux") - or sys.platform.startswith("darwin") - ): - execfile = os.path.join(path.strip('"'), app_def["executable"]) - # Run SW if was found executable - if execfile is None: - raise ApplicationLaunchFailed( - "We didn't find launcher for {}".format(app_label) - ) - - if not os.path.isfile(execfile): - raise ApplicationLaunchFailed( - "Launcher doesn't exist - {}".format(execfile) - ) - - try: - fp = open(execfile) - except PermissionError as perm_exc: - raise ApplicationLaunchFailed( - "Access denied on launcher {} - {}".format(execfile, perm_exc) - ) - - fp.close() - # check executable permission - if not os.access(execfile, os.X_OK): - raise ApplicationLaunchFailed( - "No executable permission - {}".format(execfile) - ) - - popen = avalon.lib.launch( # noqa: F841 - "/usr/bin/env", args=["bash", execfile], environment=env - ) - return popen - - -class ApplicationAction: - """Default application launcher - - This is a convenience application Action that when "config" refers to a - parsed application `.toml` this can launch the application. - - """ - _log = None - config = None - group = None - variant = None - required_session_keys = ( - "AVALON_PROJECT", - "AVALON_ASSET", - "AVALON_TASK" - ) - - @property - def log(self): - if self._log is None: - self._log = Logger().get_logger(self.__class__.__name__) - return self._log - - def is_compatible(self, session): - for key in self.required_session_keys: - if key not in session: - return False - return True - - def process(self, session, **kwargs): - """Process the full Application action""" - - project_name = session["AVALON_PROJECT"] - asset_name = session["AVALON_ASSET"] - task_name = session["AVALON_TASK"] - launch_application( - project_name, asset_name, task_name, self.name - ) - - self._ftrack_after_launch_procedure( - project_name, asset_name, task_name - ) - - def _ftrack_after_launch_procedure( - self, project_name, asset_name, task_name - ): - # TODO move to launch hook - required_keys = ("FTRACK_SERVER", "FTRACK_API_USER", "FTRACK_API_KEY") - for key in required_keys: - if not os.environ.get(key): - self.log.debug(( - "Missing required environment \"{}\"" - " for Ftrack after launch procedure." - ).format(key)) - return - - try: - import ftrack_api - session = ftrack_api.Session(auto_connect_event_hub=True) - self.log.debug("Ftrack session created") - except Exception: - self.log.warning("Couldn't create Ftrack session") - return - - try: - entity = self._find_ftrack_task_entity( - session, project_name, asset_name, task_name - ) - self._ftrack_status_change(session, entity, project_name) - self._start_timer(session, entity, ftrack_api) - except Exception: - self.log.warning( - "Couldn't finish Ftrack procedure.", exc_info=True - ) - return - - finally: - session.close() - - def _find_ftrack_task_entity( - self, session, project_name, asset_name, task_name - ): - project_entity = session.query( - "Project where full_name is \"{}\"".format(project_name) - ).first() - if not project_entity: - self.log.warning( - "Couldn't find project \"{}\" in Ftrack.".format(project_name) - ) - return - - potential_task_entities = session.query(( - "TypedContext where parent.name is \"{}\" and project_id is \"{}\"" - ).format(asset_name, project_entity["id"])).all() - filtered_entities = [] - for _entity in potential_task_entities: - if ( - _entity.entity_type.lower() == "task" - and _entity["name"] == task_name - ): - filtered_entities.append(_entity) - - if not filtered_entities: - self.log.warning(( - "Couldn't find task \"{}\" under parent \"{}\" in Ftrack." - ).format(task_name, asset_name)) - return - - if len(filtered_entities) > 1: - self.log.warning(( - "Found more than one task \"{}\"" - " under parent \"{}\" in Ftrack." - ).format(task_name, asset_name)) - return - - return filtered_entities[0] - - def _ftrack_status_change(self, session, entity, project_name): - presets = config.get_presets(project_name)["ftrack"]["ftrack_config"] - statuses = presets.get("status_update") - if not statuses: - return - - actual_status = entity["status"]["name"].lower() - already_tested = set() - ent_path = "/".join( - [ent["name"] for ent in entity["link"]] - ) - while True: - next_status_name = None - for key, value in statuses.items(): - if key in already_tested: - continue - if actual_status in value or "_any_" in value: - if key != "_ignore_": - next_status_name = key - already_tested.add(key) - break - already_tested.add(key) - - if next_status_name is None: - break - - try: - query = "Status where name is \"{}\"".format( - next_status_name - ) - status = session.query(query).one() - - entity["status"] = status - session.commit() - self.log.debug("Changing status to \"{}\" <{}>".format( - next_status_name, ent_path - )) - break - - except Exception: - session.rollback() - msg = ( - "Status \"{}\" in presets wasn't found" - " on Ftrack entity type \"{}\"" - ).format(next_status_name, entity.entity_type) - self.log.warning(msg) - - def _start_timer(self, session, entity, _ftrack_api): - self.log.debug("Triggering timer start.") - - user_entity = session.query("User where username is \"{}\"".format( - os.environ["FTRACK_API_USER"] - )).first() - if not user_entity: - self.log.warning( - "Couldn't find user with username \"{}\" in Ftrack".format( - os.environ["FTRACK_API_USER"] - ) - ) - return - - source = { - "user": { - "id": user_entity["id"], - "username": user_entity["username"] - } - } - event_data = { - "actionIdentifier": "start.timer", - "selection": [{"entityId": entity["id"], "entityType": "task"}] - } - session.event_hub.publish( - _ftrack_api.event.base.Event( - topic="ftrack.action.launch", - data=event_data, - source=source - ), - on_error="ignore" - ) - self.log.debug("Timer start triggered successfully.") - - # Special naming case for subprocess since its a built-in method. def _subprocess(*args, **kwargs): """Convenience method for getting output errors for subprocess. diff --git a/pype/lib/avalon_context.py b/pype/lib/avalon_context.py index 6cecdb93e3..25966d550e 100644 --- a/pype/lib/avalon_context.py +++ b/pype/lib/avalon_context.py @@ -5,7 +5,7 @@ import logging import collections from avalon import io, pipeline -from ..api import config +from ..api import get_project_settings import avalon.api log = logging.getLogger("AvalonContext") @@ -410,12 +410,12 @@ class BuildWorkfile: (dict): preset per entered task name """ host_name = avalon.api.registered_host().__name__.rsplit(".", 1)[-1] - presets = config.get_presets(io.Session["AVALON_PROJECT"]) + presets = get_project_settings(io.Session["AVALON_PROJECT"]) # Get presets for host build_presets = ( - presets["plugins"] - .get(host_name, {}) + presets.get(host_name, {}) .get("workfile_build") + .get("profiles") ) if not build_presets: return diff --git a/pype/lib/deprecated.py b/pype/lib/deprecated.py deleted file mode 100644 index e7296f67ef..0000000000 --- a/pype/lib/deprecated.py +++ /dev/null @@ -1,26 +0,0 @@ -import os - -from avalon import io - - -def get_avalon_database(): - """Mongo database used in avalon's io. - - * Function is not used in pype 3.0 where was replaced with usage of - AvalonMongoDB. - """ - if io._database is None: - set_io_database() - return io._database - - -def set_io_database(): - """Set avalon's io context with environemnts. - - * Function is not used in pype 3.0 where was replaced with usage of - AvalonMongoDB. - """ - required_keys = ["AVALON_PROJECT", "AVALON_ASSET", "AVALON_SILO"] - for key in required_keys: - os.environ[key] = os.environ.get(key, "") - io.install() diff --git a/pype/lib/plugin_tools.py b/pype/lib/plugin_tools.py index 0b6ace807e..a78c9e525e 100644 --- a/pype/lib/plugin_tools.py +++ b/pype/lib/plugin_tools.py @@ -4,7 +4,7 @@ import os import inspect import logging -from ..api import config +from ..api import get_project_settings log = logging.getLogger(__name__) @@ -25,7 +25,7 @@ def filter_pyblish_plugins(plugins): host = api.current_host() - presets = config.get_presets().get('plugins', {}) + presets = get_project_settings(os.environ['AVALON_PROJECT']) or {} # iterate over plugins for plugin in plugins[:]: @@ -53,7 +53,7 @@ def filter_pyblish_plugins(plugins): log.info('removing plugin {}'.format(plugin.__name__)) plugins.remove(plugin) else: - log.info('setting {}:{} on plugin {}'.format( + log.info('setting XXX {}:{} on plugin {}'.format( option, value, plugin.__name__)) setattr(plugin, option, value) diff --git a/pype/modules/ftrack/actions/action_application_loader.py b/pype/modules/ftrack/actions/action_application_loader.py deleted file mode 100644 index 8749f89555..0000000000 --- a/pype/modules/ftrack/actions/action_application_loader.py +++ /dev/null @@ -1,104 +0,0 @@ -import os -import toml -import time -from pype.modules.ftrack.lib import AppAction -from avalon import lib -from pype.api import Logger, config - -log = Logger().get_logger(__name__) - - -def registerApp(app, session, plugins_presets): - name = app['name'] - variant = "" - try: - variant = app['name'].split("_")[1] - except Exception: - pass - - abspath = lib.which_app(app['name']) - if abspath is None: - log.error( - "'{0}' - App don't have config toml file".format(app['name']) - ) - return - - apptoml = toml.load(abspath) - - ''' REQUIRED ''' - executable = apptoml['executable'] - - ''' OPTIONAL ''' - label = apptoml.get('ftrack_label', app.get('label', name)) - icon = apptoml.get('ftrack_icon', None) - description = apptoml.get('description', None) - preactions = apptoml.get('preactions', []) - - if icon: - icon = icon.format(os.environ.get('PYPE_STATICS_SERVER', '')) - - # register action - AppAction( - session, label, name, executable, variant, - icon, description, preactions, plugins_presets - ).register() - - if not variant: - log.info('- Variant is not set') - - -def register(session, plugins_presets={}): - from pype.lib import env_value_to_bool - if env_value_to_bool("PYPE_USE_APP_MANAGER", default=False): - return - - app_usages = ( - config.get_presets() - .get("global", {}) - .get("applications") - ) or {} - - apps = [] - missing_app_names = [] - launchers_path = os.path.join(os.environ["PYPE_CONFIG"], "launchers") - for file in os.listdir(launchers_path): - filename, ext = os.path.splitext(file) - if ext.lower() != ".toml": - continue - - app_usage = app_usages.get(filename) - if not app_usage: - if app_usage is None: - missing_app_names.append(filename) - continue - - loaded_data = toml.load(os.path.join(launchers_path, file)) - app_data = { - "name": filename, - "label": loaded_data.get("label", filename) - } - apps.append(app_data) - - if missing_app_names: - log.debug( - "Apps not defined in applications usage. ({})".format( - ", ".join(( - "\"{}\"".format(app_name) - for app_name in missing_app_names - )) - ) - ) - - apps = sorted(apps, key=lambda app: app["name"]) - app_counter = 0 - for app in apps: - try: - registerApp(app, session, plugins_presets) - if app_counter % 5 == 0: - time.sleep(0.1) - app_counter += 1 - except Exception as exc: - log.warning( - "\"{}\" - not a proper App ({})".format(app['name'], str(exc)), - exc_info=True - ) diff --git a/pype/modules/ftrack/actions/action_applications.py b/pype/modules/ftrack/actions/action_applications.py index a75b581ce3..cf047a658d 100644 --- a/pype/modules/ftrack/actions/action_applications.py +++ b/pype/modules/ftrack/actions/action_applications.py @@ -1,7 +1,6 @@ import os from uuid import uuid4 -from pype.api import config from pype.modules.ftrack.lib import BaseAction from pype.lib import ( ApplicationManager, @@ -205,55 +204,6 @@ class AppplicationsAction(BaseAction): "message": msg } - # TODO Move to prelaunch/afterlaunch hooks - # TODO change to settings - # Change status of task to In progress - presets = config.get_presets()["ftrack"]["ftrack_config"] - - if "status_update" in presets: - statuses = presets["status_update"] - - actual_status = entity["status"]["name"].lower() - already_tested = [] - ent_path = "/".join( - [ent["name"] for ent in entity["link"]] - ) - while True: - next_status_name = None - for key, value in statuses.items(): - if key in already_tested: - continue - if actual_status in value or "_any_" in value: - if key != "_ignore_": - next_status_name = key - already_tested.append(key) - break - already_tested.append(key) - - if next_status_name is None: - break - - try: - query = "Status where name is \"{}\"".format( - next_status_name - ) - status = session.query(query).one() - - entity["status"] = status - session.commit() - self.log.debug("Changing status to \"{}\" <{}>".format( - next_status_name, ent_path - )) - break - - except Exception: - session.rollback() - msg = ( - "Status \"{}\" in presets wasn't found" - " on Ftrack entity type \"{}\"" - ).format(next_status_name, entity.entity_type) - self.log.warning(msg) - return { "success": True, "message": "Launching {0}".format(self.label) @@ -261,7 +211,5 @@ class AppplicationsAction(BaseAction): def register(session, plugins_presets=None): - '''Register action. Called when used as an event plugin.''' - from pype.lib import env_value_to_bool - if env_value_to_bool("PYPE_USE_APP_MANAGER", default=False): - AppplicationsAction(session, plugins_presets).register() + """Register action. Called when used as an event plugin.""" + AppplicationsAction(session, plugins_presets).register() diff --git a/pype/modules/ftrack/actions/action_create_cust_attrs.py b/pype/modules/ftrack/actions/action_create_cust_attrs.py index 11931a5b30..68fae16382 100644 --- a/pype/modules/ftrack/actions/action_create_cust_attrs.py +++ b/pype/modules/ftrack/actions/action_create_cust_attrs.py @@ -1,6 +1,4 @@ -import os import collections -import toml import json import arrow import ftrack_api @@ -8,8 +6,8 @@ from pype.modules.ftrack.lib import BaseAction, statics_icon from pype.modules.ftrack.lib.avalon_sync import ( CUST_ATTR_ID_KEY, CUST_ATTR_GROUP, default_custom_attributes_definition ) -from pype.api import config -from pype.lib import ApplicationManager, env_value_to_bool +from pype.api import get_system_settings +from pype.lib import ApplicationManager """ This action creates/updates custom attributes. @@ -146,9 +144,6 @@ class CustomAttributes(BaseAction): "text", "boolean", "date", "enumerator", "dynamic enumerator", "number" ) - # Pype 3 features - use_app_manager = env_value_to_bool("PYPE_USE_APP_MANAGER", default=False) - app_manager = None def discover(self, session, entities, event): ''' @@ -171,8 +166,7 @@ class CustomAttributes(BaseAction): }) session.commit() - if self.use_app_manager: - self.app_manager = ApplicationManager() + self.app_manager = ApplicationManager() try: self.prepare_global_data(session) @@ -217,15 +211,12 @@ class CustomAttributes(BaseAction): self.groups = {} - self.presets = config.get_presets() + self.ftrack_settings = get_system_settings()["modules"]["Ftrack"] self.attrs_presets = self.prepare_attribute_pressets() def prepare_attribute_pressets(self): output = {} - - attr_presets = ( - self.presets.get("ftrack", {}).get("ftrack_custom_attributes") - ) or {} + attr_presets = self.ftrack_settings["custom_attributes"] for entity_type, preset in attr_presets.items(): # Lower entity type entity_type = entity_type.lower() @@ -391,54 +382,8 @@ class CustomAttributes(BaseAction): app_definitions.append({"empty": "< Empty >"}) return app_definitions - def application_definitions(self): - app_usages = self.presets.get("global", {}).get("applications") or {} - - app_definitions = [] - launchers_path = os.path.join(os.environ["PYPE_CONFIG"], "launchers") - - missing_app_names = [] - for file in os.listdir(launchers_path): - app_name, ext = os.path.splitext(file) - if ext.lower() != ".toml": - continue - - if not app_usages.get(app_name): - missing_app_names.append(app_name) - continue - - loaded_data = toml.load(os.path.join(launchers_path, file)) - - ftrack_label = loaded_data.get("ftrack_label") - if ftrack_label: - parts = app_name.split("_") - if len(parts) > 1: - ftrack_label = " ".join((ftrack_label, parts[-1])) - else: - ftrack_label = loaded_data.get("label", app_name) - - app_definitions.append({app_name: ftrack_label}) - - if missing_app_names: - self.log.warning( - "Apps not defined in applications usage. ({})".format( - ", ".join(( - "\"{}\"".format(app_name) - for app_name in missing_app_names - )) - ) - ) - - # Make sure there is at least one item - if not app_definitions: - app_definitions.append({"empty": "< Empty >"}) - return app_definitions - def applications_attribute(self, event): - if self.use_app_manager: - apps_data = self.app_defs_from_app_manager() - else: - apps_data = self.application_definitions() + apps_data = self.app_defs_from_app_manager() applications_custom_attr_data = { "label": "Applications", @@ -453,28 +398,13 @@ class CustomAttributes(BaseAction): } self.process_attr_data(applications_custom_attr_data, event) - def tools_from_app_manager(self): + def tools_attribute(self, event): tools_data = [] for tool_name, tool in self.app_manager.tools.items(): if tool.enabled: tools_data.append({ tool_name: tool_name }) - return tools_data - - def tools_data(self): - tool_usages = self.presets.get("global", {}).get("tools") or {} - tools_data = [] - for tool_name, usage in tool_usages.items(): - if usage: - tools_data.append({tool_name: tool_name}) - return tools_data - - def tools_attribute(self, event): - if self.use_app_manager: - tools_data = self.tools_from_app_manager() - else: - tools_data = self.tools_data() # Make sure there is at least one item if not tools_data: @@ -494,12 +424,7 @@ class CustomAttributes(BaseAction): self.process_attr_data(tools_custom_attr_data, event) def intent_attribute(self, event): - intent_key_values = ( - self.presets - .get("global", {}) - .get("intent", {}) - .get("items", {}) - ) or {} + intent_key_values = self.ftrack_settings["intent"]["items"] intent_values = [] for key, label in intent_key_values.items(): @@ -805,6 +730,9 @@ class CustomAttributes(BaseAction): return default err_msg = 'Default value is not' if type == 'number': + if isinstance(default, (str)) and default.isnumeric(): + default = float(default) + if not isinstance(default, (float, int)): raise CustAttrException('{} integer'.format(err_msg)) elif type == 'text': diff --git a/pype/modules/ftrack/actions/action_create_folders.py b/pype/modules/ftrack/actions/action_create_folders.py index e689e0260c..a131a0e35b 100644 --- a/pype/modules/ftrack/actions/action_create_folders.py +++ b/pype/modules/ftrack/actions/action_create_folders.py @@ -1,7 +1,11 @@ import os from pype.modules.ftrack.lib import BaseAction, statics_icon from avalon import lib as avalonlib -from pype.api import config, Anatomy +from pype.api import ( + Anatomy, + get_project_settings +) +from pype.lib import ApplicationManager class CreateFolders(BaseAction): @@ -93,6 +97,7 @@ class CreateFolders(BaseAction): all_entities = self.get_notask_children(entity) anatomy = Anatomy(project_name) + project_settings = get_project_settings(project_name) work_keys = ["work", "folder"] work_template = anatomy.templates @@ -106,10 +111,13 @@ class CreateFolders(BaseAction): publish_template = publish_template[key] publish_has_apps = "{app" in publish_template - presets = config.get_presets() - app_presets = presets.get("tools", {}).get("sw_folders") - cached_apps = {} + tools_settings = project_settings["global"]["tools"] + app_presets = tools_settings["Workfiles"]["sw_folders"] + app_manager_apps = None + if app_presets and (work_has_apps or publish_has_apps): + app_manager_apps = ApplicationManager().applications + cached_apps = {} collected_paths = [] for entity in all_entities: if entity.entity_type.lower() == "project": @@ -140,18 +148,20 @@ class CreateFolders(BaseAction): task_data["task"] = child["name"] apps = [] - if app_presets and (work_has_apps or publish_has_apps): - possible_apps = app_presets.get(task_type_name, []) - for app in possible_apps: - if app in cached_apps: - app_dir = cached_apps[app] + if app_manager_apps: + possible_apps = app_presets.get(task_type_name) or [] + for app_name in possible_apps: + + if app_name in cached_apps: + apps.append(cached_apps[app_name]) + continue + + app_def = app_manager_apps.get(app_name) + if app_def and app_def.is_host: + app_dir = app_def.host_name else: - try: - app_data = avalonlib.get_application(app) - app_dir = app_data["application_dir"] - except ValueError: - app_dir = app - cached_apps[app] = app_dir + app_dir = app_name + cached_apps[app_name] = app_dir apps.append(app_dir) # Template wok diff --git a/pype/modules/ftrack/actions/action_create_project_structure.py b/pype/modules/ftrack/actions/action_create_project_structure.py index 22190c16db..0815f82a69 100644 --- a/pype/modules/ftrack/actions/action_create_project_structure.py +++ b/pype/modules/ftrack/actions/action_create_project_structure.py @@ -2,7 +2,7 @@ import os import re from pype.modules.ftrack.lib import BaseAction, statics_icon -from pype.api import config, Anatomy +from pype.api import Anatomy, get_project_settings class CreateProjectFolders(BaseAction): @@ -69,25 +69,26 @@ class CreateProjectFolders(BaseAction): return True def launch(self, session, entities, event): - entity = entities[0] - project = self.get_project_from_entity(entity) - project_folder_presets = ( - config.get_presets() - .get("tools", {}) - .get("project_folder_structure") + # Get project entity + project_entity = self.get_project_from_entity(entities[0]) + # Load settings for project + project_name = project_entity["full_name"] + project_settings = get_project_settings(project_name) + project_folder_structure = ( + project_settings["global"]["project_folder_structure"] ) - if not project_folder_presets: + if not project_folder_structure: return { "success": False, - "message": "Project structure presets are not set." + "message": "Project structure is not set." } try: # Get paths based on presets - basic_paths = self.get_path_items(project_folder_presets) - anatomy = Anatomy(project["full_name"]) - self.create_folders(basic_paths, entity, project, anatomy) - self.create_ftrack_entities(basic_paths, project) + basic_paths = self.get_path_items(project_folder_structure) + anatomy = Anatomy(project_entity["full_name"]) + self.create_folders(basic_paths, project_entity, anatomy) + self.create_ftrack_entities(basic_paths, project_entity) except Exception as exc: session.rollback() @@ -219,7 +220,7 @@ class CreateProjectFolders(BaseAction): output.append(os.path.normpath(os.path.sep.join(clean_items))) return output - def create_folders(self, basic_paths, entity, project, anatomy): + def create_folders(self, basic_paths, project, anatomy): roots_paths = [] if isinstance(anatomy.roots, dict): for root in anatomy.roots: diff --git a/pype/modules/ftrack/actions/action_rv.py b/pype/modules/ftrack/actions/action_rv.py index f9aeb87f71..eeb5672047 100644 --- a/pype/modules/ftrack/actions/action_rv.py +++ b/pype/modules/ftrack/actions/action_rv.py @@ -3,7 +3,6 @@ import subprocess import traceback import json -from pype.api import config from pype.modules.ftrack.lib import BaseAction, statics_icon import ftrack_api from avalon import io, api @@ -11,7 +10,6 @@ from avalon import io, api class RVAction(BaseAction): """ Launch RV action """ - ignore_me = "rv" not in config.get_presets() identifier = "rv.launch.action" label = "rv" description = "rv Launcher" @@ -19,6 +17,8 @@ class RVAction(BaseAction): type = 'Application' + allowed_types = ["img", "mov", "exr", "mp4"] + def __init__(self, session, plugins_presets): """ Constructor @@ -26,36 +26,30 @@ class RVAction(BaseAction): :type session: :class:`ftrack_api.Session` """ super().__init__(session, plugins_presets) - self.rv_path = None - self.config_data = None + + # QUESTION load RV application data from AppplicationManager? + rv_path = None # RV_HOME should be set if properly installed if os.environ.get('RV_HOME'): - self.rv_path = os.path.join( + rv_path = os.path.join( os.environ.get('RV_HOME'), 'bin', 'rv' ) - else: - # if not, fallback to config file location - if "rv" in config.get_presets(): - self.config_data = config.get_presets()['rv']['config'] - self.set_rv_path() + if not os.path.exists(rv_path): + rv_path = None - if self.rv_path is None: - return + if not rv_path: + self.log.info("RV path was not found.") + self.ignore_me = True - self.allowed_types = self.config_data.get( - 'file_ext', ["img", "mov", "exr", "mp4"] - ) + self.rv_path = rv_path def discover(self, session, entities, event): """Return available actions based on *event*. """ return True - def set_rv_path(self): - self.rv_path = self.config_data.get("rv_path") - def preregister(self): if self.rv_path is None: return ( diff --git a/pype/modules/ftrack/events/event_user_assigment.py b/pype/modules/ftrack/events/event_user_assigment.py index 19a67b745f..9b0dfe84d1 100644 --- a/pype/modules/ftrack/events/event_user_assigment.py +++ b/pype/modules/ftrack/events/event_user_assigment.py @@ -8,7 +8,7 @@ from avalon.api import AvalonMongoDB from bson.objectid import ObjectId -from pype.api import config, Anatomy +from pype.api import Anatomy, get_project_settings class UserAssigmentEvent(BaseEvent): @@ -173,26 +173,50 @@ class UserAssigmentEvent(BaseEvent): return t_data def launch(self, session, event): - # load shell scripts presets - presets = config.get_presets()['ftrack'].get("user_assigment_event") - if not presets: + if not event.get("data"): return - for entity in event.get('data', {}).get('entities', []): - if entity.get('entity_type') != 'Appointment': + + entities_info = event["data"].get("entities") + if not entities_info: + return + + # load shell scripts presets + tmp_by_project_name = {} + for entity_info in entities_info: + if entity_info.get('entity_type') != 'Appointment': continue - task, user = self._get_task_and_user(session, - entity.get('action'), - entity.get('changes')) + task_entity, user_entity = self._get_task_and_user( + session, + entity_info.get('action'), + entity_info.get('changes') + ) - if not task or not user: - self.log.error( - 'Task or User was not found.') + if not task_entity or not user_entity: + self.log.error("Task or User was not found.") continue - data = self._get_template_data(task) # format directories to pass to shell script - anatomy = Anatomy(data["project"]["name"]) + project_name = task_entity["project"]["full_name"] + project_data = tmp_by_project_name.get(project_name) or {} + if "scripts_by_action" not in project_data: + project_settings = get_project_settings(project_name) + _settings = ( + project_settings["ftrack"]["events"]["user_assignment"] + ) + project_data["scripts_by_action"] = _settings.get("scripts") + tmp_by_project_name[project_name] = project_data + + scripts_by_action = project_data["scripts_by_action"] + if not scripts_by_action: + continue + + if "anatomy" not in project_data: + project_data["anatomy"] = Anatomy(project_name) + tmp_by_project_name[project_name] = project_data + + anatomy = project_data["anatomy"] + data = self._get_template_data(task_entity) anatomy_filled = anatomy.format(data) # formatting work dir is easiest part as we can use whole path work_dir = anatomy_filled["work"]["folder"] @@ -201,8 +225,10 @@ class UserAssigmentEvent(BaseEvent): publish = anatomy_filled["publish"]["folder"] # now find path to {asset} - m = re.search("(^.+?{})".format(data['asset']), - publish) + m = re.search( + "(^.+?{})".format(data["asset"]), + publish + ) if not m: msg = 'Cannot get part of publish path {}'.format(publish) @@ -213,12 +239,13 @@ class UserAssigmentEvent(BaseEvent): } publish_dir = m.group(1) - for script in presets.get(entity.get('action')): - self.log.info( - '[{}] : running script for user {}'.format( - entity.get('action'), user["username"])) - self._run_script(script, [user["username"], - work_dir, publish_dir]) + username = user_entity["username"] + event_entity_action = entity_info["action"] + for script in scripts_by_action.get(event_entity_action): + self.log.info(( + "[{}] : running script for user {}" + ).format(event_entity_action, username)) + self._run_script(script, [username, work_dir, publish_dir]) return True diff --git a/pype/modules/ftrack/events/event_version_to_task_statuses.py b/pype/modules/ftrack/events/event_version_to_task_statuses.py index fdb48cbc37..0ea72be1cb 100644 --- a/pype/modules/ftrack/events/event_version_to_task_statuses.py +++ b/pype/modules/ftrack/events/event_version_to_task_statuses.py @@ -1,12 +1,8 @@ from pype.modules.ftrack import BaseEvent -from pype.api import config +from pype.api import get_project_settings class VersionToTaskStatus(BaseEvent): - - # Presets usage - default_status_mapping = {} - def launch(self, session, event): '''Propagates status from version to task when changed''' @@ -48,14 +44,19 @@ class VersionToTaskStatus(BaseEvent): version_status_orig = version_status["name"] + # Get entities necessary for processing + version = session.get("AssetVersion", entity["entityId"]) + task = version.get("task") + if not task: + continue + + project_entity = self.get_project_from_entity(task) + project_name = project_entity["full_name"] + project_settings = get_project_settings(project_name) + # Load status mapping from presets status_mapping = ( - config.get_presets() - .get("ftrack", {}) - .get("ftrack_config", {}) - .get("status_version_to_task") - ) or self.default_status_mapping - + project_settings["ftrack"]["events"]["status_version_to_task"]) # Skip if mapping is empty if not status_mapping: continue @@ -78,16 +79,10 @@ class VersionToTaskStatus(BaseEvent): # Lower all names from presets new_status_names = [name.lower() for name in new_status_names] - # Get entities necessary for processing - version = session.get("AssetVersion", entity["entityId"]) - task = version.get("task") - if not task: - continue - if version["asset"]["type"]["short"].lower() == "scene": continue - project_schema = task["project"]["project_schema"] + project_schema = project_entity["project_schema"] # Get all available statuses for Task statuses = project_schema.get_statuses("Task", task["type_id"]) # map lowered status name with it's object diff --git a/pype/modules/ftrack/ftrack_server/ftrack_server.py b/pype/modules/ftrack/ftrack_server/ftrack_server.py index 92f3c0b3a0..ec39bdc50f 100644 --- a/pype/modules/ftrack/ftrack_server/ftrack_server.py +++ b/pype/modules/ftrack/ftrack_server/ftrack_server.py @@ -2,11 +2,13 @@ import os import sys import types import importlib -import ftrack_api import time import logging import inspect -from pype.api import Logger, config + +import ftrack_api + +from pype.api import Logger log = Logger().get_logger(__name__) @@ -109,9 +111,8 @@ class FtrackServer: key = "user" if self.server_type.lower() == "event": key = "server" - plugins_presets = config.get_presets().get( - "ftrack", {} - ).get("plugins", {}).get(key, {}) + # TODO replace with settings or get rid of passing the dictionary + plugins_presets = {} function_counter = 0 for function_dict in register_functions_dict: diff --git a/pype/modules/ftrack/ftrack_server/sub_event_processor.py b/pype/modules/ftrack/ftrack_server/sub_event_processor.py index 4a3241dd4f..c719c8fd08 100644 --- a/pype/modules/ftrack/ftrack_server/sub_event_processor.py +++ b/pype/modules/ftrack/ftrack_server/sub_event_processor.py @@ -9,7 +9,7 @@ from pype.modules.ftrack.ftrack_server.lib import ( SocketSession, ProcessEventHub, TOPIC_STATUS_SERVER ) import ftrack_api -from pype.api import Logger, config +from pype.api import Logger log = Logger().get_logger("Event processor") @@ -56,32 +56,16 @@ def register(session): def clockify_module_registration(): - module_name = "Clockify" - - menu_items = config.get_presets()["tray"]["menu_items"] - if not menu_items["item_usage"][module_name]: - return - api_key = os.environ.get("CLOCKIFY_API_KEY") if not api_key: log.warning("Clockify API key is not set.") return workspace_name = os.environ.get("CLOCKIFY_WORKSPACE") - if not workspace_name: - workspace_name = ( - menu_items - .get("attributes", {}) - .get(module_name, {}) - .get("workspace_name", {}) - ) - if not workspace_name: log.warning("Clockify Workspace is not set.") return - os.environ["CLOCKIFY_WORKSPACE"] = workspace_name - from pype.modules.clockify.constants import CLOCKIFY_FTRACK_SERVER_PATH current = os.environ.get("FTRACK_EVENTS_PATH") or "" diff --git a/pype/modules/ftrack/lib/__init__.py b/pype/modules/ftrack/lib/__init__.py index a52e73d10f..3890eacf90 100644 --- a/pype/modules/ftrack/lib/__init__.py +++ b/pype/modules/ftrack/lib/__init__.py @@ -3,7 +3,6 @@ from . import credentials from .ftrack_base_handler import BaseHandler from .ftrack_event_handler import BaseEvent from .ftrack_action_handler import BaseAction, ServerAction, statics_icon -from .ftrack_app_handler import AppAction __all__ = ( "avalon_sync", @@ -12,6 +11,5 @@ __all__ = ( "BaseEvent", "BaseAction", "ServerAction", - "statics_icon", - "AppAction" + "statics_icon" ) diff --git a/pype/modules/ftrack/lib/avalon_sync.py b/pype/modules/ftrack/lib/avalon_sync.py index 97116317af..0716a5975f 100644 --- a/pype/modules/ftrack/lib/avalon_sync.py +++ b/pype/modules/ftrack/lib/avalon_sync.py @@ -8,19 +8,13 @@ import copy from avalon.api import AvalonMongoDB import avalon -import avalon.api -from avalon.vendor import toml -from pype.api import Logger, Anatomy +from pype.api import Logger, Anatomy, get_anatomy_data from bson.objectid import ObjectId from bson.errors import InvalidId from pymongo import UpdateOne import ftrack_api -from pype.api import config -from pype.lib import ( - ApplicationManager, - env_value_to_bool -) +from pype.lib import ApplicationManager log = Logger().get_logger(__name__) @@ -178,56 +172,29 @@ def get_avalon_project_template(project_name): def get_project_apps(in_app_list): - """ - Returns metadata information about apps in 'in_app_list' enhanced - from toml files. + """ Application definitions for app name. + Args: in_app_list: (list) - names of applications Returns: - tuple (list, dictionary) - list of dictionaries about apps - dictionary of warnings + tuple (list, dictionary) - list of dictionaries with apps definitions + dictionary of warnings """ apps = [] warnings = collections.defaultdict(list) - if env_value_to_bool("PYPE_USE_APP_MANAGER", default=False): - missing_app_msg = "Missing definition of application" - application_manager = ApplicationManager() - for app_name in in_app_list: - app = application_manager.applications.get(app_name) - if app: - apps.append({ - "name": app_name, - "label": app.full_label - }) - else: - warnings[missing_app_msg].append(app_name) - return apps, warnings - - # TODO report - missing_toml_msg = "Missing config file for application" - error_msg = ( - "Unexpected error happend during preparation of application" - ) - - for app in in_app_list: - try: - toml_path = avalon.lib.which_app(app) - if not toml_path: - log.warning(missing_toml_msg + ' "{}"'.format(app)) - warnings[missing_toml_msg].append(app) - continue - + missing_app_msg = "Missing definition of application" + application_manager = ApplicationManager() + for app_name in in_app_list: + app = application_manager.applications.get(app_name) + if app: apps.append({ - "name": app, - "label": toml.load(toml_path)["label"] + "name": app_name, + "label": app.full_label }) - except Exception: - warnings[error_msg].append(app) - log.warning(( - "Error has happened during preparing application \"{}\"" - ).format(app), exc_info=True) + else: + warnings[missing_app_msg].append(app_name) return apps, warnings @@ -308,28 +275,6 @@ def get_hierarchical_attributes(session, entity, attr_names, attr_defaults={}): return hier_values -def get_task_short_name(task_type): - """ - Returns short name (code) for 'task_type'. Short name stored in - metadata dictionary in project.config per each 'task_type'. - Could be used in anatomy, paths etc. - If no appropriate short name is found in mapping, 'task_type' is - returned back unchanged. - - Currently stores data in: - 'pype-config/presets/ftrack/project_defaults.json' - Args: - task_type: (string) - Animation | Modeling ... - - Returns: - (string) - anim | model ... - """ - presets = config.get_presets()['ftrack']['project_defaults']\ - .get("task_short_names") - - return presets.get(task_type, task_type) - - class SyncEntitiesFactory: dbcon = AvalonMongoDB() @@ -1150,6 +1095,13 @@ class SyncEntitiesFactory: ) def prepare_ftrack_ent_data(self): + project_name = self.entities_dict[self.ft_project_id]["name"] + project_anatomy_data = get_anatomy_data(project_name) + + task_type_mapping = ( + project_anatomy_data["attributes"]["task_short_names"] + ) + not_set_ids = [] for id, entity_dict in self.entities_dict.items(): entity = entity_dict["entity"] @@ -1186,10 +1138,12 @@ class SyncEntitiesFactory: continue self.report_items["warning"][msg] = items tasks = {} - for tt in task_types: - tasks[tt["name"]] = { - "short_name": get_task_short_name(tt["name"]) - } + for task_type in task_types: + task_type_name = task_type["name"] + short_name = task_type_mapping.get(task_type_name) + tasks[task_type_name] = { + "short_name": short_name or task_type_name + } self.entities_dict[id]["final_entity"]["config"] = { "tasks": tasks, "apps": proj_apps diff --git a/pype/modules/ftrack/lib/ftrack_app_handler.py b/pype/modules/ftrack/lib/ftrack_app_handler.py deleted file mode 100644 index 23776aced7..0000000000 --- a/pype/modules/ftrack/lib/ftrack_app_handler.py +++ /dev/null @@ -1,223 +0,0 @@ -from pype import lib as pypelib -from pype.api import config -from .ftrack_action_handler import BaseAction - - -class AppAction(BaseAction): - """Application Action class. - - Args: - session (ftrack_api.Session): Session where action will be registered. - label (str): A descriptive string identifing your action. - varaint (str, optional): To group actions together, give them the same - label and specify a unique variant per action. - identifier (str): An unique identifier for app. - description (str): A verbose descriptive text for you action. - icon (str): Url path to icon which will be shown in Ftrack web. - """ - - type = "Application" - preactions = ["start.timer"] - - def __init__( - self, session, label, name, executable, variant=None, - icon=None, description=None, preactions=[], plugins_presets={} - ): - self.label = label - self.identifier = name - self.executable = executable - self.variant = variant - self.icon = icon - self.description = description - self.preactions.extend(preactions) - - super().__init__(session, plugins_presets) - if label is None: - raise ValueError("Action missing label.") - if name is None: - raise ValueError("Action missing identifier.") - if executable is None: - raise ValueError("Action missing executable.") - - def register(self): - """Registers the action, subscribing the discover and launch topics.""" - - discovery_subscription = ( - "topic=ftrack.action.discover and source.user.username={0}" - ).format(self.session.api_user) - - self.session.event_hub.subscribe( - discovery_subscription, - self._discover, - priority=self.priority - ) - - launch_subscription = ( - "topic=ftrack.action.launch" - " and data.actionIdentifier={0}" - " and source.user.username={1}" - ).format( - self.identifier, - self.session.api_user - ) - self.session.event_hub.subscribe( - launch_subscription, - self._launch - ) - - def discover(self, session, entities, event): - """Return true if we can handle the selected entities. - - Args: - session (ftrack_api.Session): Helps to query necessary data. - entities (list): Object of selected entities. - event (ftrack_api.Event): Ftrack event causing discover callback. - """ - - if ( - len(entities) != 1 - or entities[0].entity_type.lower() != "task" - ): - return False - - entity = entities[0] - if entity["parent"].entity_type.lower() == "project": - return False - - avalon_project_apps = event["data"].get("avalon_project_apps", None) - avalon_project_doc = event["data"].get("avalon_project_doc", None) - if avalon_project_apps is None: - if avalon_project_doc is None: - ft_project = self.get_project_from_entity(entity) - database = pypelib.get_avalon_database() - project_name = ft_project["full_name"] - avalon_project_doc = database[project_name].find_one({ - "type": "project" - }) or False - event["data"]["avalon_project_doc"] = avalon_project_doc - - if not avalon_project_doc: - return False - - project_apps_config = avalon_project_doc["config"].get("apps", []) - avalon_project_apps = [ - app["name"] for app in project_apps_config - ] or False - event["data"]["avalon_project_apps"] = avalon_project_apps - - if not avalon_project_apps: - return False - - return self.identifier in avalon_project_apps - - def _launch(self, event): - entities = self._translate_event(event) - - preactions_launched = self._handle_preactions( - self.session, event - ) - if preactions_launched is False: - return - - response = self.launch(self.session, entities, event) - - return self._handle_result(response) - - def launch(self, session, entities, event): - """Callback method for the custom action. - - return either a bool (True if successful or False if the action failed) - or a dictionary with they keys `message` and `success`, the message - should be a string and will be displayed as feedback to the user, - success should be a bool, True if successful or False if the action - failed. - - *session* is a `ftrack_api.Session` instance - - *entities* is a list of tuples each containing the entity type and - the entity id. If the entity is a hierarchical you will always get - the entity type TypedContext, once retrieved through a get operation - you will have the "real" entity type ie. example Shot, Sequence - or Asset Build. - - *event* the unmodified original event - """ - - entity = entities[0] - - task_name = entity["name"] - asset_name = entity["parent"]["name"] - project_name = entity["project"]["full_name"] - try: - pypelib.launch_application( - project_name, asset_name, task_name, self.identifier - ) - - except pypelib.ApplicationLaunchFailed as exc: - self.log.error(str(exc)) - return { - "success": False, - "message": str(exc) - } - - except Exception: - msg = "Unexpected failure of application launch {}".format( - self.label - ) - self.log.error(msg, exc_info=True) - return { - "success": False, - "message": msg - } - - # Change status of task to In progress - presets = config.get_presets()["ftrack"]["ftrack_config"] - - if "status_update" in presets: - statuses = presets["status_update"] - - actual_status = entity["status"]["name"].lower() - already_tested = [] - ent_path = "/".join( - [ent["name"] for ent in entity["link"]] - ) - while True: - next_status_name = None - for key, value in statuses.items(): - if key in already_tested: - continue - if actual_status in value or "_any_" in value: - if key != "_ignore_": - next_status_name = key - already_tested.append(key) - break - already_tested.append(key) - - if next_status_name is None: - break - - try: - query = "Status where name is \"{}\"".format( - next_status_name - ) - status = session.query(query).one() - - entity["status"] = status - session.commit() - self.log.debug("Changing status to \"{}\" <{}>".format( - next_status_name, ent_path - )) - break - - except Exception: - session.rollback() - msg = ( - "Status \"{}\" in presets wasn't found" - " on Ftrack entity type \"{}\"" - ).format(next_status_name, entity.entity_type) - self.log.warning(msg) - - return { - "success": True, - "message": "Launching {0}".format(self.label) - } diff --git a/pype/plugin.py b/pype/plugin.py index a169e82beb..1b769cd1f1 100644 --- a/pype/plugin.py +++ b/pype/plugin.py @@ -2,7 +2,7 @@ import tempfile import os import pyblish.api -from pype.api import config +from pype.api import get_project_settings import inspect ValidatePipelineOrder = pyblish.api.ValidatorOrder + 0.05 @@ -24,12 +24,14 @@ def imprint_attributes(plugin): plugin_host = file.split(os.path.sep)[-3:-2][0] plugin_name = type(plugin).__name__ try: - config_data = config.get_presets()['plugins'][plugin_host][plugin_kind][plugin_name] # noqa: E501 + settings = get_project_settings(os.environ['AVALON_PROJECT']) + settings_data = settings[plugin_host][plugin_kind][plugin_name] # noqa: E501 + print(settings_data) except KeyError: print("preset not found") return - for option, value in config_data.items(): + for option, value in settings_data.items(): if option == "enabled" and value is False: setattr(plugin, "active", False) else: diff --git a/pype/plugins/global/publish/collect_presets.py b/pype/plugins/global/publish/collect_presets.py index 4ffb2fc0b3..95fb4dbfad 100644 --- a/pype/plugins/global/publish/collect_presets.py +++ b/pype/plugins/global/publish/collect_presets.py @@ -8,7 +8,7 @@ Provides: """ from pyblish import api -from pype.api import config +from pype.api import get_current_project_settings class CollectPresets(api.ContextPlugin): @@ -18,23 +18,7 @@ class CollectPresets(api.ContextPlugin): label = "Collect Presets" def process(self, context): - presets = config.get_presets() - try: - # try if it is not in projects custom directory - # `{PYPE_PROJECT_CONFIGS}/[PROJECT_NAME]/init.json` - # init.json define preset names to be used - p_init = presets["init"] - presets["colorspace"] = presets["colorspace"][p_init["colorspace"]] - presets["dataflow"] = presets["dataflow"][p_init["dataflow"]] - except KeyError: - self.log.warning("No projects custom preset available...") - presets["colorspace"] = presets["colorspace"]["default"] - presets["dataflow"] = presets["dataflow"]["default"] - self.log.info( - "Presets `colorspace` and `dataflow` loaded from `default`..." - ) + project_settings = get_current_project_settings() + context.data["presets"] = project_settings - context.data["presets"] = presets - - # self.log.info(context.data["presets"]) return diff --git a/pype/plugins/maya/create/create_rendersetup.py b/pype/plugins/maya/create/create_rendersetup.py index 98f54f2d70..969c085ea6 100644 --- a/pype/plugins/maya/create/create_rendersetup.py +++ b/pype/plugins/maya/create/create_rendersetup.py @@ -26,10 +26,10 @@ class CreateRenderSetup(avalon.maya.Creator): # \__| | # \_____/ - # from pype.api import config + # from pype.api import get_project_settings # import maya.app.renderSetup.model.renderSetup as renderSetup - # presets = config.get_presets(project=os.environ['AVALON_PROJECT']) - # layer = presets['plugins']['maya']['create']['renderSetup']["layer"] + # settings = get_project_settings(os.environ['AVALON_PROJECT']) + # layer = settings['maya']['create']['renderSetup']["layer"] # rs = renderSetup.instance() # rs.createRenderLayer(layer) diff --git a/pype/plugins/maya/load/load_ass.py b/pype/plugins/maya/load/load_ass.py index ffe70c39e8..9b851a3757 100644 --- a/pype/plugins/maya/load/load_ass.py +++ b/pype/plugins/maya/load/load_ass.py @@ -1,7 +1,7 @@ from avalon import api import pype.hosts.maya.plugin import os -from pype.api import config +from pype.api import get_project_settings import clique @@ -74,8 +74,8 @@ class AssProxyLoader(pype.hosts.maya.plugin.ReferenceLoader): proxyShape.dso.set(path) proxyShape.aiOverrideShaders.set(0) - presets = config.get_presets(project=os.environ['AVALON_PROJECT']) - colors = presets['plugins']['maya']['load']['colors'] + settings = get_project_settings(os.environ['AVALON_PROJECT']) + colors = settings['maya']['load']['colors'] c = colors.get(family) if c is not None: @@ -196,8 +196,8 @@ class AssStandinLoader(api.Loader): label = "{}:{}".format(namespace, name) root = pm.group(name=label, empty=True) - presets = config.get_presets(project=os.environ['AVALON_PROJECT']) - colors = presets['plugins']['maya']['load']['colors'] + settings = get_project_settings(os.environ['AVALON_PROJECT']) + colors = settings['maya']['load']['colors'] c = colors.get('ass') if c is not None: diff --git a/pype/plugins/maya/load/load_gpucache.py b/pype/plugins/maya/load/load_gpucache.py index 9930dbaac6..0b3daae710 100644 --- a/pype/plugins/maya/load/load_gpucache.py +++ b/pype/plugins/maya/load/load_gpucache.py @@ -1,7 +1,7 @@ from avalon import api import pype.hosts.maya.plugin import os -from pype.api import config +from pype.api import get_project_settings reload(config) @@ -35,8 +35,8 @@ class GpuCacheLoader(api.Loader): label = "{}:{}".format(namespace, name) root = cmds.group(name=label, empty=True) - presets = config.get_presets(project=os.environ['AVALON_PROJECT']) - colors = presets['plugins']['maya']['load']['colors'] + settings = get_project_settings(os.environ['AVALON_PROJECT']) + colors = settings['maya']['load']['colors'] c = colors.get('model') if c is not None: cmds.setAttr(root + ".useOutlinerColor", 1) diff --git a/pype/plugins/maya/load/load_reference.py b/pype/plugins/maya/load/load_reference.py index dbb3cc98b2..23b3cedb55 100644 --- a/pype/plugins/maya/load/load_reference.py +++ b/pype/plugins/maya/load/load_reference.py @@ -2,7 +2,7 @@ import pype.hosts.maya.plugin from avalon import api, maya from maya import cmds import os -from pype.api import config +from pype.api import get_project_settings class ReferenceLoader(pype.hosts.maya.plugin.ReferenceLoader): @@ -77,8 +77,8 @@ class ReferenceLoader(pype.hosts.maya.plugin.ReferenceLoader): cmds.setAttr(groupName + ".displayHandle", 1) - presets = config.get_presets(project=os.environ['AVALON_PROJECT']) - colors = presets['plugins']['maya']['load']['colors'] + settings = get_project_settings(os.environ['AVALON_PROJECT']) + colors = settings['maya']['load']['colors'] c = colors.get(family) if c is not None: groupNode.useOutlinerColor.set(1) diff --git a/pype/plugins/maya/load/load_vdb_to_redshift.py b/pype/plugins/maya/load/load_vdb_to_redshift.py index 4893640b27..17c78d7165 100644 --- a/pype/plugins/maya/load/load_vdb_to_redshift.py +++ b/pype/plugins/maya/load/load_vdb_to_redshift.py @@ -1,6 +1,6 @@ from avalon import api import os -from pype.api import config +from pype.api import get_project_settings class LoadVDBtoRedShift(api.Loader): """Load OpenVDB in a Redshift Volume Shape""" @@ -55,8 +55,8 @@ class LoadVDBtoRedShift(api.Loader): label = "{}:{}".format(namespace, name) root = cmds.group(name=label, empty=True) - presets = config.get_presets(project=os.environ['AVALON_PROJECT']) - colors = presets['plugins']['maya']['load']['colors'] + settings = get_project_settings(os.environ['AVALON_PROJECT']) + colors = settings['maya']['load']['colors'] c = colors.get(family) if c is not None: diff --git a/pype/plugins/maya/load/load_vdb_to_vray.py b/pype/plugins/maya/load/load_vdb_to_vray.py index aee0ee026d..2959ef42ec 100644 --- a/pype/plugins/maya/load/load_vdb_to_vray.py +++ b/pype/plugins/maya/load/load_vdb_to_vray.py @@ -1,5 +1,5 @@ from avalon import api -from pype.api import config +from pype.api import get_project_settings import os @@ -48,8 +48,8 @@ class LoadVDBtoVRay(api.Loader): label = "{}:{}".format(namespace, name) root = cmds.group(name=label, empty=True) - presets = config.get_presets(project=os.environ['AVALON_PROJECT']) - colors = presets['plugins']['maya']['load']['colors'] + settings = get_project_settings(os.environ['AVALON_PROJECT']) + colors = settings['maya']['load']['colors'] c = colors.get(family) if c is not None: diff --git a/pype/plugins/maya/load/load_vrayproxy.py b/pype/plugins/maya/load/load_vrayproxy.py index 894ec75c32..73f02b81e4 100644 --- a/pype/plugins/maya/load/load_vrayproxy.py +++ b/pype/plugins/maya/load/load_vrayproxy.py @@ -1,6 +1,6 @@ from avalon.maya import lib from avalon import api -from pype.api import config +from pype.api import get_project_settings import os import maya.cmds as cmds @@ -47,8 +47,8 @@ class VRayProxyLoader(api.Loader): return # colour the group node - presets = config.get_presets(project=os.environ['AVALON_PROJECT']) - colors = presets['plugins']['maya']['load']['colors'] + settings = get_project_settings(os.environ['AVALON_PROJECT']) + colors = settings['maya']['load']['colors'] c = colors.get(family) if c is not None: cmds.setAttr("{0}.useOutlinerColor".format(group_node), 1) diff --git a/pype/plugins/maya/load/load_yeti_cache.py b/pype/plugins/maya/load/load_yeti_cache.py index ef0b5d5efa..19cf3920fe 100644 --- a/pype/plugins/maya/load/load_yeti_cache.py +++ b/pype/plugins/maya/load/load_yeti_cache.py @@ -9,7 +9,7 @@ from maya import cmds from avalon import api, io from avalon.maya import lib as avalon_lib, pipeline from pype.hosts.maya import lib -from pype.api import config +from pype.api import get_project_settings from pprint import pprint @@ -59,8 +59,8 @@ class YetiCacheLoader(api.Loader): group_name = "{}:{}".format(namespace, name) group_node = cmds.group(nodes, name=group_name) - presets = config.get_presets(project=os.environ['AVALON_PROJECT']) - colors = presets['plugins']['maya']['load']['colors'] + settings = get_project_settings(os.environ['AVALON_PROJECT']) + colors = settings['maya']['load']['colors'] c = colors.get(family) if c is not None: diff --git a/pype/plugins/maya/load/load_yeti_rig.py b/pype/plugins/maya/load/load_yeti_rig.py index 0604953198..3a9339c707 100644 --- a/pype/plugins/maya/load/load_yeti_rig.py +++ b/pype/plugins/maya/load/load_yeti_rig.py @@ -1,7 +1,7 @@ import os from collections import defaultdict -from pype.api import config +from pype.api import get_project_settings import pype.hosts.maya.plugin from pype.hosts.maya import lib @@ -77,8 +77,8 @@ class YetiRigLoader(pype.hosts.maya.plugin.ReferenceLoader): groupName = "{}:{}".format(namespace, name) - presets = config.get_presets(project=os.environ['AVALON_PROJECT']) - colors = presets['plugins']['maya']['load']['colors'] + settings = get_project_settings(os.environ['AVALON_PROJECT']) + colors = settings['maya']['load']['colors'] c = colors.get('yetiRig') if c is not None: diff --git a/pype/plugins/maya/publish/extract_camera_mayaScene.py b/pype/plugins/maya/publish/extract_camera_mayaScene.py index 8fce48badf..0443357ba9 100644 --- a/pype/plugins/maya/publish/extract_camera_mayaScene.py +++ b/pype/plugins/maya/publish/extract_camera_mayaScene.py @@ -102,7 +102,8 @@ class ExtractCameraMayaScene(pype.api.Extractor): def process(self, instance): """Plugin entry point.""" # get settings - ext_mapping = instance.context.data["presets"]["maya"].get("ext_mapping") # noqa: E501 + ext_mapping = (instance.context.data["presets"]["maya"] + .get("ext_mapping")) # noqa: E501 if ext_mapping: self.log.info("Looking in presets for scene type ...") # use extension mapping for first family found diff --git a/pype/plugins/maya/publish/submit_maya_muster.py b/pype/plugins/maya/publish/submit_maya_muster.py index ffe434048a..9c67b45721 100644 --- a/pype/plugins/maya/publish/submit_maya_muster.py +++ b/pype/plugins/maya/publish/submit_maya_muster.py @@ -11,7 +11,7 @@ from avalon.vendor import requests import pyblish.api from pype.hosts.maya import lib -from pype.api import config +from pype.api import get_system_settings # mapping between Maya renderer names and Muster template ids @@ -25,10 +25,10 @@ def _get_template_id(renderer): :rtype: int """ - templates = config.get_presets()["muster"]["templates_mapping"] + templates = get_system_settings()["modules"]["Muster"]["templates_mapping"] if not templates: - raise RuntimeError(("Muster template mapping missing in pype-config " - "`presets/muster/templates_mapping.json`")) + raise RuntimeError(("Muster template mapping missing in " + "pype-settings")) try: template_id = templates[renderer] except KeyError: diff --git a/pype/plugins/nuke/load/load_mov.py b/pype/plugins/nuke/load/load_mov.py index d252aaa09d..104f59d5be 100644 --- a/pype/plugins/nuke/load/load_mov.py +++ b/pype/plugins/nuke/load/load_mov.py @@ -4,7 +4,7 @@ import contextlib from avalon import api, io from pype.hosts.nuke import presets -from pype.api import config +from pype.api import get_project_settings @contextlib.contextmanager @@ -73,7 +73,8 @@ def add_review_presets_config(): "families": list(), "representations": list() } - review_presets = config.get_presets()["plugins"]["global"]["publish"].get( + settings = get_project_settings(io.Session["AVALON_PROJECT"]) + review_presets = settings["global"]["publish"].get( "ExtractReview", {}) outputs = review_presets.get("outputs", {}) diff --git a/pype/scripts/otio_burnin.py b/pype/scripts/otio_burnin.py index 8da1dd8616..4f5c290e9d 100644 --- a/pype/scripts/otio_burnin.py +++ b/pype/scripts/otio_burnin.py @@ -5,7 +5,7 @@ import subprocess import platform import json import opentimelineio_contrib.adapters.ffmpeg_burnins as ffmpeg_burnins -from pype.api import config, resources +from pype.api import resources import pype.lib @@ -428,12 +428,6 @@ def burnins_from_data( } """ - # Use legacy processing when options are not set - if options is None or burnin_values is None: - presets = config.get_presets().get("tools", {}).get("burnins", {}) - options = presets.get("options") - burnin_values = presets.get("burnins") or {} - burnin = ModifiedBurnins(input_path, options_init=options) frame_start = data.get("frame_start") diff --git a/pype/scripts/slates/slate_base/lib.py b/pype/scripts/slates/slate_base/lib.py index 6b0c01883c..ec283e0e22 100644 --- a/pype/scripts/slates/slate_base/lib.py +++ b/pype/scripts/slates/slate_base/lib.py @@ -12,11 +12,6 @@ from .items import ( ItemTable, ItemImage, ItemRectangle, ItemPlaceHolder ) -try: - from pype.api.config import get_presets -except Exception: - get_presets = dict - log = logging.getLogger(__name__) @@ -41,11 +36,7 @@ def create_slates( ) elif slate_data is None: - slate_presets = ( - get_presets() - .get("tools", {}) - .get("slates") - ) or {} + slate_presets = {} slate_data = slate_presets.get(slate_name) if slate_data is None: raise ValueError( diff --git a/pype/settings/defaults/project_anatomy/attributes.json b/pype/settings/defaults/project_anatomy/attributes.json index fbf0218999..8f35e41533 100644 --- a/pype/settings/defaults/project_anatomy/attributes.json +++ b/pype/settings/defaults/project_anatomy/attributes.json @@ -9,5 +9,21 @@ "resolutionWidth": 1920, "resolutionHeight": 1080, "pixelAspect": 1, - "applications": [] + "applications": [], + "task_short_names": { + "Generic": "gener", + "Art": "art", + "Modeling": "mdl", + "Texture": "tex", + "Lookdev": "look", + "Rigging": "rig", + "Edit": "edit", + "Layout": "lay", + "Setdress": "dress", + "Animation": "anim", + "FX": "fx", + "Lighting": "lgt", + "Paint": "paint", + "Compositing": "comp" + } } \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/Ftrack.json b/pype/settings/defaults/project_settings/ftrack.json similarity index 100% rename from pype/settings/defaults/project_settings/Ftrack.json rename to pype/settings/defaults/project_settings/ftrack.json diff --git a/pype/settings/defaults/project_settings/ftrack/project_schemas/default.json b/pype/settings/defaults/project_settings/ftrack/project_schemas/default.json new file mode 100644 index 0000000000..a90a0b3a8b --- /dev/null +++ b/pype/settings/defaults/project_settings/ftrack/project_schemas/default.json @@ -0,0 +1,39 @@ +{ + "object_types": ["Milestone", "Task", "Folder", "Asset Build", "Shot", "Library", "Sequence"], + "version_workflow": ["Pending Review", "Client Review", "On Farm", "Reviewed", "Render Complete", "Approved", "CBB", "Delivered", "Render Failed", "data"], + "task_workflow": ["Not Ready", "Ready", "Change Requested", "In progress", "Pending Review", "On Farm", "Waiting", "Render Complete", "Complete", "CBB", "On Hold", "Render Failed", "Omitted"], + "overrides": [{ + "task_types": ["Animation"], + "statuses": ["Not Ready", "Ready", "Change Requested", "Blocking", "Animating", "blocking review", "anim review", "Complete", "CBB", "On Hold", "Omitted"] + }, { + "task_types": ["Lighting"], + "statuses": ["Not Ready", "Ready", "Change Requested", "In progress", "To render", "On Farm", "Render Complete", "Complete", "CBB", "On Hold", "Render Failed", "Omitted"] + }], + "task_type_schema": ["Layout", "Animation", "Modeling", "Previz", "Lookdev", "FX", "Lighting", "Compositing", "Rigging", "Texture", "Matte-paint", "Roto-paint", "Art", "Match-moving", "Production", "Build", "Setdress", "Edit", "R&D", "Boards"], + "schemas": [{ + "object_type": "Shot", + "statuses": ["Omitted", "Normal", "Complete"], + "task_types": [] + }, { + "object_type": "Asset Build", + "statuses": ["Omitted", "Normal", "Complete"], + "task_types": ["Setups", "Sets", "Characters", "Props", "Locations", "Assembly", "R&D", "Elements"] + }, { + "object_type": "Milestone", + "statuses": ["Normal", "Complete"], + "task_types": ["Generic"] + }], + "task_templates": [{ + "name": "Character", + "task_types": ["Art", "Modeling", "Lookdev", "Rigging"] + }, { + "name": "Element", + "task_types": ["Modeling", "Lookdev"] + }, { + "name": "Prop", + "task_types": ["Modeling", "Lookdev", "Rigging"] + }, { + "name": "Location", + "task_types": ["Layout", "Setdress"] + }] +} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/global.json b/pype/settings/defaults/project_settings/global.json index 7cedd7cf88..5f76f2d0f6 100644 --- a/pype/settings/defaults/project_settings/global.json +++ b/pype/settings/defaults/project_settings/global.json @@ -27,12 +27,12 @@ "video_filters": [], "audio_filters": [], "input": [ - "gamma 2.2" + "-gamma 2.2" ], "output": [ - "pix_fmt yuv420p", - "crf 18", - "intra" + "-pix_fmt yuv420p", + "-crf 18", + "-intra" ] }, "filter": { @@ -76,18 +76,16 @@ }, "IntegrateAssetNew": { "template_name_profiles": { - "template_name_profiles": { - "publish": { - "families": [], - "tasks": [] - }, - "render": { - "families": [ - "review", - "render", - "prerender" - ] - } + "publish": { + "families": [], + "tasks": [] + }, + "render": { + "families": [ + "review", + "render", + "prerender" + ] } } }, @@ -135,15 +133,13 @@ } }, "Workfiles": { - "last_workfile_on_startup": { - "profiles": [ - { - "hosts": [], - "tasks": [], - "enabled": true - } - ] - }, + "last_workfile_on_startup": [ + { + "hosts": [], + "tasks": [], + "enabled": true + } + ], "sw_folders": { "compositing": [ "nuke", diff --git a/pype/settings/defaults/project_settings/harmony.json b/pype/settings/defaults/project_settings/harmony.json new file mode 100644 index 0000000000..5eca4f60eb --- /dev/null +++ b/pype/settings/defaults/project_settings/harmony.json @@ -0,0 +1,7 @@ +{ + "publish": {}, + "general": { + "skip_resolution_check": false, + "skip_timelines_check": false + } +} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/maya.json b/pype/settings/defaults/project_settings/maya.json index 0c640a63e4..9193ea2b52 100644 --- a/pype/settings/defaults/project_settings/maya.json +++ b/pype/settings/defaults/project_settings/maya.json @@ -132,7 +132,7 @@ "regex": "" }, "ValidateMeshHasOverlappingUVs": { - "enabled": true + "enabled": false }, "ExtractCameraAlembic": { "enabled": true, @@ -145,23 +145,78 @@ } }, "load": { - "colors": { - "model": [0.821, 0.518, 0.117], - "rig": [0.144, 0.443, 0.463], - "pointcache": [0.368, 0.821, 0.117], - "animation": [0.368, 0.821, 0.117], - "ass": [1.0, 0.332, 0.312], - "camera": [0.447, 0.312, 1.0], - "camerarig": [0.447, 0.312, 1.0], - "fbx": [1.0, 0.931, 0.312], - "mayaAscii": [0.312, 1.0, 0.747], - "setdress": [0.312, 1.0, 0.747], - "layout": [0.312, 1.0, 0.747], - "vdbcache": [0.312, 1.0, 0.428], - "vrayproxy": [0.258, 0.95, 0.541], - "yeticache": [0.2, 0.8, 0.3], - "yetiRig": [0, 0.8, 0.5] - } + "colors": { + "model": [ + 0.821, + 0.518, + 0.117 + ], + "rig": [ + 0.144, + 0.443, + 0.463 + ], + "pointcache": [ + 0.368, + 0.821, + 0.117 + ], + "animation": [ + 0.368, + 0.821, + 0.117 + ], + "ass": [ + 1.0, + 0.332, + 0.312 + ], + "camera": [ + 0.447, + 0.312, + 1.0 + ], + "fbx": [ + 1.0, + 0.931, + 0.312 + ], + "mayaAscii": [ + 0.312, + 1.0, + 0.747 + ], + "setdress": [ + 0.312, + 1.0, + 0.747 + ], + "layout": [ + 0.312, + 1.0, + 0.747 + ], + "vdbcache": [ + 0.312, + 1.0, + 0.428 + ], + "vrayproxy": [ + 0.258, + 0.95, + 0.541 + ], + "yeticache": [ + 0.2, + 0.8, + 0.3 + ], + "yetiRig": [ + 0.0, + 0.8, + 0.5 + ] + } }, "workfile_build": { "profiles": [ diff --git a/pype/settings/defaults/project_settings/standalonepublisher.json b/pype/settings/defaults/project_settings/standalonepublisher.json index 877055ceef..b8015f2832 100644 --- a/pype/settings/defaults/project_settings/standalonepublisher.json +++ b/pype/settings/defaults/project_settings/standalonepublisher.json @@ -19,7 +19,7 @@ "create_camera": "Camera", "create_editorial": "Editorial", "create_image": "Image", - "create_matchmove": "matchmove", + "create_matchmove": "Matchmove", "": "" }, "create_workfile": { @@ -112,7 +112,7 @@ }, "create_matchmove": { "name": "matchmove", - "label": "Matchmove script", + "label": "Matchmove Scripts", "family": "matchmove", "icon": "empire", "defaults": [ @@ -123,4 +123,4 @@ "help": "Script exported from matchmoving application" } } -} \ No newline at end of file +} diff --git a/pype/settings/defaults/project_settings/tools/slates/example_HD.json b/pype/settings/defaults/project_settings/tools/slates/example_HD.json deleted file mode 100644 index b06391fb63..0000000000 --- a/pype/settings/defaults/project_settings/tools/slates/example_HD.json +++ /dev/null @@ -1,212 +0,0 @@ -{ - "width": 1920, - "height": 1080, - "destination_path": "{destination_path}", - "style": { - "*": { - "font-family": "arial", - "font-color": "#ffffff", - "font-bold": false, - "font-italic": false, - "bg-color": "#0077ff", - "alignment-horizontal": "left", - "alignment-vertical": "top" - }, - "layer": { - "padding": 0, - "margin": 0 - }, - "rectangle": { - "padding": 0, - "margin": 0, - "bg-color": "#E9324B", - "fill": true - }, - "main_frame": { - "padding": 0, - "margin": 0, - "bg-color": "#252525" - }, - "table": { - "padding": 0, - "margin": 0, - "bg-color": "transparent" - }, - "table-item": { - "padding": 5, - "padding-bottom": 10, - "margin": 0, - "bg-color": "#212121", - "bg-alter-color": "#272727", - "font-color": "#dcdcdc", - "font-bold": false, - "font-italic": false, - "alignment-horizontal": "left", - "alignment-vertical": "top", - "word-wrap": false, - "ellide": true, - "max-lines": 1 - }, - "table-item-col[0]": { - "font-size": 20, - "font-color": "#898989", - "font-bold": true, - "ellide": false, - "word-wrap": true, - "max-lines": null - }, - "table-item-col[1]": { - "font-size": 40, - "padding-left": 10 - }, - "#colorbar": { - "bg-color": "#9932CC" - } - }, - "items": [{ - "type": "layer", - "direction": 1, - "name": "MainLayer", - "style": { - "#MainLayer": { - "width": 1094, - "height": 1000, - "margin": 25, - "padding": 0 - }, - "#LeftSide": { - "margin-right": 25 - } - }, - "items": [{ - "type": "layer", - "name": "LeftSide", - "items": [{ - "type": "layer", - "direction": 1, - "style": { - "table-item": { - "bg-color": "transparent", - "padding-bottom": 20 - }, - "table-item-col[0]": { - "font-size": 20, - "font-color": "#898989", - "alignment-horizontal": "right" - }, - "table-item-col[1]": { - "alignment-horizontal": "left", - "font-bold": true, - "font-size": 40 - } - }, - "items": [{ - "type": "table", - "values": [ - ["Show:", "{project[name]}"] - ], - "style": { - "table-item-field[0:0]": { - "width": 150 - }, - "table-item-field[0:1]": { - "width": 580 - } - } - }, { - "type": "table", - "values": [ - ["Submitting For:", "{intent}"] - ], - "style": { - "table-item-field[0:0]": { - "width": 160 - }, - "table-item-field[0:1]": { - "width": 218, - "alignment-horizontal": "right" - } - } - }] - }, { - "type": "rectangle", - "style": { - "bg-color": "#bc1015", - "width": 1108, - "height": 5, - "fill": true - } - }, { - "type": "table", - "use_alternate_color": true, - "values": [ - ["Version name:", "{version_name}"], - ["Date:", "{date}"], - ["Shot Types:", "{shot_type}"], - ["Submission Note:", "{submission_note}"] - ], - "style": { - "table-item": { - "padding-bottom": 20 - }, - "table-item-field[0:1]": { - "font-bold": true - }, - "table-item-field[3:0]": { - "word-wrap": true, - "ellide": true, - "max-lines": 4 - }, - "table-item-col[0]": { - "alignment-horizontal": "right", - "width": 150 - }, - "table-item-col[1]": { - "alignment-horizontal": "left", - "width": 958 - } - } - }] - }, { - "type": "layer", - "name": "RightSide", - "items": [{ - "type": "placeholder", - "name": "thumbnail", - "path": "{thumbnail_path}", - "style": { - "width": 730, - "height": 412 - } - }, { - "type": "placeholder", - "name": "colorbar", - "path": "{color_bar_path}", - "return_data": true, - "style": { - "width": 730, - "height": 55 - } - }, { - "type": "table", - "use_alternate_color": true, - "values": [ - ["Vendor:", "{vendor}"], - ["Shot Name:", "{shot_name}"], - ["Frames:", "{frame_start} - {frame_end} ({duration})"] - ], - "style": { - "table-item-col[0]": { - "alignment-horizontal": "left", - "width": 200 - }, - "table-item-col[1]": { - "alignment-horizontal": "right", - "width": 530, - "font-size": 30 - } - } - }] - }] - }] -} diff --git a/pype/settings/defaults/system_settings/modules.json b/pype/settings/defaults/system_settings/modules.json index 8c228e91ef..a36a3b75cf 100644 --- a/pype/settings/defaults/system_settings/modules.json +++ b/pype/settings/defaults/system_settings/modules.json @@ -58,13 +58,25 @@ "show": { "avalon_auto_sync": { "default": "", - "write_security_role": [], - "read_security_role": [] + "write_security_role": [ + "API", + "Administrator" + ], + "read_security_role": [ + "API", + "Administrator" + ] }, "library_project": { "default": "", - "write_security_role": [], - "read_security_role": [] + "write_security_role": [ + "API", + "Administrator" + ], + "read_security_role": [ + "API", + "Administrator" + ] } }, "is_hierarchical": { @@ -154,7 +166,7 @@ "message_time": 0.5 }, "Clockify": { - "enabled": true, + "enabled": false, "workspace_name": "studio name" }, "Deadline": { @@ -190,4 +202,4 @@ "Idle Manager": { "enabled": true } -} \ No newline at end of file +} diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 03c888faa7..60e5f00dba 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -17,6 +17,13 @@ M_DYNAMIC_KEY_LABEL = "__dynamic_keys_labels__" # NOTE key popping not implemented yet M_POP_KEY = "__pop_key__" +METADATA_KEYS = ( + M_OVERRIDEN_KEY, + M_ENVIRONMENT_KEY, + M_DYNAMIC_KEY_LABEL, + M_POP_KEY +) + # Folder where studio overrides are stored STUDIO_OVERRIDES_PATH = os.getenv("PYPE_PROJECT_CONFIGS") or "" @@ -413,27 +420,35 @@ def apply_overrides(source_data, override_data): return merge_overrides(_source_data, override_data) -def get_system_settings(): +def get_system_settings(clear_metadata=True): """System settings with applied studio overrides.""" default_values = get_default_settings()[SYSTEM_SETTINGS_KEY] studio_values = get_studio_system_settings_overrides() - return apply_overrides(default_values, studio_values) + result = apply_overrides(default_values, studio_values) + if clear_metadata: + clear_metadata_from_settings(result) + return result -def get_default_project_settings(): +def get_default_project_settings(clear_metadata=True): """Project settings with applied studio's default project overrides.""" default_values = get_default_settings()[PROJECT_SETTINGS_KEY] studio_values = get_studio_project_settings_overrides() - - return apply_overrides(default_values, studio_values) + result = apply_overrides(default_values, studio_values) + if clear_metadata: + clear_metadata_from_settings(result) + return result def get_default_anatomy_settings(): """Project anatomy data with applied studio's default project overrides.""" default_values = get_default_settings()[PROJECT_ANATOMY_KEY] studio_values = get_studio_project_anatomy_overrides() + result = apply_overrides(default_values, studio_values) + if clear_metadata: + clear_metadata_from_settings(result) + return result - return apply_overrides(default_values, studio_values) def get_anatomy_settings(project_name): @@ -447,10 +462,13 @@ def get_anatomy_settings(project_name): studio_overrides = get_default_anatomy_settings() project_overrides = get_project_anatomy_overrides(project_name) - return apply_overrides(studio_overrides, project_overrides) + result = apply_overrides(studio_overrides, project_overrides) + if clear_metadata: + clear_metadata_from_settings(result) + return result -def get_project_settings(project_name): +def get_project_settings(project_name, clear_metadata=True): """Project settings with applied studio and project overrides.""" if not project_name: raise ValueError( @@ -458,10 +476,13 @@ def get_project_settings(project_name): " Call `get_default_project_settings` to get project defaults." ) - studio_overrides = get_default_project_settings() + studio_overrides = get_default_project_settings(False) project_overrides = get_project_settings_overrides(project_name) - return apply_overrides(studio_overrides, project_overrides) + result = apply_overrides(studio_overrides, project_overrides) + if clear_metadata: + clear_metadata_from_settings(result) + return result def get_current_project_settings(): @@ -491,3 +512,16 @@ def get_environments(): """ return find_environments(get_system_settings()) + + +def clear_metadata_from_settings(values): + """Remove all metadata keys from loaded settings.""" + if isinstance(values, dict): + for key in tuple(values.keys()): + if key in METADATA_KEYS: + values.pop(key) + else: + clear_metadata_from_settings(values[key]) + elif isinstance(values, list): + for item in values: + clear_metadata_from_settings(item) diff --git a/pype/tests/test_lib_restructuralization.py b/pype/tests/test_lib_restructuralization.py index 152be8d1eb..957167a8bf 100644 --- a/pype/tests/test_lib_restructuralization.py +++ b/pype/tests/test_lib_restructuralization.py @@ -11,10 +11,6 @@ def test_backward_compatibility(printer): from pype.lib import get_latest_version from pype.lib import ApplicationLaunchFailed - from pype.lib import launch_application - from pype.lib import ApplicationAction - from pype.lib import get_avalon_database - from pype.lib import set_io_database from pype.lib import get_ffmpeg_tool_path from pype.lib import get_last_version_from_path diff --git a/pype/tools/assetcreator/app.py b/pype/tools/assetcreator/app.py index 71b1027ef4..f025af9662 100644 --- a/pype/tools/assetcreator/app.py +++ b/pype/tools/assetcreator/app.py @@ -6,7 +6,7 @@ try: import ftrack_api_old as ftrack_api except Exception: import ftrack_api -from pype.api import config +from pype.api import get_current_project_settings from pype import lib as pypelib from avalon.vendor.Qt import QtWidgets, QtCore from avalon import io, api, style, schema @@ -196,7 +196,7 @@ class Window(QtWidgets.QDialog): ft_project = session.query(project_query).one() schema_name = ft_project['project_schema']['name'] # Load config - schemas_items = config.get_presets().get('ftrack', {}).get( + schemas_items = get_current_project_settings().get('ftrack', {}).get( 'project_schemas', {} ) # Get info if it is silo project diff --git a/pype/tools/launcher/lib.py b/pype/tools/launcher/lib.py index f70929fc2e..7d2a49db9d 100644 --- a/pype/tools/launcher/lib.py +++ b/pype/tools/launcher/lib.py @@ -16,60 +16,13 @@ provides a bridge between the file-based project inventory and configuration. import os from Qt import QtGui -from avalon import lib from avalon.vendor import qtawesome from pype.api import resources -from pype.lib import ApplicationAction ICON_CACHE = {} NOT_FOUND = type("NotFound", (object, ), {}) -def get_application_actions(project): - """Define dynamic Application classes for project using `.toml` files - - Args: - project (dict): project document from the database - - Returns: - list: list of dictionaries - """ - - apps = [] - for app in project["config"]["apps"]: - try: - app_name = app["name"] - app_definition = lib.get_application(app_name) - except Exception as exc: - print("Unable to load application: %s - %s" % (app['name'], exc)) - continue - - # Get from app definition, if not there from app in project - icon = app_definition.get("icon", app.get("icon", "folder-o")) - color = app_definition.get("color", app.get("color", None)) - order = app_definition.get("order", app.get("order", 0)) - label = app_definition.get("label") or app.get("label") or app_name - label_variant = app_definition.get("label_variant") - group = app_definition.get("group") or app.get("group") - action = type( - "app_{}".format(app_name), - (ApplicationAction,), - { - "name": app_name, - "label": label, - "label_variant": label_variant, - "group": group, - "icon": icon, - "color": color, - "order": order, - "config": app_definition.copy() - } - ) - - apps.append(action) - return apps - - def get_action_icon(action): icon_name = action.icon if not icon_name: diff --git a/pype/tools/launcher/models.py b/pype/tools/launcher/models.py index 07db36fa9a..3e869f3e4a 100644 --- a/pype/tools/launcher/models.py +++ b/pype/tools/launcher/models.py @@ -7,7 +7,7 @@ from .actions import ApplicationAction from Qt import QtCore, QtGui from avalon.vendor import qtawesome from avalon import style, api -from pype.lib import ApplicationManager, env_value_to_bool +from pype.lib import ApplicationManager log = logging.getLogger(__name__) @@ -117,11 +117,7 @@ class ActionModel(QtGui.QStandardItemModel): super(ActionModel, self).__init__(parent=parent) self.dbcon = dbcon - self.use_manager = env_value_to_bool( - "PYPE_USE_APP_MANAGER", default=False - ) - if self.use_manager: - self.application_manager = ApplicationManager() + self.application_manager = ApplicationManager() self._session = {} self._groups = {} @@ -141,11 +137,7 @@ class ActionModel(QtGui.QStandardItemModel): actions = api.discover(api.Action) # Get available project actions and the application actions - if self.use_manager: - app_actions = self.get_application_actions() - else: - project_doc = self.dbcon.find_one({"type": "project"}) - app_actions = lib.get_application_actions(project_doc) + app_actions = self.get_application_actions() actions.extend(app_actions) self._registered_actions = actions diff --git a/pype/tools/pyblish_pype/control.py b/pype/tools/pyblish_pype/control.py index fecfffd821..4f7a43d6d1 100644 --- a/pype/tools/pyblish_pype/control.py +++ b/pype/tools/pyblish_pype/control.py @@ -22,7 +22,7 @@ import pyblish.version from . import util from .constants import InstanceStates -from pype.api import config +from pype.api import get_project_settings class IterationBreak(Exception): @@ -121,14 +121,14 @@ class Controller(QtCore.QObject): def presets_by_hosts(self): # Get global filters as base - presets = config.get_presets().get("plugins", {}) + presets = get_project_settings(os.environ['AVALON_PROJECT']) or {} if not presets: return {} - result = presets.get("global", {}).get("filter", {}) + result = presets.get("global", {}).get("filters", {}) hosts = pyblish.api.registered_hosts() for host in hosts: - host_presets = presets.get(host, {}).get("filter") + host_presets = presets.get(host, {}).get("filters") if not host_presets: continue diff --git a/pype/tools/pyblish_pype/model.py b/pype/tools/pyblish_pype/model.py index ec9689381e..88dce679f7 100644 --- a/pype/tools/pyblish_pype/model.py +++ b/pype/tools/pyblish_pype/model.py @@ -35,7 +35,7 @@ from six import text_type from .vendor import qtawesome from .constants import PluginStates, InstanceStates, GroupStates, Roles -from pype.api import config +from pype.api import get_system_settings # ItemTypes @@ -104,8 +104,9 @@ class IntentModel(QtGui.QStandardItemModel): self.default_index = 0 intents_preset = ( - config.get_presets() - .get("global", {}) + get_system_settings() + .get("modules", {}) + .get("Ftrack", {}) .get("intent", {}) ) diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_main.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_main.json index f0226446ea..5b3c399666 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_main.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_main.json @@ -10,7 +10,7 @@ "type": "anatomy_roots", "key": "roots", "is_file": true - }, + }, { "type": "schema", "name": "schema_anatomy_templates" @@ -48,6 +48,10 @@ "type": "schema", "name": "schema_project_hiero" }, + { + "type": "schema", + "name": "schema_project_harmony" + }, { "type": "schema", "name": "schema_project_celaction" diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_ftrack.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_ftrack.json index 3b784accc1..f54c1232a6 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_ftrack.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_ftrack.json @@ -1,6 +1,6 @@ { "type": "dict", - "key": "Ftrack", + "key": "ftrack", "label": "Ftrack", "collapsable": true, "checkbox_key": "enabled", diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_harmony.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_harmony.json new file mode 100644 index 0000000000..282a4350b6 --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_harmony.json @@ -0,0 +1,34 @@ +{ + "type": "dict", + "collapsable": true, + "key": "harmony", + "label": "Harmony", + "is_file": true, + "children": [ + { + "type": "dict", + "collapsable": true, + "key": "publish", + "label": "Publish plugins", + "children": [] + }, + { + "type": "dict", + "collapsable": true, + "key": "general", + "label": "General", + "children": [ + + { + "type": "boolean", + "key": "skip_resolution_check", + "label": "Skip Resolution Check" + }, + { + "type": "boolean", + "key": "skip_timelines_check", + "label": "Skip Timeliene Check" + } + ] + }] +} diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_anatomy_attributes.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_anatomy_attributes.json index 1ede46903c..a64b99ce9d 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_anatomy_attributes.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_anatomy_attributes.json @@ -68,6 +68,12 @@ {"blender_2.91": "Blender 2.91"}, {"aftereffects_2021": "After Effects 2021"} ] + }, + { + "type": "dict-modifiable", + "key": "task_short_names", + "label": "Task short names (by Task type)", + "object_type": "text" } ] } diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_global_tools.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_global_tools.json index 5a19e4827d..529794fd28 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_global_tools.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_global_tools.json @@ -28,45 +28,41 @@ "label": "Workfiles", "children": [ { - "type": "dict", - "collapsable": true, - "key": "last_workfile_on_startup", + "type": "collapsible-wrap", "label": "Open last workfiles on launch", - "checkbox_key": "enabled", - "is_group": true, "children": [ - { - "type": "list", - "key": "profiles", - "label": "Profiles", - "object_type": - { - "type": "dict", - "children": [ - { - "key": "hosts", - "label": "Hosts", - "type": "list", - "object_type": "text" - }, - { - "key": "tasks", - "label": "Tasks", - "type": "list", - "object_type": "text" - }, - { - "type": "splitter" - }, - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - } - ] - } - } - ] + { + "type": "list", + "key": "last_workfile_on_startup", + "label": "", + "is_group": true, + "object_type": + { + "type": "dict", + "children": [ + { + "key": "hosts", + "label": "Hosts", + "type": "list", + "object_type": "text" + }, + { + "key": "tasks", + "label": "Tasks", + "type": "list", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + } + ] + } + }] }, { "type": "dict-modifiable", diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json b/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json index fa4cd3eea8..58cd81f544 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json @@ -59,6 +59,7 @@ "type": "dict", "label": "Intent", "collapsable_key": true, + "is_group": true, "children": [ { "type": "label", diff --git a/pype/tools/standalonepublish/widgets/widget_family.py b/pype/tools/standalonepublish/widgets/widget_family.py index 1c8f2238fc..5c0c8ccd38 100644 --- a/pype/tools/standalonepublish/widgets/widget_family.py +++ b/pype/tools/standalonepublish/widgets/widget_family.py @@ -1,10 +1,11 @@ +import os from collections import namedtuple from Qt import QtWidgets, QtCore from . import HelpRole, FamilyRole, ExistsRole, PluginRole, PluginKeyRole from . import FamilyDescriptionWidget -from pype.api import config +from pype.api import get_project_settings class FamilyWidget(QtWidgets.QWidget): @@ -309,9 +310,14 @@ class FamilyWidget(QtWidgets.QWidget): def refresh(self): has_families = False - presets = config.get_presets().get('standalone_publish', {}) + settings = get_project_settings(os.environ['AVALON_PROJECT']) + sp_settings = settings.get('standalonepublisher', {}) + print(sp_settings) + + for key, creator in sp_settings.get("create", {}).items(): + if key == "__dynamic_keys_labels__": + continue - for key, creator in presets.get('families', {}).items(): creator = namedtuple("Creator", creator.keys())(*creator.values()) label = creator.label or creator.family diff --git a/pype/tools/tray/pype_tray.py b/pype/tools/tray/pype_tray.py index a4cf4eabfe..81a628fdb5 100644 --- a/pype/tools/tray/pype_tray.py +++ b/pype/tools/tray/pype_tray.py @@ -3,7 +3,8 @@ import sys import platform from avalon import style from Qt import QtCore, QtGui, QtWidgets, QtSvg -from pype.api import config, Logger, resources +from pype.api import Logger, resources +from pype.settings.lib import get_system_settings, load_json_file import pype.version try: import configparser @@ -31,18 +32,11 @@ class TrayManager: self.errors = [] CURRENT_DIR = os.path.dirname(__file__) - self.modules_imports = config.load_json( + self.modules_imports = load_json_file( os.path.join(CURRENT_DIR, "modules_imports.json") ) - presets = config.get_presets(first_run=True) - menu_items = presets["tray"]["menu_items"] - try: - self.modules_usage = menu_items["item_usage"] - except Exception: - self.modules_usage = {} - self.log.critical("Couldn't find modules usage data.") - - self.module_attributes = menu_items.get("attributes") or {} + module_settings = get_system_settings()["modules"] + self.module_settings = module_settings self.icon_run = QtGui.QIcon( resources.get_resource("icons", "circle_green.png") @@ -75,23 +69,19 @@ class TrayManager: import_path = item.get("import_path") title = item.get("title") - item_usage = self.modules_usage.get(title) - if item_usage is None: - item_usage = self.modules_usage.get(import_path, True) - - if not item_usage: + module_data = self.module_settings.get(title) + if not module_data: if not title: title = import_path - self.log.info("{} - Module ignored".format(title)) + self.log.warning("{} - Module data not found".format(title)) continue - _attributes = self.module_attributes.get(title) - if _attributes is None: - _attributes = self.module_attributes.get(import_path) - - if _attributes: - item["attributes"] = _attributes + enabled = module_data.pop("enabled", True) + if not enabled: + self.log.debug("{} - Module is disabled".format(title)) + continue + item["attributes"] = module_data items.append(item) if items: @@ -207,7 +197,7 @@ class TrayManager: ) klass = getattr(module, "CLASS_DEFINIION", None) if not klass and attributes: - self.log.error(( + self.log.debug(( "There are defined attributes for module \"{}\" but" "module does not have defined \"CLASS_DEFINIION\"." ).format(import_path)) diff --git a/setup/maya/userSetup.py b/setup/maya/userSetup.py index bbf66846da..6ee008c5fc 100644 --- a/setup/maya/userSetup.py +++ b/setup/maya/userSetup.py @@ -1,5 +1,5 @@ import os -from pype.api import config +from pype.api import get_project_settings import pype.hosts.maya.lib as mlib from maya import cmds @@ -7,14 +7,15 @@ from maya import cmds print("starting PYPE usersetup") # build a shelf -presets = config.get_presets() -shelf_preset = presets['maya'].get('project_shelf') +settings = get_project_settings(os.environ['AVALON_PROJECT']) +shelf_preset = settings['maya'].get('project_shelf') if shelf_preset: project = os.environ["AVALON_PROJECT"] - icon_path = os.path.join(os.environ['PYPE_PROJECT_SCRIPTS'], project,"icons") + icon_path = os.path.join(os.environ['PYPE_PROJECT_SCRIPTS'], + project, "icons") icon_path = os.path.abspath(icon_path) for i in shelf_preset['imports']: