added Task import to syncToAvalon, preparing apps actions

This commit is contained in:
Jakub Trllo 2018-10-25 09:45:06 +02:00
parent f56fdc0700
commit 129ac28b21
20 changed files with 1251 additions and 120 deletions

View file

@ -1,22 +0,0 @@
# Module Arrow,clique need to be installed!!!!!!!!!
import ftrack_api
# TO DO load config with column name that need to be imported/exported
import os
from avalon import api as avalon
from pyblish import api as pyblish
from avalon import io
#project = Avalonsession.find({"type": "project"})
# Action - On create/change/delete event chekc if data are send data to avalon
session = ftrack_api.Session(
server_url="https://pype.ftrackapp.com",
api_key="4e01eda0-24b3-4451-8e01-70edc03286be",
api_user="jakub.trllo"
)

View file

@ -0,0 +1,32 @@
import os
import logging
import ftrack_api
from ftrack_action_handler.appaction import AppAction
from avalon import io
os.environ['AVALON_PROJECTS']='tmp'
io.install()
projects = sorted(io.projects(), key=lambda x: x['name'])
io.uninstall()
# Temporary
s = ftrack_api.Session(
server_url="https://pype.ftrackapp.com",
api_key="4e01eda0-24b3-4451-8e01-70edc03286be",
api_user="jakub.trllo"
)
def register(session):
actions = []
for project in projects:
os.environ['AVALON_PROJECTS'] = project['name']
for app in project['config']['apps']:
actions.append(AppAction(session, project['name'], app['label'], app['name']))
for action in actions:
action.register()
print(actions)
session.event_hub.wait()
register(s)

View file

@ -3,43 +3,26 @@
import sys
import argparse
import logging
import collections
import os
import datetime
import json
import ftrack_api
from ftrack_action_handler.action import BaseAction
from avalon import io, inventory, schema
from avalon.vendor import toml
class JobKiller(BaseAction):
'''Edit meta data action.'''
#: Action identifier.
identifier = 'job.kill'
#: Action label.
label = 'Job Killer'
#: Action description.
description = 'Killing all running jobs younger than day'
def validate_selection(self, session, entities):
'''Return if *entities* is a valid selection.'''
# if (len(entities) != 1):
# # If entities contains more than one item return early since
# # metadata cannot be edited for several entites at the same time.
# return False
pass
# entity_type, entity_id = entities[0]
# if (
# entity_type not in session.types
# ):
# # Return False if the target entity does not have a metadata
# # attribute.
# return False
return True
@ -67,8 +50,11 @@ class JobKiller(BaseAction):
session.commit()
print('Complete')
return True
print('All running jobs were killed Successfully!')
return {
'success': True,
'message': 'All running jobs were killed Successfully!'
}
def register(session, **kw):

View file

@ -0,0 +1,154 @@
# :coding: utf-8
# :copyright: Copyright (c) 2017 ftrack
import sys
import argparse
import logging
import collections
import os
import json
import ftrack_api
from ftrack_action_handler.action import BaseAction
from avalon import io, inventory, schema
from avalon.vendor import toml
class TestAction(BaseAction):
'''Edit meta data action.'''
#: Action identifier.
identifier = 'test.action'
#: Action label.
label = 'Test action'
#: Action description.
description = 'Test action'
def validate_selection(self, session, entities):
'''Return if *entities* is a valid selection.'''
pass
return True
def discover(self, session, entities, event):
'''Return True if action is valid.'''
self.logger.info('Got selection: {0}'.format(entities))
return self.validate_selection(session, entities)
def launch(self, session, entities, event):
def fromTop(index, max, inEnt):
output=dict()
entity = session.get(inEnt[index]['type'], inEnt[index]['id'])
tasks=[]
for e in entity['children']:
if e.entity_type in ['Task']:
tasks.append(e['name'])
# Get info about all parents
if index < max:
output = {'name': entity['name'], 'tasks': tasks, 'childrens': fromTop(index+1, max, inEnt)}
# Get info about all childrens
else:
childrens = []
for e in entity['children']:
if e.entity_type not in ['Task']:
childrens.append(toBottom(e))
output = {'name': entity['name'], 'tasks': tasks, 'childrens': childrens}
return output
def toBottom(entity):
tasks = []
childrens = []
# If entity have childrens do:
if entity['children']:
# Get all tasks
for e in entity['children']:
if e.entity_type in ['Task']:
tasks.append(e['name'])
# Get childrens of children
for e in entity['children']:
if e.entity_type not in ['Task']:
childrens.append(toBottom(e))
return {'name': entity['name'], 'tasks': tasks, 'childrens': childrens}
def project(proj):
type = 'project'
addProjectToDatabase()
projId = getIdFromDb()
if len(proj['childrens'] > 0):
childrens = proj['childrens']
test(childrens, None, projId)
def test(childrens, parentId, projId):
type = 'asset'
for child in childrens:
silo = 'Assets' if child['type'] in ['AssetBuild', 'Library'] else 'Film'
addtodatabase()
newId = fromdatabase()
if len(child['childrens']) > 0:
test(child, newId, projId)
for entity in entities:
entity_type, entity_id = entity
entity = session.get(entity_type, entity_id)
max = len(entity['link']) - 1
out = fromTop(0, max, entity['link'])
print(100*"_")
print(out)
return True
def register(session, **kw):
'''Register plugin. Called when used as an plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
action_handler = TestAction(session)
action_handler.register()
def main(arguments=None):
'''Set up logging and register action.'''
if arguments is None:
arguments = []
parser = argparse.ArgumentParser()
# Allow setting of logging level from arguments.
loggingLevels = {}
for level in (
logging.NOTSET, logging.DEBUG, logging.INFO, logging.WARNING,
logging.ERROR, logging.CRITICAL
):
loggingLevels[logging.getLevelName(level).lower()] = level
parser.add_argument(
'-v', '--verbosity',
help='Set the logging output verbosity.',
choices=loggingLevels.keys(),
default='info'
)
namespace = parser.parse_args(arguments)
# Set up basic logging
logging.basicConfig(level=loggingLevels[namespace.verbosity])
session = ftrack_api.Session(
server_url="https://pype.ftrackapp.com",
api_key="4e01eda0-24b3-4451-8e01-70edc03286be",
api_user="jakub.trllo"
)
register(session)
# Wait for events
logging.info(
'Registered actions and listening for events. Use Ctrl-C to abort.'
)
session.event_hub.wait()
if __name__ == '__main__':
raise SystemExit(main(sys.argv[1:]))

View file

@ -3,9 +3,7 @@
import sys
import argparse
import logging
import collections
import os
import json
import ftrack_api
from ftrack_action_handler.action import BaseAction
@ -32,7 +30,6 @@ class SyncToAvalon(BaseAction):
# # If entities contains more than one item return early since
# # metadata cannot be edited for several entites at the same time.
# return False
pass
# entity_type, entity_id = entities[0]
# if (
# entity_type not in session.types
@ -40,7 +37,7 @@ class SyncToAvalon(BaseAction):
# # Return False if the target entity does not have a metadata
# # attribute.
# return False
pass
return True
def discover(self, session, entities, event):
@ -49,6 +46,7 @@ class SyncToAvalon(BaseAction):
self.logger.info('Got selection: {0}'.format(entities))
return self.validate_selection(session, entities)
def importToAvalon(self, session, entity):
eLinks = []
custAttrName = 'avalon_mongo_id'
@ -60,7 +58,7 @@ class SyncToAvalon(BaseAction):
name = e['name']
else:
name = e['name'].replace(" ", "-")
print("Name of "+tmp.entity_type+" was changed from "+e['name']+" to "+name)
print("Name of "+tmp.entity_type+" - "+e['name']+" was changed to "+name)
eLinks.append({"type": tmp.entity_type, "name": name, "ftrackId": tmp['id']})
@ -81,7 +79,7 @@ class SyncToAvalon(BaseAction):
except IOError:
raise
# Create project in Avalon
# --- Create project and assets in Avalon ---
io.install()
# Check if project exists -> Create project
if (io.find_one(
@ -93,30 +91,32 @@ class SyncToAvalon(BaseAction):
if custAttrName in entityProj['custom_attributes'] and entityProj['custom_attributes'][custAttrName] is '':
entityProj['custom_attributes'][custAttrName] = str(projectId)
# If entity is Project or Silo kill action
if (len(eLinks) > 2) and not (eLinks[-1]['type'] in ['Project']):
silo = eLinks[1]
# If entity is Project or have only 1 entity kill action
if (len(eLinks) > 1) and not (eLinks[-1]['type'] in ['Project']):
# TODO how to check if entity is Asset Library or AssetBuild?
silo = 'Assets' if eLinks[-1]['type'] in ['AssetBuild', 'Library'] else 'Film'
os.environ['AVALON_SILO'] = silo
# Create Assets
assets = []
for i in range(2, len(eLinks)):
for i in range(1, len(eLinks)):
assets.append(eLinks[i])
folderStruct = []
folderStruct.append(silo['name'])
parentId = None
data = {'visualParent': parentId, 'parents': folderStruct,
'ftrackId': None, 'entityType': None}
for asset in assets:
os.environ['AVALON_ASSET'] = asset['name']
data.update({'ftrackId': asset['ftrackId'], 'entityType': asset['type']})
if (io.find_one({'type': 'asset', 'name': asset['name']}) is None):
inventory.create_asset(asset['name'], silo['name'], data, projectId)
inventory.create_asset(asset['name'], silo, data, projectId)
print("Asset "+asset['name']+" created")
else:
# TODO check if is asset in same folder!!! ???? FEATURE FOR FUTURE
# tmp = io.find_one({'type': 'asset', 'name': asset['name']})
print("Asset "+asset["name"]+" already exist")
parentId = io.find_one({'type': 'asset', 'name': asset['name']})['_id']
@ -144,6 +144,10 @@ class SyncToAvalon(BaseAction):
})
try:
#TODO It's better to have these env set, are they used anywhere?
os.environ['AVALON_PROJECTS'] = "tmp"
os.environ['AVALON_ASSET'] = "tmp"
os.environ['AVALON_SILO'] = "tmp"
importable = []
def getShotAsset(entity):
@ -167,8 +171,13 @@ class SyncToAvalon(BaseAction):
job['status'] = 'done'
session.commit()
except:
print('Synchronization to Avalon was successfull!')
except(e):
job['status'] = 'failed'
print('During synchronization to Avalon went something wrong!')
print(e)
return True
@ -213,7 +222,6 @@ def main(arguments=None):
session = ftrack_api.Session(
server_url="https://pype.ftrackapp.com",
api_key="4e01eda0-24b3-4451-8e01-70edc03286be",
api_user="jakub.trllo"
)
register(session)

View file

@ -0,0 +1,247 @@
# :coding: utf-8
# :copyright: Copyright (c) 2017 ftrack
import sys
import argparse
import logging
import os
import json
import ftrack_api
from ftrack_action_handler.action import BaseAction
from avalon import io, inventory, schema
from avalon.vendor import toml
class SyncToAvalon(BaseAction):
'''Edit meta data action.'''
#: Action identifier.
identifier = 'sync.to.avalon'
#: Action label.
label = 'SyncToAvalon'
#: Action description.
description = 'Send data from Ftrack to Avalon'
#: Action icon.
icon = 'https://cdn1.iconfinder.com/data/icons/hawcons/32/699650-icon-92-inbox-download-512.png'
def validate_selection(self, session, entities):
'''Return if *entities* is a valid selection.'''
# if (len(entities) != 1):
# # If entities contains more than one item return early since
# # metadata cannot be edited for several entites at the same time.
# return False
# entity_type, entity_id = entities[0]
# if (
# entity_type not in session.types
# ):
# # Return False if the target entity does not have a metadata
# # attribute.
# return False
pass
return True
def discover(self, session, entities, event):
'''Return True if action is valid.'''
self.logger.info('Got selection: {0}'.format(entities))
return self.validate_selection(session, entities)
def importToAvalon(self, session, entity):
eLinks = []
custAttrName = 'avalon_mongo_id'
# TODO read from file, which data are in scope???
# get needed info of entity and all parents
for e in entity['link']:
tmp = session.get(e['type'], e['id'])
if e['name'].find(" ") == -1:
name = e['name']
else:
name = e['name'].replace(" ", "-")
print("Name of "+tmp.entity_type+" - "+e['name']+" was changed to "+name)
eLinks.append({"type": tmp.entity_type, "name": name, "ftrackId": tmp['id']})
entityProj = session.get(eLinks[0]['type'], eLinks[0]['ftrackId'])
# set AVALON_PROJECT env
os.environ["AVALON_PROJECT"] = entityProj["full_name"]
# get schema of project TODO read different schemas based on project type
template = {"schema": "avalon-core:inventory-1.0"}
schema = entityProj['project_schema']['name']
fname = os.path.join(os.path.dirname(
os.path.realpath(__file__)),
(schema + '.toml'))
try:
with open(fname) as f:
config = toml.load(f)
except IOError:
raise
# --- Create project and assets in Avalon ---
io.install()
# Check if project exists -> Create project
if (io.find_one(
{"type": "project", "name": entityProj["full_name"]}) is None):
inventory.save(entityProj["full_name"], config, template)
# Store project Id
projectId = io.find_one({"type": "project", "name": entityProj["full_name"]})["_id"]
if custAttrName in entityProj['custom_attributes'] and entityProj['custom_attributes'][custAttrName] is '':
entityProj['custom_attributes'][custAttrName] = str(projectId)
# If entity is Project or have only 1 entity kill action
if (len(eLinks) > 1) and not (eLinks[-1]['type'] in ['Project']):
# TODO how to check if entity is Asset Library or AssetBuild?
silo = 'Assets' if eLinks[-1]['type'] in ['AssetBuild', 'Library'] else 'Film'
os.environ['AVALON_SILO'] = silo
# Create Assets
assets = []
for i in range(1, len(eLinks)):
assets.append(eLinks[i])
folderStruct = []
parentId = None
data = {'visualParent': parentId, 'parents': folderStruct,
'tasks':None, 'ftrackId': None, 'entityType': None}
for asset in assets:
os.environ['AVALON_ASSET'] = asset['name']
data.update({'ftrackId': asset['ftrackId'], 'entityType': asset['type']})
# Get tasks of each asset
assetEnt = session.get('TypedContext', asset['ftrackId'])
tasks = []
for child in assetEnt['children']:
if child.entity_type in ['Task']:
tasks.append(child['name'])
data.update({'tasks': tasks})
if (io.find_one({'type': 'asset', 'name': asset['name']}) is None):
# Create asset in DB
inventory.create_asset(asset['name'], silo, data, projectId)
print("Asset "+asset['name']+" - created")
else:
io.update_many({'type': 'asset','name': asset['name']},
{'$set':{'data':data}})
# TODO check if is asset in same folder!!! ???? FEATURE FOR FUTURE
print("Asset "+asset["name"]+" - already exist")
parentId = io.find_one({'type': 'asset', 'name': asset['name']})['_id']
data.update({'visualParent': parentId, 'parents': folderStruct})
folderStruct.append(asset['name'])
# Set custom attribute to avalon/mongo id of entity (parentID is last)
if custAttrName in entity['custom_attributes'] and entity['custom_attributes'][custAttrName] is '':
entity['custom_attributes'][custAttrName] = str(parentId)
io.uninstall()
def launch(self, session, entities, event):
# JOB SETTINGS
userId = event['source']['user']['id']
user = session.query('User where id is ' + userId).one()
job = session.create('Job', {
'user': user,
'status': 'running',
'data': json.dumps({
'description': 'Synch Ftrack to Avalon.'
})
})
try:
print("action <" + self.__class__.__name__ + "> is running")
#TODO It's better to have these env set, are they used anywhere?
os.environ['AVALON_PROJECTS'] = "tmp"
os.environ['AVALON_ASSET'] = "tmp"
os.environ['AVALON_SILO'] = "tmp"
importable = []
def getShotAsset(entity):
if not (entity.entity_type in ['Task']):
if entity not in importable:
importable.append(entity)
if entity['children']:
childrens = entity['children']
for child in childrens:
getShotAsset(child)
# get all entities separately
for entity in entities:
entity_type, entity_id = entity
act_ent = session.get(entity_type, entity_id)
getShotAsset(act_ent)
for e in importable:
self.importToAvalon(session, e)
job['status'] = 'done'
session.commit()
print('Synchronization to Avalon was successfull!')
except Exception as e:
job['status'] = 'failed'
print('During synchronization to Avalon went something wrong!')
print(e)
return True
def register(session, **kw):
'''Register plugin. Called when used as an plugin.'''
# Validate that session is an instance of ftrack_api.Session. If not,
# assume that register is being called from an old or incompatible API and
# return without doing anything.
if not isinstance(session, ftrack_api.session.Session):
return
action_handler = SyncToAvalon(session)
action_handler.register()
print("----- action - <" + action_handler.__class__.__name__ + "> - Has been registered -----")
def main(arguments=None):
'''Set up logging and register action.'''
if arguments is None:
arguments = []
parser = argparse.ArgumentParser()
# Allow setting of logging level from arguments.
loggingLevels = {}
for level in (
logging.NOTSET, logging.DEBUG, logging.INFO, logging.WARNING,
logging.ERROR, logging.CRITICAL
):
loggingLevels[logging.getLevelName(level).lower()] = level
parser.add_argument(
'-v', '--verbosity',
help='Set the logging output verbosity.',
choices=loggingLevels.keys(),
default='info'
)
namespace = parser.parse_args(arguments)
# Set up basic logging
logging.basicConfig(level=loggingLevels[namespace.verbosity])
session = ftrack_api.Session(
server_url="https://pype.ftrackapp.com",
api_key="4e01eda0-24b3-4451-8e01-70edc03286be",
)
register(session)
# Wait for events
logging.info(
'Registered actions and listening for events. Use Ctrl-C to abort.'
)
session.event_hub.wait()
if __name__ == '__main__':
raise SystemExit(main(sys.argv[1:]))

View file

@ -5,11 +5,10 @@ import argparse
import logging
import collections
import os
import json
import ftrack_api
from ftrack_action_handler.action import BaseAction
from avalon import io, inventory, schema
from avalon.vendor import toml
@ -28,19 +27,7 @@ class TestAction(BaseAction):
def validate_selection(self, session, entities):
'''Return if *entities* is a valid selection.'''
# if (len(entities) != 1):
# # If entities contains more than one item return early since
# # metadata cannot be edited for several entites at the same time.
# return False
pass
# entity_type, entity_id = entities[0]
# if (
# entity_type not in session.types
# ):
# # Return False if the target entity does not have a metadata
# # attribute.
# return False
return True
def discover(self, session, entities, event):
@ -50,17 +37,13 @@ class TestAction(BaseAction):
return self.validate_selection(session, entities)
def launch(self, session, entities, event):
""" JOB SETTING """
return True
def register(session, **kw):
'''Register plugin. Called when used as an plugin.'''
# Validate that session is an instance of ftrack_api.Session. If not,
# assume that register is being called from an old or incompatible API and
# return without doing anything.
if not isinstance(session, ftrack_api.session.Session):
return

View file

@ -0,0 +1,169 @@
def _discoverApplications(self):
'''Return a list of applications that can be launched from this host.
An application should be of the form:
dict(
'identifier': 'name_version',
'label': 'Name',
'variant': 'version',
'description': 'description',
'path': 'Absolute path to the file',
'version': 'Version of the application',
'icon': 'URL or name of predefined icon'
)
'''
applications = []
if sys.platform == 'darwin':
prefix = ['/', 'Applications']
elif sys.platform == 'win32':
prefix = ['C:\\', 'Program Files.*']
self.logger.debug(
'Discovered applications:\n{0}'.format(
pprint.pformat(applications)
)
)
return applications
class ApplicationLauncher(object):
'''Launch applications described by an application store.
Launched applications are started detached so exiting current process will
not close launched applications.
'''
def __init__(self, applicationStore):
'''Instantiate launcher with *applicationStore* of applications.
*applicationStore* should be an instance of :class:`ApplicationStore`
holding information about applications that can be launched.
'''
super(ApplicationLauncher, self).__init__()
self.logger = logging.getLogger(
__name__ + '.' + self.__class__.__name__
)
self.applicationStore = applicationStore
def launch(self, applicationIdentifier, context=None):
'''Launch application matching *applicationIdentifier*.
*context* should provide information that can guide how to launch the
application.
Return a dictionary of information containing:
success - A boolean value indicating whether application launched
successfully or not.
message - Any additional information (such as a failure message).
'''
# Look up application.
applicationIdentifierPattern = applicationIdentifier
if applicationIdentifierPattern == 'hieroplayer':
applicationIdentifierPattern += '*'
application = self.applicationStore.getApplication(
applicationIdentifierPattern
)
if application is None:
return {
'success': False,
'message': (
'{0} application not found.'
.format(applicationIdentifier)
)
}
# Construct command and environment.
command = self._getApplicationLaunchCommand(application, context)
environment = self._getApplicationEnvironment(application, context)
# Environment must contain only strings.
self._conformEnvironment(environment)
success = True
message = '{0} application started.'.format(application['label'])
try:
options = dict(
env=environment,
close_fds=True
)
# Ensure that current working directory is set to the root of the
# application being launched to avoid issues with applications
# locating shared libraries etc.
applicationRootPath = os.path.dirname(application['path'])
options['cwd'] = applicationRootPath
# Ensure subprocess is detached so closing connect will not also
# close launched applications.
if sys.platform == 'win32':
options['creationflags'] = subprocess.CREATE_NEW_CONSOLE
else:
options['preexec_fn'] = os.setsid
launchData = dict(
command=command,
options=options,
application=application,
context=context
)
ftrack.EVENT_HUB.publish(
ftrack.Event(
topic='ftrack.connect.application.launch',
data=launchData
),
synchronous=True
)
ftrack_connect.session.get_shared_session().event_hub.publish(
ftrack_api.event.base.Event(
topic='ftrack.connect.application.launch',
data=launchData
),
synchronous=True
)
# Reset variables passed through the hook since they might
# have been replaced by a handler.
command = launchData['command']
options = launchData['options']
application = launchData['application']
context = launchData['context']
self.logger.debug(
'Launching {0} with options {1}'.format(command, options)
)
process = subprocess.Popen(command, **options)
except (OSError, TypeError):
self.logger.exception(
'{0} application could not be started with command "{1}".'
.format(applicationIdentifier, command)
)
success = False
message = '{0} application could not be started.'.format(
application['label']
)
else:
self.logger.debug(
'{0} application started. (pid={1})'.format(
applicationIdentifier, process.pid
)
)
return {
'success': success,
'message': message
}

View file

@ -0,0 +1,51 @@
import ftrack_utils
import ftrack_api
session = ftrack_api.Session(
server_url="https://pype.ftrackapp.com",
api_key="4e01eda0-24b3-4451-8e01-70edc03286be",
api_user="jakub.trllo",
)
objTypes = set()
# TODO get all entity types ---- NOT TASK,MILESTONE,LIBRARY --> should be editable!!!
allObjTypes = session.query('ObjectType').all()
for object in range(len(allObjTypes)):
index = len(allObjTypes)-object-1
if (str(allObjTypes[index]['name']) in ['Task','Milestone','Library']):
allObjTypes.pop(index)
for k in allObjTypes:
print(k['name'])
# Name & Label for export Avalon-mongo ID to Ftrack
# allCustAttr = session.query('CustomAttributeConfiguration').all()
# curCustAttr = []
# for ca in allCustAttr:
# curCustAttr.append(ca['key'])
#
# custAttrName = 'avalon_mongo_id'
# custAttrLabel = 'Avalon/Mongo Id'
# custAttrType = session.query('CustomAttributeType where name is "text"').one()
# # TODO WHICH SECURITY ROLE IS RIGHT
# custAttrSecuRole = session.query('SecurityRole').all()
# for custAttrObjType in objTypes:
# # Create Custom attribute if not exists
# if custAttrName not in curCustAttr:
# session.create('CustomAttributeConfiguration', {
# 'entity_type': 'task',
# 'object_type_id': custAttrObjType['id'],
# 'type': custAttrType,
# 'label': custAttrLabel,
# 'key': custAttrName,
# 'default': '',
# 'write_security_roles': custAttrSecuRole,
# 'read_security_roles': custAttrSecuRole,
# 'config': json.dumps({'markdown': False}),
# })
# session.commit()

View file

@ -0,0 +1,38 @@
# import ftrack_api as local session
import ftrack_api
from utils import print_entity_head
#
session = ftrack_api.Session()
# ----------------------------------
def file_version_statuses(event):
'''Set new version status to data if version matches given types'''
# start of event procedure ----------------------------------
for entity in event['data'].get('entities', []):
# Filter to new assetversions
if (entity['entityType'] == 'assetversion'
and entity['action'] == 'add'):
print "\n\nevent script: {}".format(__file__)
print_entity_head.print_entity_head(entity, session)
version = session.get('AssetVersion', entity['entityId'])
asset_type = version['asset']['type']['name']
file_status = session.query(
'Status where name is "{}"'.format('data')).one()
# Setting task status
try:
if asset_type.lower() in ['cam', 'cache', 'rig', 'scene']:
version['status'] = file_status
except Exception as e:
print '!!! status couldnt be set [ {} ]'.format(e)
else:
print '>>> updated to [ {} ]'.format(file_status['name'])
session.commit()
# end of event procedure ----------------------------------

View file

@ -0,0 +1,81 @@
# import ftrack_api as local session
import ftrack_api
import operator
from utils import print_entity_head
#
session = ftrack_api.Session()
# ----------------------------------
def new_task_update(event):
''' Set next task to ready when previous task is \
completed. '''
# start of event procedure ----------------------------------
def get_next_task(task):
parent = task['parent']
# tasks = parent['tasks']
tasks = parent['children']
def sort_types(types):
data = {}
for t in types:
data[t] = t.get('sort')
data = sorted(data.items(), key=operator.itemgetter(1))
results = []
for item in data:
results.append(item[0])
return results
types_sorted = sort_types(session.query('Type'))
next_types = None
for t in types_sorted:
if t['id'] == task['type_id']:
next_types = types_sorted[(types_sorted.index(t) + 1):]
for nt in next_types:
for t in tasks:
if nt['id'] == t['type_id']:
return t
return None
for entity in event['data'].get('entities', []):
if (entity['entityType'] == 'task' and 'statusid' in entity['keys']):
print "\n\nevent script: {}".format(__file__)
print_entity_head.print_entity_head(entity, session)
task = session.get('Task', entity['entityId'])
status = session.get('Status',
entity['changes']['statusid']['new'])
state = status['state']['name']
next_task = get_next_task(task)
# Setting next task to NOT STARTED, if on NOT READY
if next_task and state == 'Done':
if next_task['status']['name'].lower() == 'not ready':
# Get path to task
path = task['name']
for p in task['ancestors']:
path = p['name'] + '/' + path
# Setting next task status
try:
status_to_set = session.query(
'Status where name is "{}"'.format('Ready')).one()
next_task['status'] = status_to_set
except Exception as e:
print '!!! [ {} ] status couldnt be set: [ {} ]'.format(
path, e)
else:
print '>>> [ {} ] updated to [ Ready ]'.format(path)
session.commit()
# end of event procedure ----------------------------------

View file

@ -0,0 +1,39 @@
# import ftrack_api as local session
import ftrack_api
from utils import print_entity_head
#
session = ftrack_api.Session()
# ----------------------------------
def radio_buttons(event):
'''Provides a readio button behaviour to any bolean attribute in
radio_button group.'''
# start of event procedure ----------------------------------
for entity in event['data'].get('entities', []):
if entity['entityType'] == 'assetversion':
print "\n\nevent script: {}".format(__file__)
print_entity_head.print_entity_head(entity, session)
group = session.query(
'CustomAttributeGroup where name is "radio_button"').one()
radio_buttons = []
for g in group['custom_attribute_configurations']:
radio_buttons.append(g['key'])
for key in entity['keys']:
if (key in radio_buttons and entity['changes'] is not None):
if entity['changes'][key]['new'] == '1':
version = session.get('AssetVersion',
entity['entityId'])
asset = session.get('Asset', entity['parentId'])
for v in asset['versions']:
if version is not v:
v['custom_attributes'][key] = 0
session.commit()
# end of event procedure ----------------------------------

View file

@ -0,0 +1,28 @@
# import ftrack_api as local session
import ftrack_api
from utils import print_entity_head
#
session = ftrack_api.Session()
# ----------------------------------
def test_event(event):
'''just a testing event'''
# start of event procedure ----------------------------------
for entity in event['data'].get('entities', []):
if entity['entityType'] == 'task' and entity['action'] == 'update':
print "\n\nevent script: {}".format(__file__)
print_entity_head.print_entity_head(entity, session)
# for k in task.keys():
# print k, task[k]
# print '\n'
# print task['assignments']
for e in entity.keys():
print '{0}: {1}'.format(e, entity[e])
# end of event procedure ----------------------------------

View file

@ -0,0 +1,43 @@
import ftrack_api
from utils import print_entity_head
#
session = ftrack_api.Session()
# ----------------------------------
def thumbnail_updates(event):
'''Update thumbnails automatically'''
# start of event procedure ----------------------------------
for entity in event['data'].get('entities', []):
# update created task thumbnail with first parent thumbnail
if entity['entityType'] == 'task' and entity['action'] == 'add':
print "\n\nevent script: {}".format(__file__)
print_entity_head.print_entity_head(entity, session)
task = session.get('TypedContext', entity['entityId'])
parent = task['parent']
if parent.get('thumbnail') and not task.get('thumbnail'):
task['thumbnail'] = parent['thumbnail']
print '>>> Updated thumbnail on [ %s/%s ]'.format(
parent['name'], task['name'])
# Update task thumbnail from published version
if entity['entityType'] == 'assetversion' and entity['action'] == 'encoded':
version = session.get('AssetVersion', entity['entityId'])
thumbnail = version.get('thumbnail')
task = version['task']
if thumbnail:
task['thumbnail'] = thumbnail
task['parent']['thumbnail'] = thumbnail
print '>>> Updating thumbnail for task and shot [ {} ]'.format(
task['name'])
session.commit()
# end of event procedure ----------------------------------

View file

@ -0,0 +1,62 @@
# import ftrack_api as local session
import ftrack_api
from utils import print_entity_head
#
session = ftrack_api.Session()
# ----------------------------------
def version_to_task_status(event):
'''Push version status to task'''
# start of event procedure ----------------------------------
for entity in event['data'].get('entities', []):
# Filter non-assetversions
if (entity['entityType'] == 'assetversion'
and 'statusid' in entity['keys']):
print "\n\nevent script: {}".format(__file__)
print_entity_head.print_entity_head(entity, session)
version = session.get('AssetVersion', entity['entityId'])
version_status = session.get('Status',
entity['changes']['statusid']['new'])
task_status = version_status
task = version['task']
print '>>> version status: [ {} ]'.format(version_status['name'])
status_to_set = None
# Filter to versions with status change to "render complete"
if version_status['name'].lower() == 'reviewed':
status_to_set = 'Change requested'
if version_status['name'].lower() == 'approved':
status_to_set = 'Complete'
if task['type']['name'] == 'Lighting':
status_to_set = 'To render'
print '>>> status to set: [ {} ]'.format(status_to_set)
if status_to_set is not None:
task_status = session.query(
'Status where name is "{}"'.format(status_to_set)).one()
#
# Proceed if the task status was set
if task_status:
# Get path to task
path = task['name']
for p in task['ancestors']:
path = p['name'] + '/' + path
# Setting task status
try:
task['status'] = task_status
session.commit()
except Exception as e:
print '!!! [ {} ] status couldnt be set: [ {} ]'.format(
path, e)
# print '{} status couldnt be set: {}'
else:
print '>>> [ {} ] updated to [ {} ]'.format(
path, task_status['name'])
# end of event procedure ----------------------------------

View file

@ -31,7 +31,7 @@ def deleteAssetsFromShotByName(shotId, assNm=None):
if nm == assNm:
a.delete()
# Created as action
def killRunningTasks(tm=None):
import datetime
import ftrack_api
@ -58,47 +58,6 @@ def killRunningTasks(tm=None):
print('Complete')
def createCustomAttr():
import ftrack_api
session = ftrack_api.Session()
objTypes = set() # for custom attribute creation
# TODO get all entity types ---- NOT TASK
# objTypes.add(session.query('ObjectType where name is ' + entity.entity_type).one())
# Name & Label for export Avalon-mongo ID to Ftrack
allCustAttr = session.query('CustomAttributeConfiguration').all()
curCustAttr = []
for ca in allCustAttr:
curCustAttr.append(ca['key'])
custAttrName = 'avalon_mongo_id'
custAttrLabel = 'Avalon/Mongo Id'
custAttrType = session.query('CustomAttributeType where name is "text"').one()
# TODO WHICH SECURITY ROLE IS RIGHT
custAttrSecuRole = session.query('SecurityRole').all()
for custAttrObjType in objTypes:
# custAttrObjType = session.query('ObjectType where name is ' + entity.entity_type).one()
# Create Custom attribute if not exists
if custAttrName not in curCustAttr:
session.create('CustomAttributeConfiguration', {
'entity_type': 'task',
'object_type_id': custAttrObjType['id'],
'type': custAttrType,
'label': custAttrLabel,
'key': custAttrName,
'default': '',
'write_security_roles': custAttrSecuRole,
'read_security_roles': custAttrSecuRole,
'config': json.dumps({'markdown': False}),
})
session.commit()
def checkRegex():
# _handle_result -> would be solution?
# """ TODO Check if name of entities match REGEX"""

View file

@ -0,0 +1,16 @@
# import ftrack_api as local session
import ftrack_api
#
session = ftrack_api.Session()
# ----------------------------------
def test_event(event):
'''just a testing event'''
# start of event procedure ----------------------------------
for entity in event['data'].get('entities', []):
print(100*"_")
print(entity['changes'])
# end of event procedure ----------------------------------

View file

@ -0,0 +1,16 @@
# import ftrack_api as local session
import ftrack_api
#
session = ftrack_api.Session()
# ----------------------------------
def test_event(event):
'''just a testing event'''
# start of event procedure ----------------------------------
for entity in event['data'].get('entities', []):
print(100*"_")
print(entity['keys'])
# end of event procedure ----------------------------------

View file

@ -23,6 +23,7 @@ class BaseAction(object):
variant = None
identifier = None
description = None
icon = None
def __init__(self, session):
'''Expects a ftrack_api.Session instance'''
@ -77,7 +78,7 @@ class BaseAction(object):
'variant': self.variant,
'description': self.description,
'actionIdentifier': self.identifier,
'icon': self.icon,
}]
}

View file

@ -0,0 +1,240 @@
# :coding: utf-8
# :copyright: Copyright (c) 2017 ftrack
import logging
import getpass
import ftrack_api
class AppAction(object):
'''Custom Action base class
<label> - a descriptive string identifing your action.
<varaint> - To group actions together, give them the same
label and specify a unique variant per action.
<identifier> - a unique identifier for app.
<description> - a verbose descriptive text for you action
<icon> - icon in ftrack
'''
label = None
variant = None
identifier = None
description = None
icon = None
def __init__(self, session, project, label, name, variant=None, description=None, icon=None):
'''Expects a ftrack_api.Session instance'''
self.logger = logging.getLogger(
'{0}.{1}'.format(__name__, self.__class__.__name__)
)
if label is None:
raise ValueError('Action missing label.')
elif name is None:
raise ValueError('Action missing identifier.')
elif project is None:
raise ValueError('Action missing project name.')
self._session = session
self.project = project
self.label = label
self.identifier = name
@property
def session(self):
'''Return current session.'''
return self._session
def register(self):
'''Registers the action, subscribing the the discover and launch topics.'''
self.session.event_hub.subscribe(
'topic=ftrack.action.discover', self._discover
)
# self.session.event_hub.subscribe(
# 'topic=ftrack.action.discover and source.user.username={0}'.format(
# getpass.getuser()
# ), self._discover
# )
self.session.event_hub.subscribe(
'topic=ftrack.action.launch and data.actionIdentifier={0}'.format(
self.identifier
),
self._launch
)
def _discover(self, event):
args = self._translate_event(
self.session, event
)
accepts = self.discover(
self.session, *args
)
if accepts:
return {
'items': [{
'label': self.label,
'variant': self.variant,
'description': self.description,
'actionIdentifier': self.identifier,
'icon': self.icon,
}]
}
def discover(self, session, entities, event):
'''Return true if we can handle the selected entities.
*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
'''
return False
def _translate_event(self, session, event):
'''Return *event* translated structure to be used with the API.'''
_selection = event['data'].get('selection', [])
_entities = list()
for entity in _selection:
_entities.append(
(
self._get_entity_type(entity), entity.get('entityId')
)
)
return [
_entities,
event
]
def _get_entity_type(self, entity):
'''Return translated entity type tht can be used with API.'''
# 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()
for schema in self.session.schemas:
alias_for = schema.get('alias_for')
if (
alias_for and isinstance(alias_for, str) and
alias_for.lower() == entity_type
):
return schema['id']
for schema in self.session.schemas:
if schema['id'].lower() == entity_type:
return schema['id']
raise ValueError(
'Unable to translate entity type: {0}.'.format(entity_type)
)
def _launch(self, event):
args = self._translate_event(
self.session, event
)
interface = self._interface(
self.session, *args
)
if interface:
return interface
response = self.launch(
self.session, *args
)
return self._handle_result(
self.session, response, *args
)
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
'''
# TODO Delete this line
print("PRETEND APP IS RUNNING")
return True
def _interface(self, *args):
interface = self.interface(*args)
if interface:
return {
'items': interface
}
def interface(self, session, entities, event):
'''Return a interface if applicable or None
*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
'''
return None
def _handle_result(self, session, result, entities, event):
'''Validate the returned result from the action callback'''
if isinstance(result, bool):
result = {
'success': result,
'message': (
'{0} launched successfully.'.format(
self.label
)
)
}
elif isinstance(result, dict):
for key in ('success', 'message'):
if key in result:
continue
raise KeyError(
'Missing required key: {0}.'.format(key)
)
else:
self.logger.error(
'Invalid result type must be bool or dictionary!'
)
return result