From 87a000227af72854798e19427536b45669f93226 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 4 May 2020 19:23:01 +0200 Subject: [PATCH 01/10] sync to avalon ignores MongoID attribute in hierarchical attributes --- pype/ftrack/events/event_sync_to_avalon.py | 2 ++ pype/ftrack/lib/avalon_sync.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/pype/ftrack/events/event_sync_to_avalon.py b/pype/ftrack/events/event_sync_to_avalon.py index faf75395404..71e52c68da0 100644 --- a/pype/ftrack/events/event_sync_to_avalon.py +++ b/pype/ftrack/events/event_sync_to_avalon.py @@ -1244,6 +1244,8 @@ def get_cust_attr_values(self, entity, keys=None): self.process_session, entity, hier_keys, defaults ) for key, val in hier_values.items(): + if key == CustAttrIdKey: + continue output[key] = val return output diff --git a/pype/ftrack/lib/avalon_sync.py b/pype/ftrack/lib/avalon_sync.py index 474c70bd260..179977d403b 100644 --- a/pype/ftrack/lib/avalon_sync.py +++ b/pype/ftrack/lib/avalon_sync.py @@ -877,6 +877,8 @@ def set_hierarchical_attribute(self, hier_attrs, sync_ids): project_values[key] = value for key in avalon_hier: + if key == CustAttrIdKey: + continue value = self.entities_dict[top_id]["avalon_attrs"][key] if value is not None: project_values[key] = value From 25b2c66ebee62d0e7de33e34ed997c23514b3d5c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 4 May 2020 19:26:42 +0200 Subject: [PATCH 02/10] security roles are queried effectivelly --- .../actions/action_create_cust_attrs.py | 80 +++++++++---------- 1 file changed, 36 insertions(+), 44 deletions(-) diff --git a/pype/ftrack/actions/action_create_cust_attrs.py b/pype/ftrack/actions/action_create_cust_attrs.py index 5279a95a20a..ff3a30b5346 100644 --- a/pype/ftrack/actions/action_create_cust_attrs.py +++ b/pype/ftrack/actions/action_create_cust_attrs.py @@ -138,7 +138,7 @@ def launch(self, session, entities, event): self.types = {} self.object_type_ids = {} self.groups = {} - self.security_roles = {} + self.security_roles = None # JOB SETTINGS userId = event['source']['user']['id'] @@ -199,8 +199,8 @@ def avalon_mongo_id_attributes(self, session): filtered_types_id.add(obj_type['id']) # Set security roles for attribute - role_list = ['API', 'Administrator'] - roles = self.get_security_role(role_list) + role_list = ("API", "Administrator", "Pypeclub") + roles = self.get_security_roles(role_list) # Set Text type of Attribute custom_attribute_type = self.get_type('text') # Set group to 'avalon' @@ -416,48 +416,40 @@ def get_group(self, attr): '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 = [] - security_roles_lowered = [role.lower() for role in security_roles] - if len(security_roles) == 0 or 'all' in security_roles_lowered: - roles = self.get_role_ALL() - elif security_roles_lowered[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 + def query_roles(self): + if self.security_roles is None: + self.security_roles = {} + for role in self.session.query("SecurityRole").all(): + key = role["name"].lower() + self.security_roles[key] = role + return self.security_roles + + def get_security_roles(self, security_roles): + security_roles = self.query_roles() + + security_roles_lowered = tuple(name.lower() for name in security_roles) + if ( + len(security_roles_lowered) == 0 + or "all" in security_roles_lowered + ): + return tuple(security_roles.values()) + + output = [] + if security_roles_lowered[0] == "except": + excepts = security_roles_lowered[1:] + for role_name, role in security_roles.items(): + if role_name not in excepts: + output.append(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 NoResultFoundError: + for role_name in security_roles_lowered: + if role_name in security_roles: + output.append(security_roles[role_name]) + else: raise CustAttrException(( - 'Securit role "{}" does not exist' + "Securit role \"{}\" was not found in Ftrack." ).format(role_name)) - - return roles + return output def get_default(self, attr): type = attr['type'] @@ -512,8 +504,8 @@ def get_optional(self, attr): 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) + output['read_security_roles'] = self.get_security_roles(roles_read) + output['write_security_roles'] = self.get_security_roles(roles_write) return output From 83352891585c3b4f567585d3f66a43d30b1de7ec Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 4 May 2020 19:27:03 +0200 Subject: [PATCH 03/10] few minor possible bugs fix --- pype/ftrack/actions/action_create_cust_attrs.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pype/ftrack/actions/action_create_cust_attrs.py b/pype/ftrack/actions/action_create_cust_attrs.py index ff3a30b5346..8ee1d138e3f 100644 --- a/pype/ftrack/actions/action_create_cust_attrs.py +++ b/pype/ftrack/actions/action_create_cust_attrs.py @@ -8,7 +8,6 @@ from pype.ftrack import BaseAction from pype.ftrack.lib.avalon_sync import CustAttrIdKey from pypeapp import config -from ftrack_api.exception import NoResultFoundError """ This action creates/updates custom attributes. @@ -382,15 +381,15 @@ def get_enumerator_config(self, attr): config = json.dumps({ 'multiSelect': multiSelect, 'data': json.dumps(data) - }) + }) return config def get_group(self, attr): - if isinstance(attr, str): - group_name = attr - else: + if isinstance(attr, dict): group_name = attr['group'].lower() + else: + group_name = attr if group_name in self.groups: return self.groups[group_name] From 1f2451df7377aa328f7984ee96167f28505baa23 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 4 May 2020 19:36:40 +0200 Subject: [PATCH 04/10] added method for converting mongoid attr from per entity type to hierarchical attribute --- .../actions/action_create_cust_attrs.py | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/pype/ftrack/actions/action_create_cust_attrs.py b/pype/ftrack/actions/action_create_cust_attrs.py index 8ee1d138e3f..12e40b7ee53 100644 --- a/pype/ftrack/actions/action_create_cust_attrs.py +++ b/pype/ftrack/actions/action_create_cust_attrs.py @@ -224,6 +224,94 @@ def avalon_mongo_id_attributes(self, session): data['object_type_id'] = str(object_type_id) self.process_attribute(data) + def convert_mongo_id_to_hierarchical( + self, hierarchical_attr, object_type_attrs, session, event + ): + user_msg = "Converting old custom attributes. This may take some time." + self.show_message(event, user_msg, True) + self.log.info(user_msg) + + object_types_per_id = { + object_type["id"]: object_type + for object_type in session.query("ObjectType").all() + } + + cust_attr_query = ( + "select value, entity_id from ContextCustomAttributeValue " + "where configuration_id is {}" + ) + for attr_def in object_type_attrs: + attr_ent_type = attr_def["entity_type"] + if attr_ent_type == "show": + entity_type_label = "Project" + elif attr_ent_type == "task": + entity_type_label = ( + object_types_per_id[attr_def["object_type_id"]] + ) + else: + self.log.warning( + "Unsupported entity type: \"{}\". Skipping.".format( + attr_ent_type + ) + ) + continue + + self.log.debug(( + "Converting Avalon MongoID attr for Entity type \"{}\"." + ).format(entity_type_label)) + + call_expr = [{ + "action": "query", + "expression": cust_attr_query.format(attr_def["id"]) + }] + if hasattr(session, "call"): + [values] = session.call(call_expr) + else: + [values] = session._call(call_expr) + + for value in values["data"]: + table_values = collections.OrderedDict({ + "configuration_id": hierarchical_attr["id"], + "entity_id": value["entity_id"] + }) + + session.recorded_operations.push( + ftrack_api.operation.UpdateEntityOperation( + "ContextCustomAttributeValue", + table_values, + "value", + ftrack_api.symbol.NOT_SET, + value["value"] + ) + ) + + try: + session.commit() + + except Exception: + session.rollback() + self.log.warning( + ( + "Couldn't transfer Avalon Mongo ID" + " attribute for entity type \"{}\"." + ).format(entity_type_label), + exc_info=True + ) + + try: + session.delete(attr_def) + session.commit() + + except Exception: + session.rollback() + self.log.warning( + ( + "Couldn't delete Avalon Mongo ID" + " attribute for entity type \"{}\"." + ).format(entity_type_label), + exc_info=True + ) + def custom_attributes_from_file(self, session, event): presets = config.get_presets()['ftrack']['ftrack_custom_attributes'] From f345da371520b96ba6cc021fdaea224510d66776 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 4 May 2020 19:47:19 +0200 Subject: [PATCH 05/10] removed standalone action support --- .../actions/action_create_cust_attrs.py | 42 ------------------- 1 file changed, 42 deletions(-) diff --git a/pype/ftrack/actions/action_create_cust_attrs.py b/pype/ftrack/actions/action_create_cust_attrs.py index 12e40b7ee53..2ff0a3b49d1 100644 --- a/pype/ftrack/actions/action_create_cust_attrs.py +++ b/pype/ftrack/actions/action_create_cust_attrs.py @@ -1,9 +1,6 @@ import os -import sys -import argparse import json import arrow -import logging import ftrack_api from pype.ftrack import BaseAction from pype.ftrack.lib.avalon_sync import CustAttrIdKey @@ -652,42 +649,3 @@ def register(session, plugins_presets={}): '''Register plugin. Called when used as an plugin.''' CustomAttributes(session, plugins_presets).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:])) From 9935972a859531014d81b7c71efc69a2c649ff05 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 4 May 2020 19:54:12 +0200 Subject: [PATCH 06/10] hierarchical mongo id attribute automatically replace previous access --- .../actions/action_create_cust_attrs.py | 193 +++++++++--------- 1 file changed, 95 insertions(+), 98 deletions(-) diff --git a/pype/ftrack/actions/action_create_cust_attrs.py b/pype/ftrack/actions/action_create_cust_attrs.py index 2ff0a3b49d1..c141d6672c4 100644 --- a/pype/ftrack/actions/action_create_cust_attrs.py +++ b/pype/ftrack/actions/action_create_cust_attrs.py @@ -1,4 +1,5 @@ import os +import collections import json import arrow import ftrack_api @@ -131,11 +132,6 @@ def discover(self, session, entities, event): return True def launch(self, session, entities, event): - self.types = {} - self.object_type_ids = {} - self.groups = {} - self.security_roles = None - # JOB SETTINGS userId = event['source']['user']['id'] user = session.query('User where id is ' + userId).one() @@ -149,7 +145,8 @@ def launch(self, session, entities, event): }) session.commit() try: - self.avalon_mongo_id_attributes(session) + self.prepare_global_data(session) + self.avalon_mongo_id_attributes(session, event) self.custom_attributes_from_file(session, event) job['status'] = 'done' @@ -166,60 +163,92 @@ def launch(self, session, entities, event): return True - def avalon_mongo_id_attributes(self, session): - # Attribute Name and Label - cust_attr_label = 'Avalon/Mongo Id' + def prepare_global_data(self, session): + self.types_per_name = { + attr_type["name"].lower(): attr_type + for attr_type in session.query("CustomAttributeType").all() + } - # Types that don't need object_type_id - base = {'show'} + self.security_roles = { + role["name"].lower(): role + for role in session.query("SecurityRole").all() + } - # Don't create custom attribute on these entity types: - exceptions = ['task', 'milestone'] - exceptions.extend(base) + object_types = session.query("ObjectType").all() + self.object_types_per_id = { + object_type["id"]: object_type for object_type in object_types + } + self.object_types_per_name = { + object_type["name"].lower(): object_type + for object_type in object_types + } - # Get all possible object types - all_obj_types = session.query('ObjectType').all() + self.groups = {} - # Filter object types by exceptions - filtered_types_id = set() + def avalon_mongo_id_attributes(self, session, event): + hierarchical_attr, object_type_attrs = ( + self.mongo_id_custom_attributes(session) + ) - for obj_type in all_obj_types: - name = obj_type['name'] - if " " in name: - name = name.replace(' ', '') + if hierarchical_attr is None: + self.create_hierarchical_mongo_attr(session) + hierarchical_attr, object_type_attrs = ( + self.mongo_id_custom_attributes(session) + ) + + if hierarchical_attr is None: + return + + if object_type_attrs: + self.convert_mongo_id_to_hierarchical( + hierarchical_attr, object_type_attrs, session, event + ) + + def mongo_id_custom_attributes(self, session): + cust_attrs_query = ( + "select id, entity_type, object_type_id, is_hierarchical, default" + " from CustomAttributeConfiguration" + " where key = \"{}\"" + ).format(CustAttrIdKey) + + mongo_id_avalon_attr = session.query(cust_attrs_query).all() + heirarchical_attr = None + object_type_attrs = [] + for cust_attr in mongo_id_avalon_attr: + if cust_attr["is_hierarchical"]: + heirarchical_attr = cust_attr + + else: + object_type_attrs.append(cust_attr) - if obj_type['name'] not in self.object_type_ids: - self.object_type_ids[name] = obj_type['id'] + return heirarchical_attr, object_type_attrs - if name.lower() not in exceptions: - filtered_types_id.add(obj_type['id']) + def create_hierarchical_mongo_attr(self, session): + # Attribute Name and Label + cust_attr_label = "Avalon/Mongo ID" # Set security roles for attribute role_list = ("API", "Administrator", "Pypeclub") roles = self.get_security_roles(role_list) # Set Text type of Attribute - custom_attribute_type = self.get_type('text') + custom_attribute_type = self.types_per_name["text"] # Set group to 'avalon' - group = self.get_group('avalon') - - data = {} - data['key'] = CustAttrIdKey - 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) + group = self.get_group("avalon") + + data = { + "key": CustAttrIdKey, + "label": cust_attr_label, + "type": custom_attribute_type, + "default": "", + "write_security_roles": roles, + "read_security_roles": roles, + "group": group, + "is_hierarchical": True, + "entity_type": "show", + "config": json.dumps({"markdown": False}) + } + + self.process_attribute(data) def convert_mongo_id_to_hierarchical( self, hierarchical_attr, object_type_attrs, session, event @@ -401,11 +430,11 @@ def get_required(self, attr): '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) + + type_name = attr['type'].lower() + output['type'] = self.types_per_name[type_name] config = None if type_name == 'number': @@ -500,35 +529,25 @@ def get_group(self, attr): 'Found more than one group "{}"'.format(group_name) ) - def query_roles(self): - if self.security_roles is None: - self.security_roles = {} - for role in self.session.query("SecurityRole").all(): - key = role["name"].lower() - self.security_roles[key] = role - return self.security_roles - def get_security_roles(self, security_roles): - security_roles = self.query_roles() - security_roles_lowered = tuple(name.lower() for name in security_roles) if ( len(security_roles_lowered) == 0 or "all" in security_roles_lowered ): - return tuple(security_roles.values()) + return list(self.security_roles.values()) output = [] if security_roles_lowered[0] == "except": excepts = security_roles_lowered[1:] - for role_name, role in security_roles.items(): + for role_name, role in self.security_roles.items(): if role_name not in excepts: output.append(role) else: for role_name in security_roles_lowered: - if role_name in security_roles: - output.append(security_roles[role_name]) + if role_name in self.security_roles: + output.append(self.security_roles[role_name]) else: raise CustAttrException(( "Securit role \"{}\" was not found in Ftrack." @@ -593,27 +612,12 @@ def get_optional(self, attr): 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: - type = 'show' - if 'entity_type' in attr: - type = attr['entity_type'] - - return { - 'is_hierarchical': True, - 'entity_type': type - } + if attr.get("is_hierarchical", False): + return { + "is_hierarchical": True, + "entity_type": attr.get("entity_type") or "show" + } if 'entity_type' not in attr: raise CustAttrException('Missing entity_type') @@ -625,23 +629,16 @@ def get_entity_type(self, 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: - 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] + object_type_name_low = object_type_name.lower() + object_type = self.object_types_per_name.get(object_type_name_low) + if not object_type: + raise CustAttrException(( + 'Object type with name "{}" don\'t exist' + ).format(object_type_name)) return { 'entity_type': attr['entity_type'], - 'object_type_id': object_type_id + 'object_type_id': object_type["id"] } From 0c73041ab402d4e0eb54d8d84c6bbfc599713632 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 4 May 2020 20:29:15 +0200 Subject: [PATCH 07/10] fixed getting configuration id with hierarchical mongo id attribute --- pype/ftrack/lib/avalon_sync.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/pype/ftrack/lib/avalon_sync.py b/pype/ftrack/lib/avalon_sync.py index 179977d403b..e915e861848 100644 --- a/pype/ftrack/lib/avalon_sync.py +++ b/pype/ftrack/lib/avalon_sync.py @@ -291,6 +291,8 @@ def launch_setup(self, project_full_name): self.filtered_ids = [] self.not_selected_ids = [] + self.hier_cust_attr_ids_by_key = {} + self._ent_paths_by_ftrack_id = {} self.ftrack_avalon_mapper = None @@ -812,6 +814,7 @@ def set_hierarchical_attribute(self, hier_attrs, sync_ids): key = attr["key"] attribute_key_by_id[attr["id"]] = key attributes_by_key[key] = attr + self.hier_cust_attr_ids_by_key[key] = attr["id"] store_key = "hier_attrs" if key.startswith("avalon_"): @@ -1595,9 +1598,16 @@ def create_avalon_entity(self, ftrack_id): if current_id != new_id_str: # store mongo id to ftrack entity - configuration_id = self.entities_dict[ftrack_id][ - "avalon_attrs_id" - ][CustAttrIdKey] + configuration_id = self.hier_cust_attr_ids_by_key.get( + CustAttrIdKey + ) + if not configuration_id: + # NOTE this is for cases when CustAttrIdKey key is not + # hierarchical custom attribute but per entity type + configuration_id = self.entities_dict[ftrack_id][ + "avalon_attrs_id" + ][CustAttrIdKey] + _entity_key = collections.OrderedDict({ "configuration_id": configuration_id, "entity_id": ftrack_id From 738c18792830ba450536cc63d84b1f44e31c4a8b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 5 May 2020 10:00:06 +0200 Subject: [PATCH 08/10] annoying fix --- pype/ftrack/actions/action_create_cust_attrs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/ftrack/actions/action_create_cust_attrs.py b/pype/ftrack/actions/action_create_cust_attrs.py index c141d6672c4..37b11256d2f 100644 --- a/pype/ftrack/actions/action_create_cust_attrs.py +++ b/pype/ftrack/actions/action_create_cust_attrs.py @@ -272,7 +272,7 @@ def convert_mongo_id_to_hierarchical( entity_type_label = "Project" elif attr_ent_type == "task": entity_type_label = ( - object_types_per_id[attr_def["object_type_id"]] + object_types_per_id[attr_def["object_type_id"]]["name"] ) else: self.log.warning( From 88124a25891f63c8960f2583ff70b5bf0cb5cc10 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 7 May 2020 15:50:59 +0200 Subject: [PATCH 09/10] ContextCustomAttributeValue is used for querying hierarchical custom attribute values for better stability --- pype/ftrack/lib/avalon_sync.py | 79 +++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 31 deletions(-) diff --git a/pype/ftrack/lib/avalon_sync.py b/pype/ftrack/lib/avalon_sync.py index e915e861848..be9dfd842d7 100644 --- a/pype/ftrack/lib/avalon_sync.py +++ b/pype/ftrack/lib/avalon_sync.py @@ -692,7 +692,6 @@ def set_cutom_attributes(self): ent_type["name"]: ent_type["id"] for ent_type in ent_types } - attrs = set() # store default values per entity type attrs_per_entity_type = collections.defaultdict(dict) avalon_attrs = collections.defaultdict(dict) @@ -700,9 +699,10 @@ def set_cutom_attributes(self): attrs_per_entity_type_ca_id = collections.defaultdict(dict) avalon_attrs_ca_id = collections.defaultdict(dict) + attribute_key_by_id = {} for cust_attr in custom_attrs: key = cust_attr["key"] - attrs.add(cust_attr["id"]) + attribute_key_by_id[cust_attr["id"]] = key ca_ent_type = cust_attr["entity_type"] if key.startswith("avalon_"): if ca_ent_type == "show": @@ -776,7 +776,7 @@ def set_cutom_attributes(self): "\"{}\"".format(id) for id in sync_ids ]) attributes_joined = ", ".join([ - "\"{}\"".format(name) for name in attrs + "\"{}\"".format(attr_id) for attr_id in attribute_key_by_id.keys() ]) cust_attr_query = ( @@ -794,13 +794,13 @@ def set_cutom_attributes(self): else: [values] = self.session._call(call_expr) - for value in values["data"]: - entity_id = value["entity_id"] - key = value["configuration"]["key"] + for item in values["data"]: + entity_id = item["entity_id"] + key = attribute_key_by_id[item["configuration_id"]] store_key = "custom_attributes" if key.startswith("avalon_"): store_key = "avalon_attrs" - self.entities_dict[entity_id][store_key][key] = value["value"] + self.entities_dict[entity_id][store_key][key] = item["value"] # process hierarchical attributes self.set_hierarchical_attribute(hier_attrs, sync_ids) @@ -824,6 +824,21 @@ def set_hierarchical_attribute(self, hier_attrs, sync_ids): attr["default"] ) + # Add attribute ids to entities dictionary + avalon_attribute_id_by_key = { + attr_key: attr_id + for attr_id, attr_key in attribute_key_by_id.items() + if attr_key.startswith("avalon_") + } + for entity_id, entity_dict in self.entities_dict.items(): + if "avalon_attrs_id" not in self.entities_dict[entity_id]: + self.entities_dict[entity_id]["avalon_attrs_id"] = {} + + for attr_key, attr_id in avalon_attribute_id_by_key.items(): + self.entities_dict[entity_id]["avalon_attrs_id"][attr_key] = ( + attr_id + ) + # Prepare dict with all hier keys and None values prepare_dict = {} prepare_dict_avalon = {} @@ -845,32 +860,34 @@ def set_hierarchical_attribute(self, hier_attrs, sync_ids): entity_ids_joined = ", ".join([ "\"{}\"".format(id) for id in sync_ids ]) - + attributes_joined = ", ".join([ + "\"{}\"".format(attr_id) for attr_id in attribute_key_by_id.keys() + ]) avalon_hier = [] - for configuration_id in attribute_key_by_id.keys(): - call_expr = [{ - "action": "query", - "expression": ( - "select value, entity_id from CustomAttributeValue " - "where entity_id in ({}) and configuration_id is \"{}\"" - ).format(entity_ids_joined, configuration_id) - }] - if hasattr(self.session, "call"): - [values] = self.session.call(call_expr) - else: - [values] = self.session._call(call_expr) + call_expr = [{ + "action": "query", + "expression": ( + "select value, entity_id from ContextCustomAttributeValue " + "where entity_id in ({}) and configuration_id in ({})" + ).format(entity_ids_joined, attributes_joined) + }] + if hasattr(self.session, "call"): + [values] = self.session.call(call_expr) + else: + [values] = self.session._call(call_expr) - for value in values["data"]: - if value["value"] is None: - continue - entity_id = value["entity_id"] - key = attribute_key_by_id[value["configuration_id"]] - if key.startswith("avalon_"): - store_key = "avalon_attrs" - avalon_hier.append(key) - else: - store_key = "hier_attrs" - self.entities_dict[entity_id][store_key][key] = value["value"] + for item in values["data"]: + value = item["value"] + if value is None: + continue + entity_id = item["entity_id"] + key = attribute_key_by_id[item["configuration_id"]] + if key.startswith("avalon_"): + store_key = "avalon_attrs" + avalon_hier.append(key) + else: + store_key = "hier_attrs" + self.entities_dict[entity_id][store_key][key] = value # Get dictionary with not None hierarchical values to pull to childs top_id = self.ft_project_id From aff69af21bb9eb5803dc5972544ca1430dee3f6c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 7 May 2020 15:53:27 +0200 Subject: [PATCH 10/10] removed unused variable --- pype/ftrack/lib/avalon_sync.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/ftrack/lib/avalon_sync.py b/pype/ftrack/lib/avalon_sync.py index be9dfd842d7..293b2d00496 100644 --- a/pype/ftrack/lib/avalon_sync.py +++ b/pype/ftrack/lib/avalon_sync.py @@ -830,7 +830,7 @@ def set_hierarchical_attribute(self, hier_attrs, sync_ids): for attr_id, attr_key in attribute_key_by_id.items() if attr_key.startswith("avalon_") } - for entity_id, entity_dict in self.entities_dict.items(): + for entity_id in self.entities_dict.keys(): if "avalon_attrs_id" not in self.entities_dict[entity_id]: self.entities_dict[entity_id]["avalon_attrs_id"] = {}