From 82e62723bf8fc7625ee76887896f72171d4a740a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Aug 2019 18:21:56 +0200 Subject: [PATCH] created remapping ftrack action that change attribute names in data of avalon entities (per project) --- .../actions/action_attributes_remapper.py | 261 ++++++++++++++++++ .../action_icons/AttributesRemapper.svg | 1 + 2 files changed, 262 insertions(+) create mode 100644 pype/ftrack/actions/action_attributes_remapper.py create mode 100644 res/ftrack/action_icons/AttributesRemapper.svg diff --git a/pype/ftrack/actions/action_attributes_remapper.py b/pype/ftrack/actions/action_attributes_remapper.py new file mode 100644 index 0000000000..056b62fda8 --- /dev/null +++ b/pype/ftrack/actions/action_attributes_remapper.py @@ -0,0 +1,261 @@ +import os + +from pype.vendor import ftrack_api +from pype.ftrack import BaseAction +from avalon.tools.libraryloader.io_nonsingleton import DbConnector + + +class AttributesRemapper(BaseAction): + '''Edit meta data action.''' + + #: Action identifier. + identifier = 'attributes.remapper' + #: Action label. + label = 'Attributes Remapper' + #: Action description. + description = 'Remaps attributes in avalon DB' + + #: roles that are allowed to register this action + role_list = ["Pypeclub", "Administrator"] + icon = '{}/ftrack/action_icons/AttributesRemapper.svg'.format( + os.environ.get('PYPE_STATICS_SERVER', '') + ) + + db_con = DbConnector() + keys_to_change = { + "fstart": "frameStart", + "startFrame": "frameStart", + "edit_in": "frameStart", + + "fend": "frameEnd", + "endFrame": "frameEnd", + "edit_out": "frameEnd", + + "handle_start": "handleStart", + "handle_end": "handleEnd", + "frameRate": "fps", + "framerate": "fps", + "resolution_width": "resolutionWidth", + "resolution_height": "resolutionHeight", + "pixel_aspect": "pixelAspect" + } + + def discover(self, session, entities, event): + ''' Validation ''' + + return True + + def interface(self, session, entities, event): + if event['data'].get('values', {}): + return + + title = 'Select Projects where attributes should be remapped' + + items = [] + + selection_enum = { + 'label': 'Process type', + 'type': 'enumerator', + 'name': 'process_type', + 'data': [ + { + 'label': 'Selection', + 'value': 'selection' + }, { + 'label': 'Inverted selection', + 'value': 'except' + } + ], + 'value': 'selection' + } + selection_label = { + 'type': 'label', + 'value': ( + 'Selection based variants:
' + '- `Selection` - ' + 'NOTHING is processed when nothing is selected
' + '- `Inverted selection` - ' + 'ALL Projects are processed when nothing is selected' + ) + } + + items.append(selection_enum) + items.append(selection_label) + + item_splitter = {'type': 'label', 'value': '---'} + + all_projects = session.query('Project').all() + for project in all_projects: + item_label = { + 'type': 'label', + 'value': '{} ({})'.format( + project['full_name'], project['name'] + ) + } + item = { + 'name': project['id'], + 'type': 'boolean', + 'value': False + } + if len(items) > 0: + items.append(item_splitter) + items.append(item_label) + items.append(item) + + if len(items) == 0: + return { + 'success': False, + 'message': 'Didn\'t found any projects' + } + else: + return { + 'items': items, + 'title': title + } + + def launch(self, session, entities, event): + if 'values' not in event['data']: + return + + values = event['data']['values'] + process_type = values.pop('process_type') + + selection = True + if process_type == 'except': + selection = False + + interface_messages = {} + + projects_to_update = [] + for project_id, update_bool in values.items(): + if not update_bool and selection: + continue + + if update_bool and not selection: + continue + + project = session.query( + 'Project where id is "{}"'.format(project_id) + ).one() + projects_to_update.append(project) + + if not projects_to_update: + self.log.debug('Nothing to update') + return { + 'success': True, + 'message': 'Nothing to update' + } + + + self.db_con.install() + + relevant_types = ["project", "asset", "version"] + + for ft_project in projects_to_update: + self.log.debug( + "Processing project \"{}\"".format(ft_project["full_name"]) + ) + + self.db_con.Session["AVALON_PROJECT"] = ft_project["full_name"] + project = self.db_con.find_one({'type': 'project'}) + if not project: + key = "Projects not synchronized to db" + if key not in interface_messages: + interface_messages[key] = [] + interface_messages[key].append(ft_project["full_name"]) + continue + + # Get all entities in project collection from MongoDB + _entities = self.db_con.find({}) + for _entity in _entities: + ent_t = _entity.get("type", "*unknown type") + name = _entity.get("name", "*unknown name") + + self.log.debug( + "- {} ({})".format(name, ent_t) + ) + + # Skip types that do not store keys to change + if ent_t.lower() not in relevant_types: + self.log.debug("-- skipping - type is not relevant") + continue + + # Get data which will change + updating_data = {} + source_data = _entity["data"] + + for key_from, key_to in self.keys_to_change.items(): + # continue if final key already exists + if key_to in source_data: + continue + + # continue if final key was set in update_data + if key_to in updating_data: + continue + + # continue if source key not exist or value is None + value = source_data.get(key_from) + if not value: + continue + + self.log.debug( + "-- changing key {} to {}".format(key_from, key_to) + ) + updating_data[key_to] = value + + # Pop out old keys from entity + is_obsolete = False + for key in self.keys_to_change: + if key not in source_data: + continue + is_obsolete = True + source_data.pop(key) + + # continue if there is nothing to change + if not is_obsolete and not updating_data: + self.log.debug("-- nothing to change") + continue + + source_data.update(updating_data) + + self.db_con.update_many( + {"_id": _entity["_id"]}, + {"$set": {"data": source_data}} + ) + + self.db_con.uninstall() + + if interface_messages: + self.show_interface_from_dict( + event, interface_messages, "Errors during remapping attributes" + ) + + return True + + def show_interface_from_dict(self, event, messages, title=""): + items = [] + + for key, value in messages.items(): + if not value: + continue + subtitle = {'type': 'label', 'value':'# {}'.format(key)} + items.append(subtitle) + if isinstance(value, list): + for item in value: + message = { + 'type': 'label', 'value': '

{}

'.format(item) + } + items.append(message) + else: + message = {'type': 'label', 'value': '

{}

'.format(value)} + items.append(message) + + self.show_interface(event, items, title) + +def register(session, **kw): + '''Register plugin. Called when used as an plugin.''' + + if not isinstance(session, ftrack_api.session.Session): + return + + AttributesRemapper(session).register() diff --git a/res/ftrack/action_icons/AttributesRemapper.svg b/res/ftrack/action_icons/AttributesRemapper.svg new file mode 100644 index 0000000000..94bf8c4f14 --- /dev/null +++ b/res/ftrack/action_icons/AttributesRemapper.svg @@ -0,0 +1 @@ + \ No newline at end of file