From a4ec8b54f58c189f3c664264f1fd85d8fe334cff Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 13 Dec 2018 11:33:35 +0100 Subject: [PATCH] - added show_message to BaseAction - added priorities to register - test action show only to Pypeclub user - custom attributes can be created from json file (instructions inside) --- .../actions/action_createCustomAttributes.py | 246 -------- .../actions/action_create_cust_attrs.py | 576 ++++++++++++++++++ .../actions/action_sync_to_avalon_local.py | 2 +- pype/ftrack/actions/action_test.py | 13 +- pype/ftrack/actions/ftrack_action_handler.py | 44 +- 5 files changed, 627 insertions(+), 254 deletions(-) delete mode 100644 pype/ftrack/actions/action_createCustomAttributes.py create mode 100644 pype/ftrack/actions/action_create_cust_attrs.py diff --git a/pype/ftrack/actions/action_createCustomAttributes.py b/pype/ftrack/actions/action_createCustomAttributes.py deleted file mode 100644 index d45a92a01d..0000000000 --- a/pype/ftrack/actions/action_createCustomAttributes.py +++ /dev/null @@ -1,246 +0,0 @@ -# :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 import BaseAction - -from avalon import io, inventory, lib -from avalon.vendor import toml - - -class AvalonIdAttribute(BaseAction): - '''Edit meta data action.''' - - #: Action identifier. - identifier = 'avalon.id.attribute' - #: Action label. - label = 'Create Avalon Attribute' - #: Action description. - description = 'Creates Avalon/Mongo ID for double check' - - - def discover(self, session, entities, event): - ''' - Validation - - action is only for Administrators - ''' - success = False - userId = event['source']['user']['id'] - user = session.query('User where id is ' + userId).one() - for role in user['user_security_roles']: - if role['security_role']['name'] == 'Administrator': - success = True - - return success - - - 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': 'Custom Attribute creation.' - }) - }) - session.commit() - try: - # Checkbox for event sync - cbxSyncName = 'avalon_auto_sync' - cbxSyncLabel = 'Avalon auto-sync' - cbxSyncExist = False - - # Attribute Name and Label - custAttrName = 'avalon_mongo_id' - custAttrLabel = 'Avalon/Mongo Id' - - attrs_update = set() - # Types that don't need object_type_id - base = {'show'} - - # Don't create custom attribute on these entity types: - exceptions = ['task', 'milestone'] - exceptions.extend(base) - # Get all possible object types - all_obj_types = session.query('ObjectType').all() - count_types = len(all_obj_types) - # Filter object types by exceptions - for index in range(count_types): - i = count_types - 1 - index - name = all_obj_types[i]['name'].lower() - - if " " in name: - name = name.replace(" ","") - - if name in exceptions: - all_obj_types.pop(i) - - # Get IDs of filtered object types - all_obj_types_id = set() - - for obj in all_obj_types: - all_obj_types_id.add(obj['id']) - - # Get all custom attributes - current_cust_attr = session.query('CustomAttributeConfiguration').all() - # Filter already existing AvalonMongoID attr. - for attr in current_cust_attr: - if attr['key'] == cbxSyncName: - cbxSyncExist = True - cbxAttribute = attr - if attr['key'] == custAttrName: - if attr['entity_type'] in base: - base.remove(attr['entity_type']) - attrs_update.add(attr) - if attr['object_type_id'] in all_obj_types_id: - all_obj_types_id.remove(attr['object_type_id']) - attrs_update.add(attr) - - # Set session back to begin("session.query" raises error on commit) - session.rollback() - # Set security roles for attribute - role_api = session.query('SecurityRole where name is "API"').one() - role_admin = session.query('SecurityRole where name is "Administrator"').one() - roles = [role_api,role_admin] - - # Set Text type of Attribute - custom_attribute_type = session.query( - 'CustomAttributeType where name is "text"' - ).one() - # Get/Set 'avalon' group - groups = session.query('CustomAttributeGroup where name is "avalon"').all() - if len(groups) > 1: - msg = "There are more Custom attribute groups with name 'avalon'" - self.log.warning(msg) - return { 'success': False, 'message':msg } - - elif len(groups) < 1: - group = session.create('CustomAttributeGroup', { - 'name': 'avalon', - }) - session.commit() - else: - group = groups[0] - - # Checkbox for auto-sync event / Create or Update(roles + group) - if cbxSyncExist is False: - cbxType = session.query('CustomAttributeType where name is "boolean"').first() - session.create('CustomAttributeConfiguration', { - 'entity_type': 'show', - 'type': cbxType, - 'label': cbxSyncLabel, - 'key': cbxSyncName, - 'default': False, - 'write_security_roles': roles, - 'read_security_roles': roles, - 'group':group, - }) - else: - cbxAttribute['write_security_roles'] = roles - cbxAttribute['read_security_roles'] = roles - cbxAttribute['group'] = group - - for entity_type in base: - # Create a custom attribute configuration. - session.create('CustomAttributeConfiguration', { - 'entity_type': entity_type, - 'type': custom_attribute_type, - 'label': custAttrLabel, - 'key': custAttrName, - 'default': '', - 'write_security_roles': roles, - 'read_security_roles': roles, - 'group':group, - 'config': json.dumps({'markdown': False}) - }) - - for type in all_obj_types_id: - # Create a custom attribute configuration. - session.create('CustomAttributeConfiguration', { - 'entity_type': 'task', - 'object_type_id': type, - 'type': custom_attribute_type, - 'label': custAttrLabel, - 'key': custAttrName, - 'default': '', - 'write_security_roles': roles, - 'read_security_roles': roles, - 'group':group, - 'config': json.dumps({'markdown': False}) - }) - - for attr in attrs_update: - attr['write_security_roles'] = roles - attr['read_security_roles'] = roles - attr['group'] = group - - job['status'] = 'done' - session.commit() - - except Exception as e: - session.rollback() - job['status'] = 'failed' - session.commit() - self.log.error("Creating custom attributes failed ({})".format(e)) - - return True - - -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 = AvalonIdAttribute(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() - 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:])) diff --git a/pype/ftrack/actions/action_create_cust_attrs.py b/pype/ftrack/actions/action_create_cust_attrs.py new file mode 100644 index 0000000000..bd0a03b475 --- /dev/null +++ b/pype/ftrack/actions/action_create_cust_attrs.py @@ -0,0 +1,576 @@ +# :coding: utf-8 +# :copyright: Copyright (c) 2017 ftrack +import os +import sys +import argparse +import json +import ftrack_api +from ftrack_action_handler import BaseAction + +""" +This action creates/updates custom attributes. +- first part take care about avalon_mongo_id attribute +- second part is based on json file in templates: + ~/PYPE-TEMPLATES/presets/ftrack/ftrack_custom_attributes.json + - you can add Custom attributes based on these conditions + +*** Required *************************************************************** + +label (string) + - label that will show in ftrack + +key (string) + - must contain only chars [a-z0-9_] + +type (string) + - type of custom attribute + - possibilities: text, boolean, date, enumerator, dynamic enumerator, number + +*** Required with conditions *********************************************** + +entity_type (string) + - if 'is_hierarchical' is set to False + - type of entity + - possibilities: task, show, assetversion, user, list, asset + +config (dictionary) + - for each entity type different requirements and possibilities: + - enumerator: multiSelect = True/False(default: False) + data = {key_1:value_1,key_2:value_2,..,key_n:value_n} + - 'data' is Required value with enumerator + - 'key' must contain only chars [a-z0-9_] + + - number: isdecimal = True/False(default: False) + + - text: markdown = True/False(default: False) + +object_type (string) + - IF ENTITY_TYPE is set to 'task' + - default possibilities: Folder, Shot, Sequence, Task, Library, + Milestone, Episode, Asset Build,... + +*** Optional *************************************************************** + +write_security_roles/read_security_roles (array of strings) + - default: ["ALL"] + - strings should be role names (e.g.: ["API", "Administrator"]) + - if set to ["ALL"] - all roles will be availabled + - if first is 'except' - roles will be set to all except roles in array + - Warning: Be carefull with except - roles can be different by company + - example: + write_security_roles = ["except", "User"] + read_security_roles = ["ALL"] + - User is unable to write but can read + +group (string) + - default: None + - name of group + +default + - default: None + - sets default value for custom attribute: + - text -> string + - number -> integer + - enumerator -> array with string of key/s + - boolean -> bool true/false + - date -> string in format: 'YYYY.MM.DD' or 'YYYY.MM.DD HH:mm:ss' + - example: "2018.12.24" / "2018.1.1 6:0:0" + - dynamic enumerator -> DON'T HAVE DEFAULT VALUE!!! + +is_hierarchical (bool) + - default: False + - will set hierachical attribute + - False by default + +EXAMPLE: +{ + "avalon_auto_sync": { + "label": "Avalon auto-sync", + "key": "avalon_auto_sync", + "type": "boolean", + "entity_type": "show", + "group": "avalon", + "default": false, + "write_security_role": ["API","Administrator"], + "read_security_role": ["API","Administrator"] + } +} +""" + +class CustAttrException(Exception): + pass + +class CustomAttributes(BaseAction): + '''Edit meta data action.''' + + #: Action identifier. + identifier = 'create.update.attributes' + #: Action label. + label = 'Create/Update Avalon Attributes' + #: Action description. + description = 'Creates Avalon/Mongo ID for double check' + + def __init__(self, session): + super().__init__(session) + + templates = os.environ['PYPE_STUDIO_TEMPLATES'] + path_items = [templates,'presets','ftrack', 'ftrack_custom_attributes.json'] + self.filepath = os.path.sep.join(path_items) + # self.all_current_attributes = session.query('CustomAttributeConfiguration').all() + self.types = {} + self.object_type_ids = {} + self.groups = {} + self.security_roles = {} + self.required_keys = ['key', 'label', 'type'] + self.type_posibilities = ['text', 'boolean', 'date', 'enumerator', + 'dynamic enumerator', 'number'] + + def discover(self, session, entities, event): + ''' + Validation + - action is only for Administrators + ''' + success = False + userId = event['source']['user']['id'] + user = session.query('User where id is ' + userId).one() + for role in user['user_security_roles']: + if role['security_role']['name'] == 'Administrator': + success = True + + return success + + + 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': 'Custom Attribute creation.' + }) + }) + session.commit() + try: + self.avalon_mongo_id_attributes(session) + self.custom_attributes_from_file(session, event) + + job['status'] = 'done' + session.commit() + + except Exception as e: + session.rollback() + job['status'] = 'failed' + session.commit() + self.log.error("Creating custom attributes failed ({})".format(e)) + + return True + + def avalon_mongo_id_attributes(self, session): + # Attribute Name and Label + cust_attr_name = 'avalon_mongo_id' + cust_attr_label = 'Avalon/Mongo Id' + + # Types that don't need object_type_id + base = {'show'} + + # Don't create custom attribute on these entity types: + exceptions = ['task', 'milestone'] + exceptions.extend(base) + + # Get all possible object types + all_obj_types = session.query('ObjectType').all() + + # Filter object types by exceptions + filtered_types_id = set() + + for obj_type in all_obj_types: + name = obj_type['name'] + if " " in name: + name = name.replace(" ","") + + if obj_type['name'] not in self.object_type_ids: + self.object_type_ids[name] = obj_type['id'] + + if name.lower() not in exceptions: + filtered_types_id.add(obj_type['id']) + + # Set security roles for attribute + role_list = ["API","Administrator"] + roles = self.get_security_role(role_list) + # Set Text type of Attribute + custom_attribute_type = self.get_type('text') + # Set group to 'avalon' + group = self.get_group('avalon') + + data = {} + data['key'] = cust_attr_name + data['label'] = cust_attr_label + data['type'] = custom_attribute_type + data['default'] = '' + data['write_security_roles'] = roles + data['read_security_roles'] = roles + data['group'] = group + data['config'] = json.dumps({'markdown': False}) + + for entity_type in base: + data['entity_type'] = entity_type + self.process_attribute(data) + + data['entity_type'] = 'task' + for object_type_id in filtered_types_id: + data['object_type_id'] = str(object_type_id) + self.process_attribute(data) + + def custom_attributes_from_file(self, session, event): + try: + with open(self.filepath) as data_file: + json_dict = json.load(data_file) + except Exception as e: + msg = 'Loading "Custom attribute file" Failed. Please check log for more information' + self.log.warning("{} - {}".format(msg,str(e))) + self.show_message(event, msg) + return + + for cust_attr_name in json_dict: + try: + data = {} + cust_attr = json_dict[cust_attr_name] + # Get key, label, type + data.update(self.get_required(cust_attr)) + # Get hierachical/ entity_type/ object_id + data.update(self.get_entity_type(cust_attr)) + # Get group, default, security roles + data.update(self.get_optional(cust_attr)) + # Process data + self.process_attribute(data) + + except CustAttrException as cae: + msg = 'Custom attribute error "{}" - {}'.format(cust_attr_name, str(cae)) + self.log.warning(msg) + self.show_message(event, msg) + + return True + + def process_attribute(self, data): + existing_atr = self.session.query('CustomAttributeConfiguration').all() + matching = [] + for attr in existing_atr: + if (attr['key'] != data['key'] or + attr['type']['name'] != data['type']['name']): + continue + + if 'is_hierarchical' in data: + if data['is_hierarchical'] == attr['is_hierarchical']: + matching.append(attr) + elif 'object_type_id' in data: + if (attr['entity_type'] == data['entity_type'] and + attr['object_type_id'] == data['object_type_id']): + matching.append(attr) + else: + if attr['entity_type'] == data['entity_type']: + matching.append(attr) + + if len(matching) == 0: + self.session.create('CustomAttributeConfiguration', data) + self.session.commit() + + elif len(matching) == 1: + attr_update = matching[0] + for key in data: + if key not in ['is_hierarchical','entity_type', 'object_type_id']: + attr_update[key] = data[key] + self.session.commit() + + else: + raise CustAttrException("Is duplicated") + + + def get_required(self, attr): + output = {} + for key in self.required_keys: + if key not in attr: + raise CustAttrException("Key {} is required - please set".format(key)) + + if attr['type'].lower() not in self.type_posibilities: + raise CustAttrException("Type {} is not valid".format(attr['type'])) + + type_name = attr['type'].lower() + + output['key'] = attr['key'] + output['label'] = attr['label'] + output['type'] = self.get_type(type_name) + + config = None + if type_name == 'number': + config = self.get_number_config(attr) + elif type_name == 'text': + config = self.get_text_config(attr) + elif type_name == 'enumerator': + config = self.get_enumerator_config(attr) + + if config is not None: + output['config'] = config + + return output + + def get_number_config(self, attr): + if 'config' in attr and 'isdecimal' in attr['config']: + isdecimal = attr['config']['isdecimal'] + else: + isdecimal = False + + config = json.dumps({'isdecimal':isdecimal}) + + return config + + def get_text_config(self, attr): + if 'config' in attr and 'markdown' in attr['config']: + markdown = attr['config']['markdown'] + else: + markdown = False + config = json.dumps({'markdown':markdown}) + + return config + + def get_enumerator_config(self,attr): + if 'config' not in attr: + raise CustAttrException("Missing config with data") + if 'data' not in attr['config']: + raise CustAttrException("Missing data in config") + + data = [] + for item in attr['config']['data']: + item_data = {} + for key in item: + # TODO key check by regex + item_data['menu'] = item[key] + item_data['value'] = key + data.append(item_data) + + if 'multiSelect' in attr['config']: + multiSelect = attr['config']['multiSelect'] + else: + multiSelect = False + + config = json.dumps({ + 'multiSelect':multiSelect, + 'data': json.dumps(data) + }) + + return config + + def get_group(self, attr): + if isinstance(attr, str): + group_name = attr + else: + group_name = attr['group'].lower() + if group_name in self.groups: + return self.groups[group_name] + + query = 'CustomAttributeGroup where name is "{}"'.format(group_name) + groups = self.session.query(query).all() + + if len(groups) == 1: + group = groups[0] + self.groups[group_name] = group + + return group + + elif len(groups) < 1: + group = self.session.create('CustomAttributeGroup', { + 'name': group_name, + }) + self.session.commit() + + return group + + else: + raise CustAttrException("Found more than one group '{}'".format(group_name)) + + def get_role_ALL(self): + role_name = 'ALL' + if role_name in self.security_roles: + all_roles = self.security_roles[role_name] + else: + all_roles = self.session.query('SecurityRole').all() + self.security_roles[role_name] = all_roles + for role in all_roles: + if role['name'] not in self.security_roles: + self.security_roles[role['name']] = role + return all_roles + + def get_security_role(self, security_roles): + roles = [] + if len(security_roles) == 0 or security_roles[0] == 'ALL': + roles = self.get_role_ALL() + elif security_roles[0] == 'except': + excepts = security_roles[1:] + all = self.get_role_ALL() + for role in all: + if role['name'] not in excepts: + roles.append(role) + if role['name'] not in self.security_roles: + self.security_roles[role['name']] = role + else: + for role_name in security_roles: + if role_name in self.security_roles: + roles.append(self.security_roles[role_name]) + continue + + try: + query = 'SecurityRole where name is "{}"'.format(role_name) + role = self.session.query(query).one() + self.security_roles[role_name] = role + roles.append(role) + except Exception as e: + raise CustAttrException("Securit role '{}' does not exist".format(role_name)) + + return roles + + def get_default(self, attr): + type = attr['type'] + default = attr['default'] + err_msg = 'Default value is not' + if type == 'number': + if not isinstance(default, (float, int)): + raise CustAttrException('{} integer'.format(err_msg)) + elif type == 'text': + if not isinstance(default, str): + raise CustAttrException('{} string'.format(err_msg)) + elif type == 'boolean': + if not isinstance(default, bool): + raise CustAttrException('{} boolean'.format(err_msg)) + elif type == 'enumerator': + if not isinstance(default, array): + raise CustAttrException('{} array with strings'.format(err_msg)) + # TODO check if multiSelect is available and if default is one of data menu + if not isinstance(default[0], str): + raise CustAttrException('{} array of strings'.format(err_msg)) + elif type == 'date': + date_items = default.split(" ") + try: + if len(date_items) == 1: + default = arrow.get(default, 'YY.M.D') + elif len(date_items) == 2: + default = arrow.get(default, 'YY.M.D H:m:s') + else: + raise Exception + except Exception as e: + raise CustAttrException('Date is not in proper format') + elif type == 'dynamic enumerator': + raise CustAttrException('Dynamic enumerator can\'t have default') + + return default + + def get_optional(self, attr): + output = {} + if 'group' in attr: + output['group'] = self.get_group(attr) + if 'default' in attr: + output['default'] = self.get_default(attr) + + roles_read = [] + roles_write = [] + if 'read_security_roles' in output: + roles_read = attr['read_security_roles'] + if 'read_security_roles' in output: + roles_write = attr['write_security_roles'] + output['read_security_roles'] = self.get_security_role(roles_read) + output['write_security_roles'] = self.get_security_role(roles_write) + + return output + + def get_type(self, type_name): + if type_name in self.types: + return self.types[type_name] + + query = 'CustomAttributeType where name is "{}"'.format(type_name) + type = self.session.query(query).one() + self.types[type_name] = type + + return type + + def get_entity_type(self, attr): + if 'is_hierarchical' in attr: + if attr['is_hierarchical'] is True: + return {'is_hierarchical':True} + + if 'entity_type' not in attr: + raise CustAttrException('Missing entity_type') + + if attr['entity_type'].lower() != 'task': + return {'entity_type':attr['entity_type']} + + if 'object_type' not in attr: + raise CustAttrException('Missing object_type') + + object_type_name = attr['object_type'] + if object_type_name not in self.object_type_ids: + try: + query = 'ObjectType where name is "{}"'.format(object_type_name) + object_type_id = self.session.query(query).one()['id'] + except Exception as e: + raise CustAttrException('Object type with name "{}" don\'t exist'.format(object_type_name)) + self.object_type_ids[object_type_name] = object_type_id + else: + object_type_id = self.object_type_ids[object_type_name] + + return { + 'entity_type': attr['entity_type'], + 'object_type_id': object_type_id + } + +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 = CustomAttributes(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() + 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:])) diff --git a/pype/ftrack/actions/action_sync_to_avalon_local.py b/pype/ftrack/actions/action_sync_to_avalon_local.py index 04e9ed53a5..da08cad0fc 100644 --- a/pype/ftrack/actions/action_sync_to_avalon_local.py +++ b/pype/ftrack/actions/action_sync_to_avalon_local.py @@ -309,7 +309,7 @@ def register(session, **kw): return action_handler = SyncToAvalon(session) - action_handler.register() + action_handler.register(200) def main(arguments=None): diff --git a/pype/ftrack/actions/action_test.py b/pype/ftrack/actions/action_test.py index 7995443256..432dd44f70 100644 --- a/pype/ftrack/actions/action_test.py +++ b/pype/ftrack/actions/action_test.py @@ -27,8 +27,17 @@ class TestAction(BaseAction): def discover(self, session, entities, event): ''' Validation ''' + discover = False + roleList = ['Pypeclub'] + userId = event['source']['user']['id'] + user = session.query('User where id is ' + userId).one() - return True + for role in user['user_security_roles']: + if role['security_role']['name'] in roleList: + discover = True + break + + return discover def launch(self, session, entities, event): @@ -70,7 +79,7 @@ def register(session, **kw): return action_handler = TestAction(session) - action_handler.register() + action_handler.register(10000) def main(arguments=None): diff --git a/pype/ftrack/actions/ftrack_action_handler.py b/pype/ftrack/actions/ftrack_action_handler.py index de8b7b2845..db13e606bb 100644 --- a/pype/ftrack/actions/ftrack_action_handler.py +++ b/pype/ftrack/actions/ftrack_action_handler.py @@ -54,12 +54,12 @@ class AppAction(object): '''Return current session.''' return self._session - def register(self): + def register(self, priority = 100): '''Registers the action, subscribing the discover and launch topics.''' self.session.event_hub.subscribe( 'topic=ftrack.action.discover and source.user.username={0}'.format( self.session.api_user - ), self._discover + ), self._discover,priority=priority ) self.session.event_hub.subscribe( @@ -467,12 +467,15 @@ class BaseAction(object): def reset_session(self): self.session.reset() - def register(self): - '''Registers the action, subscribing the the discover and launch topics.''' + def register(self, priority = 100): + ''' + Registers the action, subscribing the the discover and launch topics. + - highest priority event will show last + ''' self.session.event_hub.subscribe( 'topic=ftrack.action.discover and source.user.username={0}'.format( self.session.api_user - ), self._discover + ), self._discover, priority=priority ) self.session.event_hub.subscribe( @@ -632,6 +635,37 @@ class BaseAction(object): ''' return None + def show_message(self, event, input_message, result = False): + """ + Shows message to user who triggered event + - event - just source of user id + - input_message - message that is shown to user + - result - changes color of message (based on ftrack settings) + - True = Violet + - False = Red + """ + if not isinstance(result, bool): + result = False + + try: + message = str(input_message) + except: + return + + user_id = event['source']['user']['id'] + self.session.event_hub.publish( + ftrack_api.event.base.Event( + topic='ftrack.action.trigger-user-interface', + data=dict( + type='message', + success=result, + message=message + ), + target='applicationId=ftrack.client.web and user.id="{0}"'.format(user_id) + ), + on_error='ignore' + ) + def _handle_result(self, session, result, entities, event): '''Validate the returned result from the action callback''' if isinstance(result, bool):