From fd79e4e40c812398522bf0f99a7a67c16a7d2976 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 5 Aug 2019 11:48:03 +0200 Subject: [PATCH 1/9] register to ftrack all apps from config on start --- .../actions/action_application_loader.py | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/pype/ftrack/actions/action_application_loader.py b/pype/ftrack/actions/action_application_loader.py index 1b0f48f9be..41bcc0c0b0 100644 --- a/pype/ftrack/actions/action_application_loader.py +++ b/pype/ftrack/actions/action_application_loader.py @@ -4,7 +4,6 @@ import time from pype.ftrack import AppAction from avalon import lib from pypeapp import Logger -from pype import lib as pypelib log = Logger().get_logger(__name__) @@ -15,10 +14,7 @@ def registerApp(app, session): try: variant = app['name'].split("_")[1] except Exception: - log.warning(( - '"{0}" - App "name" and "variant" is not separated by "_"' - ' (variant is not set)' - ).format(app['name'])) + pass abspath = lib.which_app(app['name']) if abspath is None: @@ -47,18 +43,24 @@ def registerApp(app, session): icon, description, preactions ).register() + if not variant: + log.info('- Variant is not set') + def register(session): - projects = pypelib.get_all_avalon_projects() - apps = [] - appNames = [] - # Get all application from all projects - for project in projects: - for app in project['config']['apps']: - if app['name'] not in appNames: - appNames.append(app['name']) - apps.append(app) + + 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 + loaded_data = toml.load(os.path.join(launchers_path, file)) + app_data = { + "name": filename, + "label": loaded_data.get("label", filename) + } + apps.append(app_data) apps = sorted(apps, key=lambda x: x['name']) app_counter = 0 @@ -68,5 +70,8 @@ def register(session): if app_counter%5 == 0: time.sleep(0.1) app_counter += 1 - except Exception as e: - log.exception("'{0}' - not proper App ({1})".format(app['name'], e)) + except Exception as exc: + log.exception( + "\"{}\" - not a proper App ({})".format(app['name'], str(exc)), + exc_info=True + ) From b9d29b5321d031538b387e82bc375f9e208270e6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 5 Aug 2019 11:55:48 +0200 Subject: [PATCH 2/9] actions now print traceback on crash --- pype/ftrack/lib/ftrack_app_handler.py | 11 ++++++++--- pype/ftrack/lib/ftrack_base_handler.py | 27 +++++++++++++------------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/pype/ftrack/lib/ftrack_app_handler.py b/pype/ftrack/lib/ftrack_app_handler.py index fcec20cf6f..7389d1c2c3 100644 --- a/pype/ftrack/lib/ftrack_app_handler.py +++ b/pype/ftrack/lib/ftrack_app_handler.py @@ -221,10 +221,15 @@ class AppAction(BaseHandler): anatomy = anatomy.format(data) work_template = anatomy["work"]["folder"] - except Exception as e: - self.log.exception( - "{0} Error in anatomy.format: {1}".format(__name__, e) + except Exception as exc: + msg = "{} Error in anatomy.format: {}".format( + __name__, str(exc) ) + self.log.error(msg, exc_info=True) + return { + 'success': False, + 'message': msg + } workdir = os.path.normpath(work_template) os.environ["AVALON_WORKDIR"] = workdir diff --git a/pype/ftrack/lib/ftrack_base_handler.py b/pype/ftrack/lib/ftrack_base_handler.py index 5f219c1af8..1bb3eb7a8f 100644 --- a/pype/ftrack/lib/ftrack_base_handler.py +++ b/pype/ftrack/lib/ftrack_base_handler.py @@ -75,7 +75,7 @@ class BaseHandler(object): self.type, label) ) except Exception as e: - self.log.exception('{} "{}" - Registration failed ({})'.format( + self.log.error('{} "{}" - Registration failed ({})'.format( self.type, label, str(e)) ) return wrapper_register @@ -84,25 +84,26 @@ class BaseHandler(object): def launch_log(self, func): @functools.wraps(func) def wrapper_launch(*args, **kwargs): - label = self.__class__.__name__ if hasattr(self, 'label'): - if self.variant is None: - label = self.label - else: + if hasattr(self, 'variant'): label = '{} {}'.format(self.label, self.variant) + else: + label = self.label + else: + label = self.__class__.__name__ + self.log.info(('{} "{}": Launched').format(self.type, label)) try: - self.log.info(('{} "{}": Launched').format(self.type, label)) - result = func(*args, **kwargs) - self.log.info(('{} "{}": Finished').format(self.type, label)) - return result - except Exception as e: - msg = '{} "{}": Failed ({})'.format(self.type, label, str(e)) - self.log.exception(msg) + return func(*args, **kwargs) + except Exception as exc: + msg = '{} "{}": Failed ({})'.format(self.type, label, str(exc)) + self.log.error(msg, exc_info=True) return { 'success': False, 'message': msg } + finally: + self.log.info(('{} "{}": Finished').format(self.type, label)) return wrapper_launch @property @@ -230,7 +231,7 @@ class BaseHandler(object): # Get entity type and make sure it is lower cased. Most places except # the component tab in the Sidebar will use lower case notation. entity_type = entity.get('entityType').replace('_', '').lower() - + if session is None: session = self.session From 1592a1488eee873f8aed8c4297d5f932d06d0bf2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 5 Aug 2019 12:30:23 +0200 Subject: [PATCH 3/9] first check if entity have parents in data --- pype/lib.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pype/lib.py b/pype/lib.py index e443881d39..d521ee2751 100644 --- a/pype/lib.py +++ b/pype/lib.py @@ -45,13 +45,21 @@ def get_hierarchy(asset_name=None): if not asset_name: asset_name = io.Session.get("AVALON_ASSET", os.environ["AVALON_ASSET"]) - asset = io.find_one({ + asset_entity = io.find_one({ "type": 'asset', "name": asset_name }) + not_set = "PARENTS_NOT_SET" + entity_parents = entity.get("data", {}).get("parents", not_set) + + # If entity already have parents then just return joined + if entity_parents != not_set: + return "/".join(entity_parents) + + # Else query parents through visualParents and store result to entity hierarchy_items = [] - entity = asset + entity = asset_entity while True: parent_id = entity.get("data", {}).get("visualParent") if not parent_id: From 57318adc3e34f72df4a33285d344d3c4768a46a2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 5 Aug 2019 12:30:40 +0200 Subject: [PATCH 4/9] store found hierarchy items as parents to entity --- pype/lib.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pype/lib.py b/pype/lib.py index d521ee2751..66cef40674 100644 --- a/pype/lib.py +++ b/pype/lib.py @@ -67,6 +67,14 @@ def get_hierarchy(asset_name=None): entity = io.find_one({"_id": parent_id}) hierarchy_items.append(entity["name"]) + # Add parents to entity data for next query + entity_data = asset_entity.get("data", {}) + entity_data["parents"] = hierarchy_items + io.update_many( + {"_id": asset_entity["_id"]}, + {"$set": {"data": entity_data}} + ) + return "/".join(hierarchy_items) From 28ec081fc3a328ae7a6f518afd3237181821c81a Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Mon, 5 Aug 2019 13:06:11 +0100 Subject: [PATCH 5/9] Validate all attributes in scene. This allows to validate all attributes in the scene, and not just the ones in the instance. --- pype/plugins/maya/publish/validate_attributes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/plugins/maya/publish/validate_attributes.py b/pype/plugins/maya/publish/validate_attributes.py index 8b0f14b8b2..654df1ce72 100644 --- a/pype/plugins/maya/publish/validate_attributes.py +++ b/pype/plugins/maya/publish/validate_attributes.py @@ -66,7 +66,7 @@ class ValidateAttributes(pyblish.api.ContextPlugin): ) # Get invalid attributes. - nodes = [pm.PyNode(x) for x in instance] + nodes = pm.ls() for node in nodes: name = node.name(stripNamespace=True) if name not in attributes.keys(): From 4da9c04d9ed87f807ffdbbd98fbbe00d0a3dfce8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 5 Aug 2019 14:10:19 +0200 Subject: [PATCH 6/9] feat(nks): workio integration --- pype/nukestudio/__init__.py | 21 +++++++++++++ pype/nukestudio/workio.py | 61 +++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 pype/nukestudio/workio.py diff --git a/pype/nukestudio/__init__.py b/pype/nukestudio/__init__.py index 834455168a..2e7ccdd675 100644 --- a/pype/nukestudio/__init__.py +++ b/pype/nukestudio/__init__.py @@ -4,7 +4,17 @@ from avalon.tools import workfiles from avalon import api as avalon from pyblish import api as pyblish +from .workio import ( + open, + save, + current_file, + has_unsaved_changes, + file_extensions, + work_root +) + from .. import api + from .menu import ( install as menu_install, _update_menu_task_label @@ -17,6 +27,17 @@ import hiero log = Logger().get_logger(__name__, "nukestudio") +__all__ = [ + # Workfiles API + "open", + "save", + "current_file", + "has_unsaved_changes", + "file_extensions", + "work_root", + ] + + AVALON_CONFIG = os.getenv("AVALON_CONFIG", "pype") PARENT_DIR = os.path.dirname(__file__) diff --git a/pype/nukestudio/workio.py b/pype/nukestudio/workio.py new file mode 100644 index 0000000000..7fbd85a708 --- /dev/null +++ b/pype/nukestudio/workio.py @@ -0,0 +1,61 @@ +"""Host API required Work Files tool""" +import os +import hiero + + +def file_extensions(): + return [".hrox"] + + +def has_unsaved_changes(): + return hiero.core.projects()[-1] + + +def save(filepath): + project = hiero.core.projects()[-1] + + if project: + project.saveAs(filepath) + else: + project = hiero.core.newProject() + project.saveAs(filepath) + + +def open(filepath): + try: + hiero.core.openProject(filepath) + return True + except Exception as e: + try: + from PySide.QtGui import * + from PySide.QtCore import * + except: + from PySide2.QtGui import * + from PySide2.QtWidgets import * + from PySide2.QtCore import * + + prompt = "Cannot open the selected file: `{}`".format(e) + hiero.core.log.error(prompt) + dialog = QMessageBox.critical( + hiero.ui.mainWindow(), "Error", unicode(prompt)) + + +def current_file(): + import os + import hiero + + current_file = hiero.core.projects()[-1].path() + normalised = os.path.normpath(current_file) + + # Unsaved current file + if normalised is '': + return "NOT SAVED" + + return normalised + + + +def work_root(): + from avalon import api + + return os.path.normpath(api.Session["AVALON_WORKDIR"]).replace("\\", "/") From c4887a1842d9c137855e95815be50ff5b6f43afe Mon Sep 17 00:00:00 2001 From: jezschaj Date: Mon, 5 Aug 2019 14:11:36 +0200 Subject: [PATCH 7/9] fix(nk): validation fixes --- pype/plugins/nuke/publish/remove_ouput_node.py | 2 +- pype/plugins/nuke/publish/validate_rendered_frames.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pype/plugins/nuke/publish/remove_ouput_node.py b/pype/plugins/nuke/publish/remove_ouput_node.py index a5e80e511b..12361595fe 100644 --- a/pype/plugins/nuke/publish/remove_ouput_node.py +++ b/pype/plugins/nuke/publish/remove_ouput_node.py @@ -7,7 +7,7 @@ class RemoveOutputNode(pyblish.api.ContextPlugin): """ label = 'Output Node Remove' - order = pyblish.api.IntegratorOrder + order = pyblish.api.IntegratorOrder + 0.4 families = ["workfile"] hosts = ['nuke'] diff --git a/pype/plugins/nuke/publish/validate_rendered_frames.py b/pype/plugins/nuke/publish/validate_rendered_frames.py index 968afe2a5b..85cbe7b2c0 100644 --- a/pype/plugins/nuke/publish/validate_rendered_frames.py +++ b/pype/plugins/nuke/publish/validate_rendered_frames.py @@ -11,7 +11,7 @@ class RepairCollectionAction(pyblish.api.Action): icon = "wrench" def process(self, context, plugin): - self.log.info(context[0][1]) + self.log.info(context[0][0]) files_remove = [os.path.join(context[0].data["outputDir"], f) for r in context[0].data.get("representations", []) for f in r.get("files", []) @@ -20,7 +20,7 @@ class RepairCollectionAction(pyblish.api.Action): for f in files_remove: os.remove(f) self.log.debug("removing file: {}".format(f)) - context[0][1]["render"].setValue(True) + context[0][0]["render"].setValue(True) self.log.info("Rendering toggled ON") From 89e2b2aa4c0a2d19834d4886e91a1eefb8c5d1e2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 5 Aug 2019 22:49:16 +0200 Subject: [PATCH 8/9] (hotfix) bad variable naming config -> proj_config --- pype/ftrack/lib/avalon_sync.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pype/ftrack/lib/avalon_sync.py b/pype/ftrack/lib/avalon_sync.py index b677529ec3..962dc1165c 100644 --- a/pype/ftrack/lib/avalon_sync.py +++ b/pype/ftrack/lib/avalon_sync.py @@ -116,13 +116,13 @@ def import_to_avalon( # not override existing templates! templates = av_project['config'].get('template', None) if templates is not None: - for key, value in config['template'].items(): + for key, value in proj_config['template'].items(): if ( key in templates and templates[key] is not None and templates[key] != value ): - config['template'][key] = templates[key] + proj_config['template'][key] = templates[key] projectId = av_project['_id'] @@ -142,7 +142,7 @@ def import_to_avalon( {'_id': ObjectId(projectId)}, {'$set': { 'name': project_name, - 'config': config, + 'config': proj_config, 'data': data }} ) From 209be28ea6dae671ed6339c34c368dc887deebc5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 6 Aug 2019 00:14:55 +0200 Subject: [PATCH 9/9] (hotfix) added MongoDB check to register of app actions - without will discover of actions take ages --- pype/ftrack/actions/action_application_loader.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pype/ftrack/actions/action_application_loader.py b/pype/ftrack/actions/action_application_loader.py index 41bcc0c0b0..06a3f9c92e 100644 --- a/pype/ftrack/actions/action_application_loader.py +++ b/pype/ftrack/actions/action_application_loader.py @@ -4,6 +4,7 @@ import time from pype.ftrack import AppAction from avalon import lib from pypeapp import Logger +from pype.lib import get_all_avalon_projects log = Logger().get_logger(__name__) @@ -48,6 +49,10 @@ def registerApp(app, session): def register(session): + # WARNING getting projects only helps to check connection to mongo + # - without will `discover` of ftrack apps actions take ages + result = get_all_avalon_projects() + apps = [] launchers_path = os.path.join(os.environ["PYPE_CONFIG"], "launchers")