From 6a94143cc8b6b1f1b4c961e583cc65edb9cc9171 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 29 Nov 2018 11:34:18 +0100 Subject: [PATCH 01/21] Action raises error and show message to user with info about: What he did! --- pype/ftrack/actions/action_syncToAvalon.py | 297 +++++++++++---------- pype/lib.py | 62 +++++ 2 files changed, 220 insertions(+), 139 deletions(-) diff --git a/pype/ftrack/actions/action_syncToAvalon.py b/pype/ftrack/actions/action_syncToAvalon.py index 65b84fbc7b..851820cf1d 100644 --- a/pype/ftrack/actions/action_syncToAvalon.py +++ b/pype/ftrack/actions/action_syncToAvalon.py @@ -7,10 +7,12 @@ import os import ftrack_api import json import re +from pype import lib from ftrack_action_handler import BaseAction - -from avalon import io, inventory, lib +from bson.objectid import ObjectId +from avalon import io, inventory from avalon.vendor import toml +from pype.ftrack import ftrack_utils class SyncToAvalon(BaseAction): '''Edit meta data action.''' @@ -27,6 +29,7 @@ class SyncToAvalon(BaseAction): def discover(self, session, entities, event): ''' Validation ''' + discover = False for entity in entities: if entity.entity_type.lower() not in ['task', 'assetversion']: @@ -53,9 +56,9 @@ class SyncToAvalon(BaseAction): try: self.log.info("action <" + self.__class__.__name__ + "> is running") - + self.ca_mongoid = 'avalon_mongo_id' #TODO AVALON_PROJECTS, AVALON_ASSET, AVALON_SILO should be set up otherwise console log shows avalon debug - self.setAvalonAttributes(session) + self.setAvalonAttributes() self.importable = [] # get from top entity in hierarchy all parent entities @@ -70,33 +73,57 @@ class SyncToAvalon(BaseAction): self.getShotAsset(entity) # Check duplicate name - raise error if found - all_names = {} + all_names = [] duplicates = [] for e in self.importable: - name = self.checkName(e['name']) - if name in all_names: - duplicates.append("'{}'-'{}'".format(all_names[name], e['name'])) + lib.avalon_check_name(e) + if e['name'] in all_names: + duplicates.append("'{}'".format(e['name'])) else: - all_names[name] = e['name'] + all_names.append(e['name']) if len(duplicates) > 0: - raise ValueError("Unable to sync: Entity name duplication: {}".format(", ".join(duplicates))) + raise ValueError("Entity name duplication: {}".format(", ".join(duplicates))) + + ## ----- PROJECT ------ + # store Ftrack project- self.importable[0] must be project entity!!! + self.entityProj = self.importable[0] + # set AVALON_ env + os.environ["AVALON_PROJECT"] = self.entityProj["full_name"] + os.environ["AVALON_ASSET"] = self.entityProj["full_name"] + + self.avalon_project = None + + io.install() # Import all entities to Avalon DB for e in self.importable: self.importToAvalon(session, e) + io.uninstall() + job['status'] = 'done' session.commit() self.log.info('Synchronization to Avalon was successfull!') + except ValueError as ve: + job['status'] = 'failed' + session.commit() + message = str(ve) + self.log.error('Error during syncToAvalon: {}'.format(message)) + except Exception as e: job['status'] = 'failed' - message = str(e) - self.log.error('During synchronization to Avalon went something wrong! ({})'.format(message)) + session.commit() + exc_type, exc_obj, exc_tb = sys.exc_info() + fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] + log_message = "{}/{}/Line: {}".format(exc_type, fname, exc_tb.tb_lineno) + self.log.error('Error during syncToAvalon: {}'.format(log_message)) + message = 'Unexpected Error!!! (Please check Log for more information)' if len(message) > 0: + message = "Unable to sync: {}".format(message) return { 'success': False, 'message': message @@ -106,9 +133,10 @@ class SyncToAvalon(BaseAction): 'success': True, 'message': "Synchronization was successfull" } - def setAvalonAttributes(self, session): + + def setAvalonAttributes(self): self.custom_attributes = [] - all_avalon_attr = session.query('CustomAttributeGroup where name is "avalon"').one() + all_avalon_attr = self.session.query('CustomAttributeGroup where name is "avalon"').one() for cust_attr in all_avalon_attr['custom_attribute_configurations']: if 'avalon_' not in cust_attr['key']: self.custom_attributes.append(cust_attr) @@ -123,60 +151,7 @@ class SyncToAvalon(BaseAction): for child in childrens: self.getShotAsset(child) - def checkName(self, input_name): - if input_name.find(" ") == -1: - name = input_name - else: - name = input_name.replace(" ", "-") - self.log.info("Name of {} was changed to {}".format(input_name, name)) - return name - - def getConfig(self, entity): - apps = [] - for app in entity['custom_attributes']['applications']: - try: - label = toml.load(lib.which_app(app))['label'] - apps.append({'name':app, 'label':label}) - except Exception as e: - self.log.error('Error with application {0} - {1}'.format(app, e)) - - config = { - 'schema': 'avalon-core:config-1.0', - 'tasks': [{'name': ''}], - 'apps': apps, - # TODO redo work!!! - 'template': { - 'workfile': '{asset[name]}_{task[name]}_{version:0>3}<_{comment}>', - 'work': '{root}/{project}/{hierarchy}/{asset}/work/{task}', - 'publish':'{root}/{project}/{hierarchy}/{asset}/publish/{family}/{subset}/v{version}/{projectcode}_{asset}_{subset}_v{version}.{representation}'} - } - return config - - - def importToAvalon(self, session, entity): - eLinks = [] - - ca_mongoid = 'avalon_mongo_id' - - # get needed info of entity and all parents - for e in entity['link']: - tmp = session.get(e['type'], e['id']) - eLinks.append(tmp) - - entityProj = eLinks[0] - - # set AVALON_PROJECT env - os.environ["AVALON_PROJECT"] = entityProj["full_name"] - os.environ["AVALON_ASSET"] = entityProj['full_name'] - - # Set project template - template = {"schema": "avalon-core:inventory-1.0"} - - # --- Begin: PUSH TO Avalon --- - io.install() - ## ----- PROJECT ------ - # If project don't exists -> ELSE - avalon_project = io.find_one({"type": "project", "name": entityProj["full_name"]}) + def getData(self, entity, session): entity_type = entity.entity_type data = {} @@ -184,65 +159,25 @@ class SyncToAvalon(BaseAction): data['entityType'] = entity_type for cust_attr in self.custom_attributes: + key = cust_attr['key'] if cust_attr['entity_type'].lower() in ['asset']: - data[cust_attr['key']] = entity['custom_attributes'][cust_attr['key']] + data[key] = entity['custom_attributes'][key] elif cust_attr['entity_type'].lower() in ['show'] and entity_type.lower() == 'project': - data[cust_attr['key']] = entity['custom_attributes'][cust_attr['key']] + data[key] = entity['custom_attributes'][key] elif cust_attr['entity_type'].lower() in ['task'] and entity_type.lower() != 'project': # Put space between capitals (e.g. 'AssetBuild' -> 'Asset Build') - entity_type = re.sub(r"(\w)([A-Z])", r"\1 \2", entity_type) + entity_type_full = re.sub(r"(\w)([A-Z])", r"\1 \2", entity_type) # Get object id of entity type - ent_obj_type_id = session.query('ObjectType where name is "{}"'.format(entity_type)).one()['id'] + ent_obj_type_id = session.query('ObjectType where name is "{}"'.format(entity_type_full)).one()['id'] if cust_attr['object_type_id'] == ent_obj_type_id: - data[cust_attr['key']] = entity['custom_attributes'][cust_attr['key']] - - - if entity.entity_type.lower() in ['project']: - # Set project Config - config = self.getConfig(entity) - - if avalon_project is None: - inventory.save(entityProj['full_name'], config, template) - else: - io.update_many({'type': 'project','name': entityProj['full_name']}, - {'$set':{'config':config}}) + data[key] = entity['custom_attributes'][key] + if entity_type in ['Project']: data['code'] = entity['name'] - - # Store info about project (FtrackId) - io.update_many({ - 'type': 'project', - 'name': entity['full_name']}, - {'$set':{'data':data}}) - - projectId = io.find_one({"type": "project", "name": entityProj["full_name"]})["_id"] - if ca_mongoid in entity['custom_attributes']: - entity['custom_attributes'][ca_mongoid] = str(projectId) - else: - self.log.error("Custom attribute for <{}> is not created.".format(entity['name'])) - io.uninstall() - return - - # Store project Id - projectId = avalon_project["_id"] - - ## ----- ASSETS ------ - # Presets: - # TODO how to check if entity is Asset Library or AssetBuild? - if entity.entity_type in ['AssetBuild', 'Library']: - silo = 'Assets' - else: - silo = 'Film' - - os.environ['AVALON_SILO'] = silo - - # Get list of parents without project - parents = [] - for i in range(1, len(eLinks)-1): - parents.append(eLinks[i]) + return data # Get info for 'Data' in Avalon DB tasks = [] @@ -250,16 +185,22 @@ class SyncToAvalon(BaseAction): if child.entity_type in ['Task']: tasks.append(child['name']) + # Get list of parents without project + parents = [] + for i in range(1, len(entity['link'])-1): + tmp = session.get(entity['link'][i]['type'], entity['link'][i]['id']) + parents.append(tmp) + folderStruct = [] parentId = None for parent in parents: - name = self.checkName(parent['name']) - folderStruct.append(name) - parentId = io.find_one({'type': 'asset', 'name': name})['_id'] + parName = parent['name'] + folderStruct.append(parName) + parentId = io.find_one({'type': 'asset', 'name': parName})['_id'] if parent['parent'].entity_type != 'project' and parentId is None: self.importToAvalon(parent) - parentId = io.find_one({'type': 'asset', 'name': name})['_id'] + parentId = io.find_one({'type': 'asset', 'name': parName})['_id'] hierarchy = os.path.sep.join(folderStruct) @@ -268,37 +209,115 @@ class SyncToAvalon(BaseAction): data['tasks'] = tasks data['hierarchy'] = hierarchy + return data - name = self.checkName(entity['name']) + def importToAvalon(self, session, entity): + # --- Begin: PUSH TO Avalon --- + + entity_type = entity.entity_type + + if entity_type.lower() in ['project']: + # Set project Config + config = ftrack_utils.get_config(entity) + # Set project template + template = lib.get_avalon_project_template_schema() + if self.ca_mongoid in entity['custom_attributes']: + projectId = ObjectId(self.entityProj['custom_attributes'][self.ca_mongoid]) + self.avalon_project = io.find_one({"_id": projectId}) + + if self.avalon_project is None: + self.avalon_project = io.find_one({ + "type": "project", + "name": entity["full_name"] + }) + if self.avalon_project is None: + inventory.save(entity['full_name'], config, template) + self.avalon_project = io.find_one({ + "type": "project", + "name": entity["full_name"] + }) + + elif self.avalon_project['name'] != entity['full_name']: + raise ValueError('You can\'t change name {} to {}, avalon DB won\'t work properly!'.format(avalon_asset['name'], name)) + + data = self.getData(entity, session) + + # Store info about project (FtrackId) + io.update_many({ + 'type': 'project', + 'name': entity['full_name'] + }, { + '$set':{'data':data, 'config':config} + }) + + self.projectId = self.avalon_project["_id"] + if self.ca_mongoid in entity['custom_attributes']: + entity['custom_attributes'][self.ca_mongoid] = str(self.projectId) + else: + self.log.error("Custom attribute for <{}> is not created.".format(entity['name'])) + return + + ## ----- ASSETS ------ + # Presets: + # TODO how to check if entity is Asset Library or AssetBuild? + silo = 'Film' + if entity_type in ['AssetBuild', 'Library']: + silo = 'Assets' + + os.environ['AVALON_SILO'] = silo + + name = entity['name'] os.environ['AVALON_ASSET'] = name + data = self.getData(entity, session) + # Try to find asset in current database - avalon_asset = io.find_one({'type': 'asset', 'name': name}) - # Create if don't exists + avalon_asset = None + if self.ca_mongoid in entity['custom_attributes']: + try: + entityId = ObjectId(entity['custom_attributes'][self.ca_mongoid]) + avalon_asset = io.find_one({"_id": entityId}) + except: + self.log.debug("Entity {} don't have stored entity id in ftrack".format(entity['name'])) + if avalon_asset is None: - inventory.create_asset(name, silo, data, projectId) - self.log.debug("Asset {} - created".format(name)) - # Raise error if it seems to be different ent. with same name + avalon_asset = io.find_one({'type': 'asset', 'name': name}) + # Create if don't exists + if avalon_asset is None: + inventory.create_asset(name, silo, data, self.projectId) + self.log.debug("Asset {} - created".format(name)) - elif (avalon_asset['data']['ftrackId'] != data['ftrackId'] or - avalon_asset['data']['visualParent'] != data['visualParent'] or - avalon_asset['data']['parents'] != data['parents']): - raise ValueError('Entity <{}> is not same'.format(name)) - # Else update info - else: - io.update_many({'type': 'asset','name': name}, - {'$set':{'data':data, 'silo': silo}}) - # TODO check if is asset in same folder!!! ???? FEATURE FOR FUTURE - self.log.debug("Asset {} - updated".format(name)) + # Raise error if it seems to be different ent. with same name + else: + update = False + aD = avalon_asset['data'] + attr = ['ftrackId', 'visualParent', 'parents'] + for a in attr: + if a not in aD: update = True + if update is False: + if (avalon_asset['data']['ftrackId'] != data['ftrackId'] or + avalon_asset['data']['visualParent'] != data['visualParent'] or + avalon_asset['data']['parents'] != data['parents']): + raise ValueError('Entity <{}> is not same like in Avalon DB'.format(name)) + + elif avalon_asset['name'] != entity['name']: + raise ValueError('You can\'t change name {} to {}, avalon DB won\'t work properly - please create new asset'.format(avalon_asset['name'], name)) + + # Update info + io.update_many({'type': 'asset','name': name}, + {'$set':{'data':data, 'silo': silo}}) + + self.log.debug("Asset {} - updated".format(name)) + + entityId = io.find_one({'type': 'asset', 'name': name})['_id'] ## FTRACK FEATURE - FTRACK MUST HAVE avalon_mongo_id FOR EACH ENTITY TYPE EXCEPT TASK # Set custom attribute to avalon/mongo id of entity (parentID is last) - if ca_mongoid in entity['custom_attributes']: - entity['custom_attributes'][ca_mongoid] = str(parentId) + if self.ca_mongoid in entity['custom_attributes']: + entity['custom_attributes'][self.ca_mongoid] = str(entityId) else: self.log.error("Custom attribute for <{}> is not created.".format(entity['name'])) - io.uninstall() session.commit() diff --git a/pype/lib.py b/pype/lib.py index 3ce1441e3d..43f41b336c 100644 --- a/pype/lib.py +++ b/pype/lib.py @@ -9,6 +9,7 @@ from .vendor.pather.error import ParseError import avalon.io as io import avalon.api +import avalon log = logging.getLogger(__name__) @@ -335,3 +336,64 @@ def get_asset_data(asset=None): data = document.get("data", {}) return data + +def get_avalon_project_config_schema(): + schema = 'avalon-core:config-1.0' + return schema + +def get_avalon_project_template_schema(): + schema = {"schema": "avalon-core:inventory-1.0"} + return schema + +def get_avalon_project_template(): + from app.api import Templates + + """Get avalon template + + Returns: + dictionary with templates + """ + template = Templates(type=["anatomy"]) + proj_template = {} + proj_template['workfile'] = '{asset[name]}_{task[name]}_{version:0>3}<_{comment}>' + proj_template['work'] = '{root}/{project}/{hierarchy}/{asset}/work/{task}' + proj_template['publish'] = '{root}/{project}/{hierarchy}/{asset}/publish/{family}/{subset}/v{version}/{projectcode}_{asset}_{subset}_v{version}.{representation}' + # TODO this down should work but it can't be in default.toml: + # - Raises error when App (e.g. Nuke) is started + # proj_template['workfile'] = template.anatomy.avalon.workfile + # proj_template['work'] = template.anatomy.avalon.work + # proj_template['publish'] = template.anatomy.avalon.publish + return proj_template + +def avalon_check_name(self, entity, inSchema = None): + alright = True + name = entity['name'] + if " " in name: + alright = False + + data = {} + data['data'] = {} + data['type'] = 'asset' + schema = "avalon-core:asset-2.0" + # TODO have project any REGEX check? + if entity.entity_type in ['Project']: + # data['type'] = 'project' + name = entity['full_name'] + # schema = get_avalon_project_template_schema()['schema'] + # elif entity.entity_type in ['AssetBuild','Library']: + # data['silo'] = 'Assets' + # else: + # data['silo'] = 'Film' + data['silo'] = 'Film' + + if inSchema is not None: + schema = inSchema + data['schema'] = schema + data['name'] = name + try: + avalon.schema.validate(data) + except ValidationError: + alright = False + + if alright is False: + raise ValueError("{} includes unsupported symbols like 'dash' or 'space'".format(name)) From e71b3aef8dfe2dfb3dc1e7fdf6a067f956890d42 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 29 Nov 2018 15:17:32 +0100 Subject: [PATCH 02/21] Added few featers that are from different unmerged branches --- pype/ftrack/actions/action_syncToAvalon.py | 2 +- pype/ftrack/actions/ftrack_action_handler.py | 5 ++- pype/ftrack/ftrack_utils.py | 34 ++++++++++++++++++-- pype/lib.py | 33 ------------------- 4 files changed, 36 insertions(+), 38 deletions(-) diff --git a/pype/ftrack/actions/action_syncToAvalon.py b/pype/ftrack/actions/action_syncToAvalon.py index 851820cf1d..f7fd8e9641 100644 --- a/pype/ftrack/actions/action_syncToAvalon.py +++ b/pype/ftrack/actions/action_syncToAvalon.py @@ -77,7 +77,7 @@ class SyncToAvalon(BaseAction): duplicates = [] for e in self.importable: - lib.avalon_check_name(e) + ftrack_utils.avalon_check_name(e) if e['name'] in all_names: duplicates.append("'{}'".format(e['name'])) else: diff --git a/pype/ftrack/actions/ftrack_action_handler.py b/pype/ftrack/actions/ftrack_action_handler.py index fa108ec78b..f16647be76 100644 --- a/pype/ftrack/actions/ftrack_action_handler.py +++ b/pype/ftrack/actions/ftrack_action_handler.py @@ -74,6 +74,8 @@ class AppAction(object): self._launch ) + self.log.info("Application '{}' - Registered successfully".format(self.label)) + def _discover(self, event): args = self._translate_event( self.session, event @@ -435,7 +437,8 @@ class BaseAction(object): ), self._launch ) - self.log.info("----- action - <" + self.__class__.__name__ + "> - Has been registered -----") + + self.log.info("Action '{}' - Registered successfully".format(self.__class__.__name__)) def _discover(self, event): args = self._translate_event( diff --git a/pype/ftrack/ftrack_utils.py b/pype/ftrack/ftrack_utils.py index 23531a9fdd..829733efef 100644 --- a/pype/ftrack/ftrack_utils.py +++ b/pype/ftrack/ftrack_utils.py @@ -4,10 +4,38 @@ import ftrack_api import os from pprint import * +def avalon_check_name(self, entity, inSchema = None): + alright = True + name = entity['name'] + if " " in name: + alright = False -def checkLogin(): - # check Environments FTRACK_API_USER, FTRACK_API_KEY - pass + data = {} + data['data'] = {} + data['type'] = 'asset' + schema = "avalon-core:asset-2.0" + # TODO have project any REGEX check? + if entity.entity_type in ['Project']: + # data['type'] = 'project' + name = entity['full_name'] + # schema = get_avalon_project_template_schema()['schema'] + # elif entity.entity_type in ['AssetBuild','Library']: + # data['silo'] = 'Assets' + # else: + # data['silo'] = 'Film' + data['silo'] = 'Film' + + if inSchema is not None: + schema = inSchema + data['schema'] = schema + data['name'] = name + try: + avalon.schema.validate(data) + except ValidationError: + alright = False + + if alright is False: + raise ValueError("{} includes unsupported symbols like 'dash' or 'space'".format(name)) def checkRegex(): diff --git a/pype/lib.py b/pype/lib.py index 43f41b336c..0b9e066703 100644 --- a/pype/lib.py +++ b/pype/lib.py @@ -364,36 +364,3 @@ def get_avalon_project_template(): # proj_template['work'] = template.anatomy.avalon.work # proj_template['publish'] = template.anatomy.avalon.publish return proj_template - -def avalon_check_name(self, entity, inSchema = None): - alright = True - name = entity['name'] - if " " in name: - alright = False - - data = {} - data['data'] = {} - data['type'] = 'asset' - schema = "avalon-core:asset-2.0" - # TODO have project any REGEX check? - if entity.entity_type in ['Project']: - # data['type'] = 'project' - name = entity['full_name'] - # schema = get_avalon_project_template_schema()['schema'] - # elif entity.entity_type in ['AssetBuild','Library']: - # data['silo'] = 'Assets' - # else: - # data['silo'] = 'Film' - data['silo'] = 'Film' - - if inSchema is not None: - schema = inSchema - data['schema'] = schema - data['name'] = name - try: - avalon.schema.validate(data) - except ValidationError: - alright = False - - if alright is False: - raise ValueError("{} includes unsupported symbols like 'dash' or 'space'".format(name)) From c684a84df2c8636fa63e0cf9fcffae3ac0648d55 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 29 Nov 2018 15:47:14 +0100 Subject: [PATCH 03/21] Avalon check name is working now --- pype/ftrack/ftrack_utils.py | 44 +++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/pype/ftrack/ftrack_utils.py b/pype/ftrack/ftrack_utils.py index 829733efef..27bf49d7d1 100644 --- a/pype/ftrack/ftrack_utils.py +++ b/pype/ftrack/ftrack_utils.py @@ -1,10 +1,18 @@ -# fttrack help functions - -import ftrack_api import os +import sys from pprint import * -def avalon_check_name(self, entity, inSchema = None): +import ftrack_api +from pype import lib + +import avalon.io as io +import avalon.api +import avalon +from app.api import Logger + +log = Logger.getLogger(__name__) + +def avalon_check_name(entity, inSchema = None): alright = True name = entity['name'] if " " in name: @@ -38,6 +46,34 @@ def avalon_check_name(self, entity, inSchema = None): raise ValueError("{} includes unsupported symbols like 'dash' or 'space'".format(name)) + +def get_apps(entity): + """ Get apps from project + Requirements: + 'Entity' MUST be object of ftrack entity with entity_type 'Project' + Checking if app from ftrack is available in Templates/bin/{app_name}.toml + + Returns: + Array with dictionaries with app Name and Label + """ + apps = [] + for app in entity['custom_attributes']['applications']: + try: + label = toml.load(lib.which_app(app))['label'] + apps.append({'name':app, 'label':label}) + except Exception as e: + print('Error with application {0} - {1}'.format(app, e)) + return apps + +def get_config(entity): + config = {} + config['schema'] = lib.get_avalon_project_config_schema() + config['tasks'] = [{'name': ''}] + config['apps'] = get_apps(entity) + config['template'] = lib.get_avalon_project_template() + + return config + def checkRegex(): # _handle_result -> would be solution? # """ TODO Check if name of entities match REGEX""" From f415883a667d3e4c0287d7e561be81b980910757 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 29 Nov 2018 17:02:49 +0100 Subject: [PATCH 04/21] ftrack_utils:Imported toml --- pype/ftrack/actions/action_syncToAvalon.py | 2 +- pype/ftrack/ftrack_utils.py | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/pype/ftrack/actions/action_syncToAvalon.py b/pype/ftrack/actions/action_syncToAvalon.py index f7fd8e9641..f612e03d43 100644 --- a/pype/ftrack/actions/action_syncToAvalon.py +++ b/pype/ftrack/actions/action_syncToAvalon.py @@ -11,7 +11,7 @@ from pype import lib from ftrack_action_handler import BaseAction from bson.objectid import ObjectId from avalon import io, inventory -from avalon.vendor import toml + from pype.ftrack import ftrack_utils class SyncToAvalon(BaseAction): diff --git a/pype/ftrack/ftrack_utils.py b/pype/ftrack/ftrack_utils.py index 27bf49d7d1..f2e2dd6d84 100644 --- a/pype/ftrack/ftrack_utils.py +++ b/pype/ftrack/ftrack_utils.py @@ -4,10 +4,10 @@ from pprint import * import ftrack_api from pype import lib - import avalon.io as io import avalon.api import avalon +from avalon.vendor import toml from app.api import Logger log = Logger.getLogger(__name__) @@ -59,10 +59,17 @@ def get_apps(entity): apps = [] for app in entity['custom_attributes']['applications']: try: - label = toml.load(lib.which_app(app))['label'] - apps.append({'name':app, 'label':label}) + app_config = {} + app_file = toml.load(avalon.lib.which_app(app)) + app_config['name'] = app + app_config['label'] = app_file['label'] + if 'ftrack_label' in app_file: + app_config['ftrack_label'] = app_file['ftrack_label'] + + apps.append(app_config) + except Exception as e: - print('Error with application {0} - {1}'.format(app, e)) + log.warning('Error with application {0} - {1}'.format(app, e)) return apps def get_config(entity): From 476208b1e23c0587e0a90e106aff9d57edde72f1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 29 Nov 2018 17:11:02 +0100 Subject: [PATCH 05/21] Removed features from different branch --- pype/ftrack/ftrack_utils.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pype/ftrack/ftrack_utils.py b/pype/ftrack/ftrack_utils.py index f2e2dd6d84..d1d4176153 100644 --- a/pype/ftrack/ftrack_utils.py +++ b/pype/ftrack/ftrack_utils.py @@ -60,11 +60,8 @@ def get_apps(entity): for app in entity['custom_attributes']['applications']: try: app_config = {} - app_file = toml.load(avalon.lib.which_app(app)) app_config['name'] = app - app_config['label'] = app_file['label'] - if 'ftrack_label' in app_file: - app_config['ftrack_label'] = app_file['ftrack_label'] + app_config['label'] = toml.load(avalon.lib.which_app(app))['label'] apps.append(app_config) From 91c3bbf0222e6930745bb92487aa59af585714f3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 3 Dec 2018 10:41:13 +0100 Subject: [PATCH 06/21] Silo is set to same name as highest parent, except Project --- pype/ftrack/actions/action_syncToAvalon.py | 69 +--------------------- pype/ftrack/ftrack_utils.py | 66 +++++++++++++++++++++ 2 files changed, 69 insertions(+), 66 deletions(-) diff --git a/pype/ftrack/actions/action_syncToAvalon.py b/pype/ftrack/actions/action_syncToAvalon.py index f612e03d43..5e5c7b0605 100644 --- a/pype/ftrack/actions/action_syncToAvalon.py +++ b/pype/ftrack/actions/action_syncToAvalon.py @@ -151,66 +151,6 @@ class SyncToAvalon(BaseAction): for child in childrens: self.getShotAsset(child) - def getData(self, entity, session): - entity_type = entity.entity_type - - data = {} - data['ftrackId'] = entity['id'] - data['entityType'] = entity_type - - for cust_attr in self.custom_attributes: - key = cust_attr['key'] - if cust_attr['entity_type'].lower() in ['asset']: - data[key] = entity['custom_attributes'][key] - - elif cust_attr['entity_type'].lower() in ['show'] and entity_type.lower() == 'project': - data[key] = entity['custom_attributes'][key] - - elif cust_attr['entity_type'].lower() in ['task'] and entity_type.lower() != 'project': - # Put space between capitals (e.g. 'AssetBuild' -> 'Asset Build') - entity_type_full = re.sub(r"(\w)([A-Z])", r"\1 \2", entity_type) - # Get object id of entity type - ent_obj_type_id = session.query('ObjectType where name is "{}"'.format(entity_type_full)).one()['id'] - - if cust_attr['object_type_id'] == ent_obj_type_id: - data[key] = entity['custom_attributes'][key] - - if entity_type in ['Project']: - data['code'] = entity['name'] - return data - - # Get info for 'Data' in Avalon DB - tasks = [] - for child in entity['children']: - if child.entity_type in ['Task']: - tasks.append(child['name']) - - # Get list of parents without project - parents = [] - for i in range(1, len(entity['link'])-1): - tmp = session.get(entity['link'][i]['type'], entity['link'][i]['id']) - parents.append(tmp) - - folderStruct = [] - parentId = None - - for parent in parents: - parName = parent['name'] - folderStruct.append(parName) - parentId = io.find_one({'type': 'asset', 'name': parName})['_id'] - if parent['parent'].entity_type != 'project' and parentId is None: - self.importToAvalon(parent) - parentId = io.find_one({'type': 'asset', 'name': parName})['_id'] - - hierarchy = os.path.sep.join(folderStruct) - - data['visualParent'] = parentId - data['parents'] = folderStruct - data['tasks'] = tasks - data['hierarchy'] = hierarchy - - return data - def importToAvalon(self, session, entity): # --- Begin: PUSH TO Avalon --- @@ -240,7 +180,7 @@ class SyncToAvalon(BaseAction): elif self.avalon_project['name'] != entity['full_name']: raise ValueError('You can\'t change name {} to {}, avalon DB won\'t work properly!'.format(avalon_asset['name'], name)) - data = self.getData(entity, session) + data = ftrack_utils.get_data(self, entity, session,self.custom_attributes) # Store info about project (FtrackId) io.update_many({ @@ -259,17 +199,14 @@ class SyncToAvalon(BaseAction): ## ----- ASSETS ------ # Presets: - # TODO how to check if entity is Asset Library or AssetBuild? - silo = 'Film' - if entity_type in ['AssetBuild', 'Library']: - silo = 'Assets' + data = ftrack_utils.get_data(self, entity, session, self.custom_attributes) + silo = data.pop('silo') os.environ['AVALON_SILO'] = silo name = entity['name'] os.environ['AVALON_ASSET'] = name - data = self.getData(entity, session) # Try to find asset in current database avalon_asset = None diff --git a/pype/ftrack/ftrack_utils.py b/pype/ftrack/ftrack_utils.py index d1d4176153..ab13c8c63b 100644 --- a/pype/ftrack/ftrack_utils.py +++ b/pype/ftrack/ftrack_utils.py @@ -1,5 +1,6 @@ import os import sys +import re from pprint import * import ftrack_api @@ -12,6 +13,71 @@ from app.api import Logger log = Logger.getLogger(__name__) +def get_data(parent, entity, session, custom_attributes): + entity_type = entity.entity_type + + data = {} + data['ftrackId'] = entity['id'] + data['entityType'] = entity_type + + for cust_attr in custom_attributes: + key = cust_attr['key'] + if cust_attr['entity_type'].lower() in ['asset']: + data[key] = entity['custom_attributes'][key] + + elif cust_attr['entity_type'].lower() in ['show'] and entity_type.lower() == 'project': + data[key] = entity['custom_attributes'][key] + + elif cust_attr['entity_type'].lower() in ['task'] and entity_type.lower() != 'project': + # Put space between capitals (e.g. 'AssetBuild' -> 'Asset Build') + entity_type_full = re.sub(r"(\w)([A-Z])", r"\1 \2", entity_type) + # Get object id of entity type + ent_obj_type_id = session.query('ObjectType where name is "{}"'.format(entity_type_full)).one()['id'] + + if cust_attr['object_type_id'] == ent_obj_type_id: + data[key] = entity['custom_attributes'][key] + + if entity_type in ['Project']: + data['code'] = entity['name'] + return data + + # Get info for 'Data' in Avalon DB + tasks = [] + for child in entity['children']: + if child.entity_type in ['Task']: + tasks.append(child['name']) + + # Get list of parents without project + parents = [] + for i in range(1, len(entity['link'])-1): + tmp = session.get(entity['link'][i]['type'], entity['link'][i]['id']) + parents.append(tmp) + + silo = entity['name'] + if len(parents) > 0: + silo = parents[0]['name'] + + folderStruct = [] + parentId = None + + for parent in parents: + parName = parent['name'] + folderStruct.append(parName) + parentId = io.find_one({'type': 'asset', 'name': parName})['_id'] + if parent['parent'].entity_type != 'project' and parentId is None: + parent.importToAvalon(parent) + parentId = io.find_one({'type': 'asset', 'name': parName})['_id'] + + hierarchy = os.path.sep.join(folderStruct) + + data['silo'] = silo + data['visualParent'] = parentId + data['parents'] = folderStruct + data['tasks'] = tasks + data['hierarchy'] = hierarchy + + return data + def avalon_check_name(entity, inSchema = None): alright = True name = entity['name'] From ce1ff14dad38ba7a1e5e700fda3b49cc39dc1fc3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 3 Dec 2018 11:31:25 +0100 Subject: [PATCH 07/21] update action --- pype/ftrack/actions/action_syncToAvalon.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pype/ftrack/actions/action_syncToAvalon.py b/pype/ftrack/actions/action_syncToAvalon.py index 5e5c7b0605..596b7362f9 100644 --- a/pype/ftrack/actions/action_syncToAvalon.py +++ b/pype/ftrack/actions/action_syncToAvalon.py @@ -15,7 +15,12 @@ from avalon import io, inventory from pype.ftrack import ftrack_utils class SyncToAvalon(BaseAction): - '''Edit meta data action.''' + ''' + Synchronizing data action - from Ftrack to Avalon DB + + + + ''' #: Action identifier. identifier = 'sync.to.avalon' From a0f6e017f9b4dc12c3ee097bfa5451d97df7a7e1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 3 Dec 2018 12:47:24 +0100 Subject: [PATCH 08/21] Added few words --- pype/ftrack/actions/action_syncToAvalon.py | 26 +++++++++++++++++++++- pype/ftrack/ftrack_utils.py | 5 ----- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/pype/ftrack/actions/action_syncToAvalon.py b/pype/ftrack/actions/action_syncToAvalon.py index 596b7362f9..556c57be2a 100644 --- a/pype/ftrack/actions/action_syncToAvalon.py +++ b/pype/ftrack/actions/action_syncToAvalon.py @@ -18,7 +18,28 @@ class SyncToAvalon(BaseAction): ''' Synchronizing data action - from Ftrack to Avalon DB + Stores all information about entity. + - Name(string) - Most important information + - Parent(ObjectId) - Avalon Project Id, if entity is not project itself + - Silo(string) - Last parent except project + - Data(dictionary): + - VisualParent(ObjectId) - Avalon Id of parent asset + - Parents(array of string) - All parent names except project + - Tasks(array of string) - Tasks on asset + - FtrackId(string) + - entityType(string) - entity's type on Ftrack + * All Custom attributes in group 'Avalon' which name don't start with 'avalon_' + These information are stored also for all parents and children entities. + + Avalon ID of asset is stored to Ftrack -> Custom attribute 'avalon_mongo_id'. + - action IS NOT creating this Custom attribute if doesn't exist + - run 'Create Custom Attributes' action or do it manually (Must be in 'avalon' group) + + If Ftrack entity already has Custom Attribute 'avalon_mongo_id' that stores ID: + - names are checked -> shows error if names are not exact the same!!! + - after sync is not allowed to change names! + - only way is to create new entity in ftrack with new name ''' @@ -206,7 +227,10 @@ class SyncToAvalon(BaseAction): # Presets: data = ftrack_utils.get_data(self, entity, session, self.custom_attributes) - silo = data.pop('silo') + silo = entity['name'] + if len(data['parents']) > 0: + silo = data['parents'][0] + os.environ['AVALON_SILO'] = silo name = entity['name'] diff --git a/pype/ftrack/ftrack_utils.py b/pype/ftrack/ftrack_utils.py index ab13c8c63b..68c9be6bfb 100644 --- a/pype/ftrack/ftrack_utils.py +++ b/pype/ftrack/ftrack_utils.py @@ -53,10 +53,6 @@ def get_data(parent, entity, session, custom_attributes): tmp = session.get(entity['link'][i]['type'], entity['link'][i]['id']) parents.append(tmp) - silo = entity['name'] - if len(parents) > 0: - silo = parents[0]['name'] - folderStruct = [] parentId = None @@ -70,7 +66,6 @@ def get_data(parent, entity, session, custom_attributes): hierarchy = os.path.sep.join(folderStruct) - data['silo'] = silo data['visualParent'] = parentId data['parents'] = folderStruct data['tasks'] = tasks From 5b482789fe7ee465324b97025f20225df5cf6f34 Mon Sep 17 00:00:00 2001 From: antirotor Date: Mon, 3 Dec 2018 15:02:53 +0100 Subject: [PATCH 09/21] fixed launching on linux --- pype/ftrack/actions/ftrack_action_handler.py | 78 ++++++++++++++++---- 1 file changed, 65 insertions(+), 13 deletions(-) diff --git a/pype/ftrack/actions/ftrack_action_handler.py b/pype/ftrack/actions/ftrack_action_handler.py index b3fbd28274..6d86a52ce5 100644 --- a/pype/ftrack/actions/ftrack_action_handler.py +++ b/pype/ftrack/actions/ftrack_action_handler.py @@ -1,6 +1,7 @@ # :coding: utf-8 # :copyright: Copyright (c) 2017 ftrack import os +import sys import logging import getpass import platform @@ -298,20 +299,71 @@ class AppAction(object): # Full path to executable launcher execfile = None - for ext in os.environ["PATHEXT"].split(os.pathsep): - fpath = os.path.join(path.strip('"'), self.executable + ext) - if os.path.isfile(fpath) and os.access(fpath, os.X_OK): - execfile = fpath - break + if sys.platform == "win32": + + for ext in os.environ["PATHEXT"].split(os.pathsep): + fpath = os.path.join(path.strip('"'), self.executable + ext) + if os.path.isfile(fpath) and os.access(fpath, os.X_OK): + execfile = fpath + break + pass + + # Run SW if was found executable + if execfile is not None: + lib.launch(executable=execfile, args=[], environment=env) + else: + return { + 'success': False, + 'message': "We didn't found launcher for {0}" + .format(self.label) + } + pass + + if sys.platform.startswith('linux'): + execfile = os.path.join(path.strip('"'), self.executable) + if os.path.isfile(execfile): + try: + fp = open(execfile) + except PermissionError as p: + log.error('Access denied on {0} - {1}'. + format(execfile, p)) + return { + 'success': False, + 'message': "Access denied on launcher - {}". + format(execfile) + } + fp.close() + # check executable permission + if not os.access(execfile, os.X_OK): + log.error('No executable permission on {}'. + format(execfile)) + return { + 'success': False, + 'message': "No executable permission - {}" + .format(execfile) + } + pass + else: + log.error('Launcher doesn\'t exist - {}'. + format(execfile)) + return { + 'success': False, + 'message': "Launcher doesn't exist - {}" + .format(execfile) + } + pass + # Run SW if was found executable + if execfile is not None: + lib.launch('/usr/bin/env', args=['bash', execfile], environment=env) + else: + return { + 'success': False, + 'message': "We didn't found launcher for {0}" + .format(self.label) + } + pass + - # Run SW if was found executable - if execfile is not None: - lib.launch(executable=execfile, args=[], environment=env) - else: - return { - 'success': False, - 'message': "We didn't found launcher for {0}".format(self.label) - } # RUN TIMER IN FTRACK username = event['source']['user']['username'] From 0cf395c3bc2b2aa645c6c7c7b97d87d2227c10bf Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 4 Dec 2018 11:29:10 +0100 Subject: [PATCH 10/21] Created attributes are set to group and security roles are set for API only --- .../actions/action_createCustomAttributes.py | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/pype/ftrack/actions/action_createCustomAttributes.py b/pype/ftrack/actions/action_createCustomAttributes.py index 5f9dbd7381..13aad6e659 100644 --- a/pype/ftrack/actions/action_createCustomAttributes.py +++ b/pype/ftrack/actions/action_createCustomAttributes.py @@ -89,11 +89,25 @@ class AvalonIdAttribute(BaseAction): # Set session back to begin("session.query" raises error on commit) session.rollback() # Set security roles for attribute - custAttrSecuRole = session.query('SecurityRole').all() + role_api = session.query('SecurityRole where name is "API"').all() # Set Text type of Attribute custom_attribute_type = session.query( 'CustomAttributeType where name is "text"' ).one() + # Get/Set 'avalon' group + groups = session.query('CustomAttributeGroup where name is "avalon"').all() + if len(groups) > 1: + msg = "There are more Custom attribute groups with name 'avalon'" + self.log.warning(msg) + return { 'success': False, 'message':msg } + + elif len(groups) < 1: + group = session.create('CustomAttributeGroup', { + 'name': 'avalon', + }) + session.commit() + else: + group = groups[0] for entity_type in base: # Create a custom attribute configuration. @@ -103,8 +117,9 @@ class AvalonIdAttribute(BaseAction): 'label': custAttrLabel, 'key': custAttrName, 'default': '', - 'write_security_roles': custAttrSecuRole, - 'read_security_roles': custAttrSecuRole, + 'write_security_roles': role_api, + 'read_security_roles': role_api, + 'group':group, 'config': json.dumps({'markdown': False}) }) @@ -117,8 +132,9 @@ class AvalonIdAttribute(BaseAction): 'label': custAttrLabel, 'key': custAttrName, 'default': '', - 'write_security_roles': custAttrSecuRole, - 'read_security_roles': custAttrSecuRole, + 'write_security_roles': role_api, + 'read_security_roles': role_api, + 'group':group, 'config': json.dumps({'markdown': False}) }) From 01b331fdd9ebb56bb523aa5c89accbe9aaec29a6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 4 Dec 2018 13:18:11 +0100 Subject: [PATCH 11/21] Added few comments --- pype/ftrack/actions/action_syncToAvalon.py | 39 +++++++++++++--------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/pype/ftrack/actions/action_syncToAvalon.py b/pype/ftrack/actions/action_syncToAvalon.py index d89b0e6618..d907407ac6 100644 --- a/pype/ftrack/actions/action_syncToAvalon.py +++ b/pype/ftrack/actions/action_syncToAvalon.py @@ -17,7 +17,7 @@ class SyncToAvalon(BaseAction): Synchronizing data action - from Ftrack to Avalon DB Stores all information about entity. - - Name(string) - Most important information + - Name(string) - Most important information = identifier of entity - Parent(ObjectId) - Avalon Project Id, if entity is not project itself - Silo(string) - Last parent except project - Data(dictionary): @@ -28,17 +28,24 @@ class SyncToAvalon(BaseAction): - entityType(string) - entity's type on Ftrack * All Custom attributes in group 'Avalon' which name don't start with 'avalon_' - These information are stored also for all parents and children entities. + * These information are stored also for all parents and children entities. Avalon ID of asset is stored to Ftrack -> Custom attribute 'avalon_mongo_id'. - action IS NOT creating this Custom attribute if doesn't exist - - run 'Create Custom Attributes' action or do it manually (Must be in 'avalon' group) + - run 'Create Custom Attributes' action or do it manually (Not recommended) If Ftrack entity already has Custom Attribute 'avalon_mongo_id' that stores ID: - - names are checked -> shows error if names are not exact the same!!! + - names are checked -> shows error if names are not exact the same - after sync is not allowed to change names! - only way is to create new entity in ftrack with new name + If ID in 'avalon_mongo_id' is empty string or is not found in DB: + - tries to find entity by name + - found: + - raise error if ftrackId/visual parent/parents are not same + - not found: + - Creates asset/project + ''' #: Action identifier. @@ -79,7 +86,7 @@ class SyncToAvalon(BaseAction): }) try: - self.log.info("action <" + self.__class__.__name__ + "> is running") + self.log.info("Action <" + self.__class__.__name__ + "> is running") self.ca_mongoid = 'avalon_mongo_id' #TODO AVALON_PROJECTS, AVALON_ASSET, AVALON_SILO should be set up otherwise console log shows avalon debug self.setAvalonAttributes() @@ -96,7 +103,7 @@ class SyncToAvalon(BaseAction): for entity in entities: self.getShotAsset(entity) - # Check duplicate name - raise error if found + # Check names: REGEX in schema/duplicates - raise error if found all_names = [] duplicates = [] @@ -186,8 +193,11 @@ class SyncToAvalon(BaseAction): # Set project template template = lib.get_avalon_project_template_schema() if self.ca_mongoid in entity['custom_attributes']: - projectId = ObjectId(self.entityProj['custom_attributes'][self.ca_mongoid]) - self.avalon_project = io.find_one({"_id": projectId}) + try: + projectId = ObjectId(self.entityProj['custom_attributes'][self.ca_mongoid]) + self.avalon_project = io.find_one({"_id": projectId}) + except: + self.log.debug("Entity {} don't have stored entity id in ftrack".format(entity['name'])) if self.avalon_project is None: self.avalon_project = io.find_one({ @@ -255,15 +265,14 @@ class SyncToAvalon(BaseAction): else: update = False aD = avalon_asset['data'] - attr = ['ftrackId', 'visualParent', 'parents'] - for a in attr: - if a not in aD: update = True + check_attr = ['ftrackId', 'visualParent', 'parents'] + for attr in check_attr: + if attr not in aD: update = True if update is False: - if (avalon_asset['data']['ftrackId'] != data['ftrackId'] or - avalon_asset['data']['visualParent'] != data['visualParent'] or - avalon_asset['data']['parents'] != data['parents']): - raise ValueError('Entity <{}> is not same like in Avalon DB'.format(name)) + for attr in check_attr: + if (avalon_asset['data'][attr] != data[attr]): + raise ValueError('In Avalon DB already exists entity with name <{}>!'.format(name)) elif avalon_asset['name'] != entity['name']: raise ValueError('You can\'t change name {} to {}, avalon DB won\'t work properly - please create new asset'.format(avalon_asset['name'], name)) From 06d473425d9afcdecf0ad5d4679a9af15b44725a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 4 Dec 2018 13:39:42 +0100 Subject: [PATCH 12/21] Added action validation - only for admins, security role - for admins, Library removed from excepted entity type --- .../actions/action_createCustomAttributes.py | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/pype/ftrack/actions/action_createCustomAttributes.py b/pype/ftrack/actions/action_createCustomAttributes.py index 13aad6e659..ad1e1af5b9 100644 --- a/pype/ftrack/actions/action_createCustomAttributes.py +++ b/pype/ftrack/actions/action_createCustomAttributes.py @@ -24,14 +24,18 @@ class AvalonIdAttribute(BaseAction): def discover(self, session, entities, event): - ''' Validation ''' + ''' + Validation + - action is only for Administrators + ''' + success = False + userId = event['source']['user']['id'] + user = session.query('User where id is ' + userId).one() + for role in user['user_security_roles']: + if role['security_role']['name'] == 'Administrator': + success = True - # userId = event['source']['user']['id'] - # user = session.query('User where id is ' + userId).one() - # if user['user_security_roles'][0]['security_role']['name'] != 'Administrator': - # return False - - return True + return success def launch(self, session, entities, event): @@ -55,7 +59,7 @@ class AvalonIdAttribute(BaseAction): # Types that don't need object_type_id base = {'show'} # Don't create custom attribute on these entity types: - exceptions = ['task','milestone','library'] + exceptions = ['task', 'milestone'] exceptions.extend(base) # Get all possible object types all_obj_types = session.query('ObjectType').all() @@ -89,7 +93,10 @@ class AvalonIdAttribute(BaseAction): # Set session back to begin("session.query" raises error on commit) session.rollback() # Set security roles for attribute - role_api = session.query('SecurityRole where name is "API"').all() + role_api = session.query('SecurityRole where name is "API"').one() + role_admin = session.query('SecurityRole where name is "Administrator"').one() + roles = [role_api,role_admin] + # Set Text type of Attribute custom_attribute_type = session.query( 'CustomAttributeType where name is "text"' @@ -117,8 +124,8 @@ class AvalonIdAttribute(BaseAction): 'label': custAttrLabel, 'key': custAttrName, 'default': '', - 'write_security_roles': role_api, - 'read_security_roles': role_api, + 'write_security_roles': roles, + 'read_security_roles': roles, 'group':group, 'config': json.dumps({'markdown': False}) }) @@ -132,8 +139,8 @@ class AvalonIdAttribute(BaseAction): 'label': custAttrLabel, 'key': custAttrName, 'default': '', - 'write_security_roles': role_api, - 'read_security_roles': role_api, + 'write_security_roles': roles, + 'read_security_roles': roles, 'group':group, 'config': json.dumps({'markdown': False}) }) From 9910fa0b0d06e5a383ddd8fdbcfc91dd6e777d3d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 4 Dec 2018 14:16:02 +0100 Subject: [PATCH 13/21] Validation Error is now in context --- pype/ftrack/actions/action_syncToAvalon.py | 2 +- pype/ftrack/ftrack_utils.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pype/ftrack/actions/action_syncToAvalon.py b/pype/ftrack/actions/action_syncToAvalon.py index d907407ac6..9b8176a088 100644 --- a/pype/ftrack/actions/action_syncToAvalon.py +++ b/pype/ftrack/actions/action_syncToAvalon.py @@ -151,7 +151,7 @@ class SyncToAvalon(BaseAction): fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] log_message = "{}/{}/Line: {}".format(exc_type, fname, exc_tb.tb_lineno) self.log.error('Error during syncToAvalon: {}'.format(log_message)) - message = 'Unexpected Error!!! (Please check Log for more information)' + message = 'Unexpected Error - Please check Log for more information' if len(message) > 0: message = "Unable to sync: {}".format(message) diff --git a/pype/ftrack/ftrack_utils.py b/pype/ftrack/ftrack_utils.py index 68c9be6bfb..10a1ee8ef3 100644 --- a/pype/ftrack/ftrack_utils.py +++ b/pype/ftrack/ftrack_utils.py @@ -8,7 +8,7 @@ from pype import lib import avalon.io as io import avalon.api import avalon -from avalon.vendor import toml +from avalon.vendor import toml, jsonschema from app.api import Logger log = Logger.getLogger(__name__) @@ -74,6 +74,7 @@ def get_data(parent, entity, session, custom_attributes): return data def avalon_check_name(entity, inSchema = None): + ValidationError = jsonschema.ValidationError alright = True name = entity['name'] if " " in name: From 6e0fc1771d73004cd0b13333de2baa5b182b3c17 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 4 Dec 2018 15:50:20 +0100 Subject: [PATCH 14/21] Top entity is not pushed to Avalon as asset and error message is full now --- pype/ftrack/actions/action_syncToAvalon.py | 21 +++++++++------------ pype/ftrack/ftrack_utils.py | 13 +++++++------ 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/pype/ftrack/actions/action_syncToAvalon.py b/pype/ftrack/actions/action_syncToAvalon.py index 9b8176a088..343df2e702 100644 --- a/pype/ftrack/actions/action_syncToAvalon.py +++ b/pype/ftrack/actions/action_syncToAvalon.py @@ -233,10 +233,12 @@ class SyncToAvalon(BaseAction): ## ----- ASSETS ------ # Presets: - data = ftrack_utils.get_data(self, entity, session, self.custom_attributes) - silo = entity['name'] - if len(data['parents']) > 0: + + # return if entity is silo + if len(data['parents']) == 0: + return + else: silo = data['parents'][0] os.environ['AVALON_SILO'] = silo @@ -263,16 +265,11 @@ class SyncToAvalon(BaseAction): # Raise error if it seems to be different ent. with same name else: - update = False aD = avalon_asset['data'] - check_attr = ['ftrackId', 'visualParent', 'parents'] - for attr in check_attr: - if attr not in aD: update = True - - if update is False: - for attr in check_attr: - if (avalon_asset['data'][attr] != data[attr]): - raise ValueError('In Avalon DB already exists entity with name <{}>!'.format(name)) + # check_attr = ['parents', 'ftrackId', 'visualParent'] + if (avalon_asset['data']['parents'] != data['parents'] or + avalon_asset['silo'] != silo): + raise ValueError('In Avalon DB already exists entity with name "{0}"'.format(name)) elif avalon_asset['name'] != entity['name']: raise ValueError('You can\'t change name {} to {}, avalon DB won\'t work properly - please create new asset'.format(avalon_asset['name'], name)) diff --git a/pype/ftrack/ftrack_utils.py b/pype/ftrack/ftrack_utils.py index 10a1ee8ef3..66d739829b 100644 --- a/pype/ftrack/ftrack_utils.py +++ b/pype/ftrack/ftrack_utils.py @@ -49,16 +49,17 @@ def get_data(parent, entity, session, custom_attributes): # Get list of parents without project parents = [] - for i in range(1, len(entity['link'])-1): - tmp = session.get(entity['link'][i]['type'], entity['link'][i]['id']) - parents.append(tmp) - folderStruct = [] + for i in range(1, len(entity['link'])-1): + parEnt = session.get(entity['link'][i]['type'], entity['link'][i]['id']) + parName = parEnt['name'] + folderStruct.append(parName) + if i > 1: + parents.append(parEnt) + parentId = None for parent in parents: - parName = parent['name'] - folderStruct.append(parName) parentId = io.find_one({'type': 'asset', 'name': parName})['_id'] if parent['parent'].entity_type != 'project' and parentId is None: parent.importToAvalon(parent) From 884ac36f25cf4c1e669da1dc98ccdce1aa9b9060 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 4 Dec 2018 16:35:04 +0100 Subject: [PATCH 15/21] Creates Checkbox for auto-sync event in Project. Action update roles and group on existing attributes --- .../actions/action_createCustomAttributes.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/pype/ftrack/actions/action_createCustomAttributes.py b/pype/ftrack/actions/action_createCustomAttributes.py index ad1e1af5b9..d45a92a01d 100644 --- a/pype/ftrack/actions/action_createCustomAttributes.py +++ b/pype/ftrack/actions/action_createCustomAttributes.py @@ -53,11 +53,19 @@ class AvalonIdAttribute(BaseAction): }) session.commit() try: + # Checkbox for event sync + cbxSyncName = 'avalon_auto_sync' + cbxSyncLabel = 'Avalon auto-sync' + cbxSyncExist = False + # Attribute Name and Label custAttrName = 'avalon_mongo_id' custAttrLabel = 'Avalon/Mongo Id' + + attrs_update = set() # Types that don't need object_type_id base = {'show'} + # Don't create custom attribute on these entity types: exceptions = ['task', 'milestone'] exceptions.extend(base) @@ -77,6 +85,7 @@ class AvalonIdAttribute(BaseAction): # Get IDs of filtered object types all_obj_types_id = set() + for obj in all_obj_types: all_obj_types_id.add(obj['id']) @@ -84,11 +93,16 @@ class AvalonIdAttribute(BaseAction): current_cust_attr = session.query('CustomAttributeConfiguration').all() # Filter already existing AvalonMongoID attr. for attr in current_cust_attr: + if attr['key'] == cbxSyncName: + cbxSyncExist = True + cbxAttribute = attr if attr['key'] == custAttrName: if attr['entity_type'] in base: base.remove(attr['entity_type']) + attrs_update.add(attr) if attr['object_type_id'] in all_obj_types_id: all_obj_types_id.remove(attr['object_type_id']) + attrs_update.add(attr) # Set session back to begin("session.query" raises error on commit) session.rollback() @@ -116,6 +130,24 @@ class AvalonIdAttribute(BaseAction): else: group = groups[0] + # Checkbox for auto-sync event / Create or Update(roles + group) + if cbxSyncExist is False: + cbxType = session.query('CustomAttributeType where name is "boolean"').first() + session.create('CustomAttributeConfiguration', { + 'entity_type': 'show', + 'type': cbxType, + 'label': cbxSyncLabel, + 'key': cbxSyncName, + 'default': False, + 'write_security_roles': roles, + 'read_security_roles': roles, + 'group':group, + }) + else: + cbxAttribute['write_security_roles'] = roles + cbxAttribute['read_security_roles'] = roles + cbxAttribute['group'] = group + for entity_type in base: # Create a custom attribute configuration. session.create('CustomAttributeConfiguration', { @@ -145,11 +177,18 @@ class AvalonIdAttribute(BaseAction): 'config': json.dumps({'markdown': False}) }) + for attr in attrs_update: + attr['write_security_roles'] = roles + attr['read_security_roles'] = roles + attr['group'] = group + job['status'] = 'done' session.commit() except Exception as e: + session.rollback() job['status'] = 'failed' + session.commit() self.log.error("Creating custom attributes failed ({})".format(e)) return True From e1308d7f644453ac3bbb0e17993e432db688c553 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 4 Dec 2018 16:55:36 +0100 Subject: [PATCH 16/21] Action shows only to users in roleList: Admin & Project Manager --- pype/ftrack/actions/action_syncToAvalon.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/pype/ftrack/actions/action_syncToAvalon.py b/pype/ftrack/actions/action_syncToAvalon.py index 343df2e702..829ac3dedc 100644 --- a/pype/ftrack/actions/action_syncToAvalon.py +++ b/pype/ftrack/actions/action_syncToAvalon.py @@ -60,12 +60,20 @@ class SyncToAvalon(BaseAction): def discover(self, session, entities, event): ''' Validation ''' - + roleCheck = False discover = False - for entity in entities: - if entity.entity_type.lower() not in ['task', 'assetversion']: - discover = True - break + roleList = ['Administrator', 'Project Manager'] + userId = event['source']['user']['id'] + user = session.query('User where id is ' + userId).one() + + for role in user['user_security_roles']: + if role['security_role']['name'] in roleList: + roleCheck = True + if roleCheck is True: + for entity in entities: + if entity.entity_type.lower() not in ['task', 'assetversion']: + discover = True + break return discover From 5818350ba2df84f47782fc6eebd484d17cf6937c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 4 Dec 2018 18:16:57 +0100 Subject: [PATCH 17/21] User's can't move entites --- pype/ftrack/actions/action_syncToAvalon.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pype/ftrack/actions/action_syncToAvalon.py b/pype/ftrack/actions/action_syncToAvalon.py index 829ac3dedc..c354f2332d 100644 --- a/pype/ftrack/actions/action_syncToAvalon.py +++ b/pype/ftrack/actions/action_syncToAvalon.py @@ -35,9 +35,8 @@ class SyncToAvalon(BaseAction): - run 'Create Custom Attributes' action or do it manually (Not recommended) If Ftrack entity already has Custom Attribute 'avalon_mongo_id' that stores ID: - - names are checked -> shows error if names are not exact the same - - after sync is not allowed to change names! - - only way is to create new entity in ftrack with new name + - name, parents and silo are checked -> shows error if are not exact the same + - after sync it is not allowed to change names or move entities If ID in 'avalon_mongo_id' is empty string or is not found in DB: - tries to find entity by name @@ -281,6 +280,10 @@ class SyncToAvalon(BaseAction): elif avalon_asset['name'] != entity['name']: raise ValueError('You can\'t change name {} to {}, avalon DB won\'t work properly - please create new asset'.format(avalon_asset['name'], name)) + elif avalon_asset['silo'] != silo or avalon_asset['data']['parents'] != data['parents']: + old_path = "/".join(avalon_asset['data']['parents']) + new_path = "/".join(data['parents']) + raise ValueError('You can\'t move with entities. Entity "{}" was moved from "{}" to "{}" '.format(avalon_asset['name'], old_path, new_path)) # Update info io.update_many({'type': 'asset','name': name}, From bf30d1ef822cec56fca68cb394bf3496562d452b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 4 Dec 2018 18:51:27 +0100 Subject: [PATCH 18/21] insert 'v' before {version:0>3} --- pype/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/lib.py b/pype/lib.py index 0b9e066703..b0f427aeb6 100644 --- a/pype/lib.py +++ b/pype/lib.py @@ -355,7 +355,7 @@ def get_avalon_project_template(): """ template = Templates(type=["anatomy"]) proj_template = {} - proj_template['workfile'] = '{asset[name]}_{task[name]}_{version:0>3}<_{comment}>' + proj_template['workfile'] = '{asset[name]}_{task[name]}_v{version:0>3}<_{comment}>' proj_template['work'] = '{root}/{project}/{hierarchy}/{asset}/work/{task}' proj_template['publish'] = '{root}/{project}/{hierarchy}/{asset}/publish/{family}/{subset}/v{version}/{projectcode}_{asset}_{subset}_v{version}.{representation}' # TODO this down should work but it can't be in default.toml: From 33a59a52e85334f4dfbe9685954e6ad491246c15 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 5 Dec 2018 01:03:41 +0100 Subject: [PATCH 19/21] changing the file gather for ftrack publishing a bit. fixes gathering of image sequences --- .../ftrack/integrate_ftrack_instances.py | 21 +++++--- pype/plugins/global/publish/integrate.py | 15 +++--- pype/plugins/nuke/publish/collect_writes.py | 2 +- .../nuke/publish/integrate_rendered_frames.py | 52 ++++++++++--------- .../nuke/publish/validate_collection.py | 8 +++ 5 files changed, 58 insertions(+), 40 deletions(-) diff --git a/pype/plugins/ftrack/integrate_ftrack_instances.py b/pype/plugins/ftrack/integrate_ftrack_instances.py index 9a0a36a413..e853976b0f 100644 --- a/pype/plugins/ftrack/integrate_ftrack_instances.py +++ b/pype/plugins/ftrack/integrate_ftrack_instances.py @@ -1,5 +1,6 @@ import pyblish.api import os +import clique class IntegrateFtrackInstance(pyblish.api.InstancePlugin): @@ -20,7 +21,8 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): 'rig': 'rig', 'setdress': 'setdress', 'pointcache': 'cache', - 'review': 'mov'} + 'review': 'mov', + 'write': 'comp'} def process(self, instance): @@ -36,20 +38,25 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): componentList = [] - transfers = instance.data["transfers"] + dst_list = instance.data['destination_list'] ft_session = instance.context.data["ftrackSession"] location = ft_session.query( 'Location where name is "ftrack.unmanaged"').one() self.log.debug('location {}'.format(location)) - for src, dest in transfers: - filename, ext = os.path.splitext(src) - self.log.debug('source filename: ' + filename) - self.log.debug('source ext: ' + ext) + for file in instance.data['destination_list']: + self.log.debug('file {}'.format(file)) + + for file in dst_list: + filename, ext = os.path.splitext(file) + self.log.debug('dest ext: ' + ext) componentList.append({"assettype_data": { "short": asset_type, + }, + "asset_data": { + "name": instance.data["subset"], }, "assetversion_data": { "version": version_number, @@ -57,7 +64,7 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): "component_data": { "name": ext[1:], # Default component name is "main". }, - "component_path": dest, + "component_path": file, 'component_location': location, "component_overwrite": False, } diff --git a/pype/plugins/global/publish/integrate.py b/pype/plugins/global/publish/integrate.py index e20f59133c..4a55cd9e22 100644 --- a/pype/plugins/global/publish/integrate.py +++ b/pype/plugins/global/publish/integrate.py @@ -136,12 +136,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # \|________| # root = api.registered_root() - # template_data = {"root": root, - # "project": PROJECT, - # "silo": asset['silo'], - # "asset": ASSET, - # "subset": subset["name"], - # "version": version["name"]} + hierarchy = io.find_one({"type":'asset', "name":ASSET})['data']['parents'] if hierarchy: # hierarchy = os.path.sep.join(hierarchy) @@ -163,6 +158,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # Find the representations to transfer amongst the files # Each should be a single representation (as such, a single extension) representations = [] + destination_list = [] for files in instance.data["files"]: @@ -195,6 +191,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): dst = anatomy_filled.publish.path instance.data["transfers"].append([src, dst]) + template = anatomy.publish.path else: # Single file @@ -218,13 +215,14 @@ class IntegrateAsset(pyblish.api.InstancePlugin): dst = anatomy_filled.publish.path instance.data["transfers"].append([src, dst]) + template = anatomy.publish.path representation = { "schema": "pype:representation-2.0", "type": "representation", "parent": version_id, "name": ext[1:], - "data": {'path': dst}, + "data": {'path': dst, 'template': template}, "dependencies": instance.data.get("dependencies", "").split(), # Imprint shortcut to context @@ -243,6 +241,9 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "representation": ext[1:] } } + + destination_list.append(dst) + instance.data['destination_list'] = destination_list representations.append(representation) self.log.info("Registering {} items".format(len(representations))) diff --git a/pype/plugins/nuke/publish/collect_writes.py b/pype/plugins/nuke/publish/collect_writes.py index db966fd84d..3a6a354b4b 100644 --- a/pype/plugins/nuke/publish/collect_writes.py +++ b/pype/plugins/nuke/publish/collect_writes.py @@ -73,7 +73,7 @@ class CollectNukeWrites(pyblish.api.ContextPlugin): "outputDir": output_dir, "ext": ext, "label": label, - "families": [families], + "families": [families, 'ftrack'], "firstFrame": first_frame, "lastFrame": last_frame, "outputType": output_type, diff --git a/pype/plugins/nuke/publish/integrate_rendered_frames.py b/pype/plugins/nuke/publish/integrate_rendered_frames.py index f482a48cda..e9447c517a 100644 --- a/pype/plugins/nuke/publish/integrate_rendered_frames.py +++ b/pype/plugins/nuke/publish/integrate_rendered_frames.py @@ -1,6 +1,7 @@ import os import logging import shutil +import clique import errno import pyblish.api @@ -30,7 +31,8 @@ class IntegrateFrames(pyblish.api.InstancePlugin): self.register(instance) self.log.info("Integrating Asset in to the database ...") - # self.integrate(instance) + if instance.data.get('transfer', True): + self.integrate(instance) def register(self, instance): @@ -124,12 +126,6 @@ class IntegrateFrames(pyblish.api.InstancePlugin): # \|________| # root = api.registered_root() - # template_data = {"root": root, - # "project": PROJECT, - # "silo": asset['silo'], - # "asset": ASSET, - # "subset": subset["name"], - # "version": version["name"]} hierarchy = io.find_one({"type":'asset', "name":ASSET})['data']['parents'] if hierarchy: # hierarchy = os.path.sep.join(hierarchy) @@ -139,6 +135,7 @@ class IntegrateFrames(pyblish.api.InstancePlugin): "project": {"name": PROJECT, "code": "prjX"}, "silo": asset['silo'], + "task": api.Session["AVALON_TASK"], "asset": ASSET, "family": instance.data['family'], "subset": subset["name"], @@ -151,6 +148,7 @@ class IntegrateFrames(pyblish.api.InstancePlugin): # Find the representations to transfer amongst the files # Each should be a single representation (as such, a single extension) representations = [] + destination_list = [] for files in instance.data["files"]: @@ -164,26 +162,30 @@ class IntegrateFrames(pyblish.api.InstancePlugin): # |_______| # if isinstance(files, list): + collection = files # Assert that each member has identical suffix - _, ext = os.path.splitext(collection[0]) - assert all(ext == os.path.splitext(name)[1] - for name in collection), ( - "Files had varying suffixes, this is a bug" - ) - - assert not any(os.path.isabs(name) for name in collection) - - template_data["representation"] = ext[1:] + dst_collection = [] for fname in collection: + filename, ext = os.path.splitext(fname) + _, frame = os.path.splitext(filename) + + template_data["representation"] = ext[1:] + template_data["frame"] = frame[1:] + src = os.path.join(stagingdir, fname) anatomy_filled = anatomy.format(template_data) - dst = anatomy_filled.publish.path + dst = anatomy_filled.render.path - # if instance.data.get('transfer', True): - # instance.data["transfers"].append([src, dst]) + dst_collection.append(dst) + instance.data["transfers"].append([src, dst]) + + template = anatomy.render.path + + collections, remainder = clique.assemble(dst_collection) + dst = collections[0].format('{head}{padding}{tail}') else: # Single file @@ -204,19 +206,17 @@ class IntegrateFrames(pyblish.api.InstancePlugin): src = os.path.join(stagingdir, fname) anatomy_filled = anatomy.format(template_data) - dst = anatomy_filled.publish.path + dst = anatomy_filled.render.path + template = anatomy.render.path + instance.data["transfers"].append([src, dst]) - # if instance.data.get('transfer', True): - # dst = src - # instance.data["transfers"].append([src, dst]) - representation = { "schema": "pype:representation-2.0", "type": "representation", "parent": version_id, "name": ext[1:], - "data": {'path': src}, + "data": {'path': dst, 'template': template}, "dependencies": instance.data.get("dependencies", "").split(), # Imprint shortcut to context @@ -235,6 +235,8 @@ class IntegrateFrames(pyblish.api.InstancePlugin): "representation": ext[1:] } } + destination_list.append(dst) + instance.data['destination_list'] = destination_list representations.append(representation) self.log.info("Registering {} items".format(len(representations))) diff --git a/pype/plugins/nuke/publish/validate_collection.py b/pype/plugins/nuke/publish/validate_collection.py index 4088272bc4..d3a2df4ead 100644 --- a/pype/plugins/nuke/publish/validate_collection.py +++ b/pype/plugins/nuke/publish/validate_collection.py @@ -45,6 +45,14 @@ class ValidateCollection(pyblish.api.InstancePlugin): assert remainder is not None, self.log.info("There are some extra files in folder") + basename, ext = os.path.splitext(list(collections[0])[0]) + assert all(ext == os.path.splitext(name)[1] + for name in collections[0]), self.log.info( + "Files had varying suffixes" + ) + + assert not any(os.path.isabs(name) for name in collections[0]), self.log.info("some file name are absolute") + self.log.info('frame_length: {}'.format(frame_length)) self.log.info('len(list(instance.data["files"])): {}'.format( len(list(instance.data["files"][0])))) From 23d7bc5f0c12fbfeef61aca56deaf1e6aa41ceb0 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 5 Dec 2018 01:39:14 +0100 Subject: [PATCH 20/21] add project code to publishes --- pype/plugins/global/publish/integrate.py | 8 +++----- pype/plugins/nuke/publish/integrate_rendered_frames.py | 7 +++---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/pype/plugins/global/publish/integrate.py b/pype/plugins/global/publish/integrate.py index 4a55cd9e22..698eb907e9 100644 --- a/pype/plugins/global/publish/integrate.py +++ b/pype/plugins/global/publish/integrate.py @@ -82,8 +82,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): self.log.debug("Establishing staging directory @ %s" % stagingdir) - project = io.find_one({"type": "project"}, - projection={"config.template.publish": True}) + project = io.find_one({"type": "project"}) asset = io.find_one({"type": "asset", "name": ASSET, @@ -136,7 +135,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # \|________| # root = api.registered_root() - hierarchy = io.find_one({"type":'asset', "name":ASSET})['data']['parents'] if hierarchy: # hierarchy = os.path.sep.join(hierarchy) @@ -144,7 +142,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): template_data = {"root": root, "project": {"name": PROJECT, - "code": "prjX"}, + "code": project['data']['code']}, "silo": asset['silo'], "asset": ASSET, "family": instance.data['family'], @@ -230,7 +228,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "context": { "root": root, "project": PROJECT, - "projectcode": "prjX", + "projectcode": project['data']['code'], 'task': api.Session["AVALON_TASK"], "silo": asset['silo'], "asset": ASSET, diff --git a/pype/plugins/nuke/publish/integrate_rendered_frames.py b/pype/plugins/nuke/publish/integrate_rendered_frames.py index e9447c517a..d02516b67f 100644 --- a/pype/plugins/nuke/publish/integrate_rendered_frames.py +++ b/pype/plugins/nuke/publish/integrate_rendered_frames.py @@ -72,8 +72,7 @@ class IntegrateFrames(pyblish.api.InstancePlugin): self.log.debug("Establishing staging directory @ %s" % stagingdir) - project = io.find_one({"type": "project"}, - projection={"config.template.publish": True}) + project = io.find_one({"type": "project"}) asset = io.find_one({"type": "asset", "name": ASSET, @@ -133,7 +132,7 @@ class IntegrateFrames(pyblish.api.InstancePlugin): template_data = {"root": root, "project": {"name": PROJECT, - "code": "prjX"}, + "code": project['data']['code']}, "silo": asset['silo'], "task": api.Session["AVALON_TASK"], "asset": ASSET, @@ -224,7 +223,7 @@ class IntegrateFrames(pyblish.api.InstancePlugin): "context": { "root": root, "project": PROJECT, - "projectcode": "prjX", + "projectcode": project['data']['code'], 'task': api.Session["AVALON_TASK"], "silo": asset['silo'], "asset": ASSET, From 112f4ca48db573a78be2e8123fd6d319e4a9c842 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 5 Dec 2018 11:15:45 +0100 Subject: [PATCH 21/21] add img and render to ftrack assets --- pype/plugins/ftrack/integrate_ftrack_instances.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pype/plugins/ftrack/integrate_ftrack_instances.py b/pype/plugins/ftrack/integrate_ftrack_instances.py index e853976b0f..441dd6f88d 100644 --- a/pype/plugins/ftrack/integrate_ftrack_instances.py +++ b/pype/plugins/ftrack/integrate_ftrack_instances.py @@ -22,7 +22,8 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): 'setdress': 'setdress', 'pointcache': 'cache', 'review': 'mov', - 'write': 'comp'} + 'write': 'img', + 'render': 'render'} def process(self, instance):