mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 08:24:53 +01:00
added Task import to syncToAvalon, preparing apps actions
This commit is contained in:
parent
f56fdc0700
commit
129ac28b21
20 changed files with 1251 additions and 120 deletions
|
|
@ -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"
|
||||
)
|
||||
32
pype/ftrack/actions/action_Apps.py
Normal file
32
pype/ftrack/actions/action_Apps.py
Normal 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)
|
||||
|
|
@ -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):
|
||||
|
|
|
|||
154
pype/ftrack/actions/action_sTa_opt.py
Normal file
154
pype/ftrack/actions/action_sTa_opt.py
Normal 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:]))
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
247
pype/ftrack/actions/action_syncToAvalon_v2.py
Normal file
247
pype/ftrack/actions/action_syncToAvalon_v2.py
Normal 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:]))
|
||||
|
|
@ -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
|
||||
|
||||
169
pype/ftrack/actions/connect_discoverApplications.py
Normal file
169
pype/ftrack/actions/connect_discoverApplications.py
Normal 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
|
||||
}
|
||||
51
pype/ftrack/create_custAttributes_AvalonId.py
Normal file
51
pype/ftrack/create_custAttributes_AvalonId.py
Normal 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()
|
||||
38
pype/ftrack/events/file_version_statuses.py
Normal file
38
pype/ftrack/events/file_version_statuses.py
Normal 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 ----------------------------------
|
||||
81
pype/ftrack/events/new_task_update.py
Normal file
81
pype/ftrack/events/new_task_update.py
Normal 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 ----------------------------------
|
||||
39
pype/ftrack/events/radio_buttons.py
Normal file
39
pype/ftrack/events/radio_buttons.py
Normal 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 ----------------------------------
|
||||
28
pype/ftrack/events/test_event.py
Normal file
28
pype/ftrack/events/test_event.py
Normal 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 ----------------------------------
|
||||
43
pype/ftrack/events/thumbnail_updates.py
Normal file
43
pype/ftrack/events/thumbnail_updates.py
Normal 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 ----------------------------------
|
||||
62
pype/ftrack/events/version_to_task_status.py
Normal file
62
pype/ftrack/events/version_to_task_status.py
Normal 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 ----------------------------------
|
||||
|
|
@ -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"""
|
||||
|
|
|
|||
16
pype/ftrack/test_events/test_event.py
Normal file
16
pype/ftrack/test_events/test_event.py
Normal 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 ----------------------------------
|
||||
16
pype/ftrack/test_events/test_event2.py
Normal file
16
pype/ftrack/test_events/test_event2.py
Normal 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 ----------------------------------
|
||||
3
pype/vendor/ftrack_action_handler/action.py
vendored
3
pype/vendor/ftrack_action_handler/action.py
vendored
|
|
@ -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,
|
||||
}]
|
||||
}
|
||||
|
||||
|
|
|
|||
240
pype/vendor/ftrack_action_handler/appaction.py
vendored
Normal file
240
pype/vendor/ftrack_action_handler/appaction.py
vendored
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue