mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Ftrack: Hierarchical <> Non-Hierarchical attributes sync fix (#4635)
* modify action to use 'CustomAttributeValue' and fix bugs * modify and fix event handler to push hierarchical values * added few smaller comments * removed unused variables
This commit is contained in:
parent
b5a0b706bb
commit
de4b3e4d65
2 changed files with 546 additions and 866 deletions
|
|
@ -9,7 +9,7 @@ from openpype_modules.ftrack.lib import (
|
|||
|
||||
|
||||
class PushHierValuesToNonHier(ServerAction):
|
||||
"""Action push hierarchical custom attribute values to non hierarchical.
|
||||
"""Action push hierarchical custom attribute values to non-hierarchical.
|
||||
|
||||
Hierarchical value is also pushed to their task entities.
|
||||
|
||||
|
|
@ -119,17 +119,109 @@ class PushHierValuesToNonHier(ServerAction):
|
|||
self.join_query_keys(object_ids)
|
||||
)).all()
|
||||
|
||||
output = {}
|
||||
attrs_by_obj_id = collections.defaultdict(list)
|
||||
hiearchical = []
|
||||
for attr in attrs:
|
||||
if attr["is_hierarchical"]:
|
||||
hiearchical.append(attr)
|
||||
continue
|
||||
obj_id = attr["object_type_id"]
|
||||
if obj_id not in output:
|
||||
output[obj_id] = []
|
||||
output[obj_id].append(attr)
|
||||
return output, hiearchical
|
||||
attrs_by_obj_id[obj_id].append(attr)
|
||||
return attrs_by_obj_id, hiearchical
|
||||
|
||||
def query_attr_value(
|
||||
self,
|
||||
session,
|
||||
hier_attrs,
|
||||
attrs_by_obj_id,
|
||||
dst_object_type_ids,
|
||||
task_entity_ids,
|
||||
non_task_entity_ids,
|
||||
parent_id_by_entity_id
|
||||
):
|
||||
all_non_task_ids_with_parents = set()
|
||||
for entity_id in non_task_entity_ids:
|
||||
all_non_task_ids_with_parents.add(entity_id)
|
||||
_entity_id = entity_id
|
||||
while True:
|
||||
parent_id = parent_id_by_entity_id.get(_entity_id)
|
||||
if (
|
||||
parent_id is None
|
||||
or parent_id in all_non_task_ids_with_parents
|
||||
):
|
||||
break
|
||||
all_non_task_ids_with_parents.add(parent_id)
|
||||
_entity_id = parent_id
|
||||
|
||||
all_entity_ids = (
|
||||
set(all_non_task_ids_with_parents)
|
||||
| set(task_entity_ids)
|
||||
)
|
||||
attr_ids = {attr["id"] for attr in hier_attrs}
|
||||
for obj_id in dst_object_type_ids:
|
||||
attrs = attrs_by_obj_id.get(obj_id)
|
||||
if attrs is not None:
|
||||
for attr in attrs:
|
||||
attr_ids.add(attr["id"])
|
||||
|
||||
real_values_by_entity_id = {
|
||||
entity_id: {}
|
||||
for entity_id in all_entity_ids
|
||||
}
|
||||
|
||||
attr_values = query_custom_attributes(
|
||||
session, attr_ids, all_entity_ids, True
|
||||
)
|
||||
for item in attr_values:
|
||||
entity_id = item["entity_id"]
|
||||
attr_id = item["configuration_id"]
|
||||
real_values_by_entity_id[entity_id][attr_id] = item["value"]
|
||||
|
||||
# Fill hierarchical values
|
||||
hier_attrs_key_by_id = {
|
||||
hier_attr["id"]: hier_attr
|
||||
for hier_attr in hier_attrs
|
||||
}
|
||||
hier_values_per_entity_id = {}
|
||||
for entity_id in all_non_task_ids_with_parents:
|
||||
real_values = real_values_by_entity_id[entity_id]
|
||||
hier_values_per_entity_id[entity_id] = {}
|
||||
for attr_id, attr in hier_attrs_key_by_id.items():
|
||||
key = attr["key"]
|
||||
hier_values_per_entity_id[entity_id][key] = (
|
||||
real_values.get(attr_id)
|
||||
)
|
||||
|
||||
output = {}
|
||||
for entity_id in non_task_entity_ids:
|
||||
output[entity_id] = {}
|
||||
for attr in hier_attrs_key_by_id.values():
|
||||
key = attr["key"]
|
||||
value = hier_values_per_entity_id[entity_id][key]
|
||||
tried_ids = set()
|
||||
if value is None:
|
||||
tried_ids.add(entity_id)
|
||||
_entity_id = entity_id
|
||||
while value is None:
|
||||
parent_id = parent_id_by_entity_id.get(_entity_id)
|
||||
if not parent_id:
|
||||
break
|
||||
value = hier_values_per_entity_id[parent_id][key]
|
||||
if value is not None:
|
||||
break
|
||||
_entity_id = parent_id
|
||||
tried_ids.add(parent_id)
|
||||
|
||||
if value is None:
|
||||
value = attr["default"]
|
||||
|
||||
if value is not None:
|
||||
for ent_id in tried_ids:
|
||||
hier_values_per_entity_id[ent_id][key] = value
|
||||
|
||||
output[entity_id][key] = value
|
||||
|
||||
return real_values_by_entity_id, output
|
||||
|
||||
def propagate_values(self, session, event, selected_entities):
|
||||
ftrack_settings = self.get_ftrack_settings(
|
||||
|
|
@ -156,29 +248,24 @@ class PushHierValuesToNonHier(ServerAction):
|
|||
}
|
||||
|
||||
task_object_type = object_types_by_low_name["task"]
|
||||
destination_object_types = [task_object_type]
|
||||
dst_object_type_ids = {task_object_type["id"]}
|
||||
for ent_type in interest_entity_types:
|
||||
obj_type = object_types_by_low_name.get(ent_type)
|
||||
if obj_type and obj_type not in destination_object_types:
|
||||
destination_object_types.append(obj_type)
|
||||
|
||||
destination_object_type_ids = set(
|
||||
obj_type["id"]
|
||||
for obj_type in destination_object_types
|
||||
)
|
||||
if obj_type:
|
||||
dst_object_type_ids.add(obj_type["id"])
|
||||
|
||||
interest_attributes = action_settings["interest_attributes"]
|
||||
# Find custom attributes definitions
|
||||
attrs_by_obj_id, hier_attrs = self.attrs_configurations(
|
||||
session, destination_object_type_ids, interest_attributes
|
||||
session, dst_object_type_ids, interest_attributes
|
||||
)
|
||||
# Filter destination object types if they have any object specific
|
||||
# custom attribute
|
||||
for obj_id in tuple(destination_object_type_ids):
|
||||
for obj_id in tuple(dst_object_type_ids):
|
||||
if obj_id not in attrs_by_obj_id:
|
||||
destination_object_type_ids.remove(obj_id)
|
||||
dst_object_type_ids.remove(obj_id)
|
||||
|
||||
if not destination_object_type_ids:
|
||||
if not dst_object_type_ids:
|
||||
# TODO report that there are not matching custom attributes
|
||||
return {
|
||||
"success": True,
|
||||
|
|
@ -192,14 +279,14 @@ class PushHierValuesToNonHier(ServerAction):
|
|||
session,
|
||||
selected_ids,
|
||||
project_entity,
|
||||
destination_object_type_ids
|
||||
dst_object_type_ids
|
||||
)
|
||||
|
||||
self.log.debug("Preparing whole project hierarchy by ids.")
|
||||
|
||||
entities_by_obj_id = {
|
||||
obj_id: []
|
||||
for obj_id in destination_object_type_ids
|
||||
for obj_id in dst_object_type_ids
|
||||
}
|
||||
|
||||
self.log.debug("Filtering Task entities.")
|
||||
|
|
@ -223,10 +310,16 @@ class PushHierValuesToNonHier(ServerAction):
|
|||
"message": "Nothing to do in your selection."
|
||||
}
|
||||
|
||||
self.log.debug("Getting Hierarchical custom attribute values parents.")
|
||||
hier_values_by_entity_id = self.get_hier_values(
|
||||
self.log.debug("Getting Custom attribute values.")
|
||||
(
|
||||
real_values_by_entity_id,
|
||||
hier_values_by_entity_id
|
||||
) = self.query_attr_value(
|
||||
session,
|
||||
hier_attrs,
|
||||
attrs_by_obj_id,
|
||||
dst_object_type_ids,
|
||||
task_entity_ids,
|
||||
non_task_entity_ids,
|
||||
parent_id_by_entity_id
|
||||
)
|
||||
|
|
@ -237,7 +330,8 @@ class PushHierValuesToNonHier(ServerAction):
|
|||
hier_attrs,
|
||||
task_entity_ids,
|
||||
hier_values_by_entity_id,
|
||||
parent_id_by_entity_id
|
||||
parent_id_by_entity_id,
|
||||
real_values_by_entity_id
|
||||
)
|
||||
|
||||
self.log.debug("Setting values to entities themselves.")
|
||||
|
|
@ -245,7 +339,8 @@ class PushHierValuesToNonHier(ServerAction):
|
|||
session,
|
||||
entities_by_obj_id,
|
||||
attrs_by_obj_id,
|
||||
hier_values_by_entity_id
|
||||
hier_values_by_entity_id,
|
||||
real_values_by_entity_id
|
||||
)
|
||||
|
||||
return True
|
||||
|
|
@ -322,112 +417,64 @@ class PushHierValuesToNonHier(ServerAction):
|
|||
|
||||
return parent_id_by_entity_id, filtered_entities
|
||||
|
||||
def get_hier_values(
|
||||
self,
|
||||
session,
|
||||
hier_attrs,
|
||||
focus_entity_ids,
|
||||
parent_id_by_entity_id
|
||||
):
|
||||
all_ids_with_parents = set()
|
||||
for entity_id in focus_entity_ids:
|
||||
all_ids_with_parents.add(entity_id)
|
||||
_entity_id = entity_id
|
||||
while True:
|
||||
parent_id = parent_id_by_entity_id.get(_entity_id)
|
||||
if (
|
||||
not parent_id
|
||||
or parent_id in all_ids_with_parents
|
||||
):
|
||||
break
|
||||
all_ids_with_parents.add(parent_id)
|
||||
_entity_id = parent_id
|
||||
|
||||
hier_attr_ids = tuple(hier_attr["id"] for hier_attr in hier_attrs)
|
||||
hier_attrs_key_by_id = {
|
||||
hier_attr["id"]: hier_attr["key"]
|
||||
for hier_attr in hier_attrs
|
||||
}
|
||||
|
||||
values_per_entity_id = {}
|
||||
for entity_id in all_ids_with_parents:
|
||||
values_per_entity_id[entity_id] = {}
|
||||
for key in hier_attrs_key_by_id.values():
|
||||
values_per_entity_id[entity_id][key] = None
|
||||
|
||||
values = query_custom_attributes(
|
||||
session, hier_attr_ids, all_ids_with_parents, True
|
||||
)
|
||||
for item in values:
|
||||
entity_id = item["entity_id"]
|
||||
key = hier_attrs_key_by_id[item["configuration_id"]]
|
||||
|
||||
values_per_entity_id[entity_id][key] = item["value"]
|
||||
|
||||
output = {}
|
||||
for entity_id in focus_entity_ids:
|
||||
output[entity_id] = {}
|
||||
for key in hier_attrs_key_by_id.values():
|
||||
value = values_per_entity_id[entity_id][key]
|
||||
tried_ids = set()
|
||||
if value is None:
|
||||
tried_ids.add(entity_id)
|
||||
_entity_id = entity_id
|
||||
while value is None:
|
||||
parent_id = parent_id_by_entity_id.get(_entity_id)
|
||||
if not parent_id:
|
||||
break
|
||||
value = values_per_entity_id[parent_id][key]
|
||||
if value is not None:
|
||||
break
|
||||
_entity_id = parent_id
|
||||
tried_ids.add(parent_id)
|
||||
|
||||
if value is not None:
|
||||
for ent_id in tried_ids:
|
||||
values_per_entity_id[ent_id][key] = value
|
||||
|
||||
output[entity_id][key] = value
|
||||
return output
|
||||
|
||||
def set_task_attr_values(
|
||||
self,
|
||||
session,
|
||||
hier_attrs,
|
||||
task_entity_ids,
|
||||
hier_values_by_entity_id,
|
||||
parent_id_by_entity_id
|
||||
parent_id_by_entity_id,
|
||||
real_values_by_entity_id
|
||||
):
|
||||
hier_attr_id_by_key = {
|
||||
attr["key"]: attr["id"]
|
||||
for attr in hier_attrs
|
||||
}
|
||||
filtered_task_ids = set()
|
||||
for task_id in task_entity_ids:
|
||||
parent_id = parent_id_by_entity_id.get(task_id) or {}
|
||||
parent_id = parent_id_by_entity_id.get(task_id)
|
||||
parent_values = hier_values_by_entity_id.get(parent_id)
|
||||
if not parent_values:
|
||||
continue
|
||||
if parent_values:
|
||||
filtered_task_ids.add(task_id)
|
||||
|
||||
if not filtered_task_ids:
|
||||
return
|
||||
|
||||
for task_id in filtered_task_ids:
|
||||
parent_id = parent_id_by_entity_id[task_id]
|
||||
parent_values = hier_values_by_entity_id[parent_id]
|
||||
hier_values_by_entity_id[task_id] = {}
|
||||
real_task_attr_values = real_values_by_entity_id[task_id]
|
||||
for key, value in parent_values.items():
|
||||
hier_values_by_entity_id[task_id][key] = value
|
||||
if value is None:
|
||||
continue
|
||||
|
||||
configuration_id = hier_attr_id_by_key[key]
|
||||
_entity_key = collections.OrderedDict([
|
||||
("configuration_id", configuration_id),
|
||||
("entity_id", task_id)
|
||||
])
|
||||
|
||||
session.recorded_operations.push(
|
||||
ftrack_api.operation.UpdateEntityOperation(
|
||||
"ContextCustomAttributeValue",
|
||||
op = None
|
||||
if configuration_id not in real_task_attr_values:
|
||||
op = ftrack_api.operation.CreateEntityOperation(
|
||||
"CustomAttributeValue",
|
||||
_entity_key,
|
||||
{"value": value}
|
||||
)
|
||||
elif real_task_attr_values[configuration_id] != value:
|
||||
op = ftrack_api.operation.UpdateEntityOperation(
|
||||
"CustomAttributeValue",
|
||||
_entity_key,
|
||||
"value",
|
||||
ftrack_api.symbol.NOT_SET,
|
||||
real_task_attr_values[configuration_id],
|
||||
value
|
||||
)
|
||||
)
|
||||
if len(session.recorded_operations) > 100:
|
||||
session.commit()
|
||||
|
||||
if op is not None:
|
||||
session.recorded_operations.push(op)
|
||||
if len(session.recorded_operations) > 100:
|
||||
session.commit()
|
||||
|
||||
session.commit()
|
||||
|
||||
|
|
@ -436,39 +483,68 @@ class PushHierValuesToNonHier(ServerAction):
|
|||
session,
|
||||
entities_by_obj_id,
|
||||
attrs_by_obj_id,
|
||||
hier_values_by_entity_id
|
||||
hier_values_by_entity_id,
|
||||
real_values_by_entity_id
|
||||
):
|
||||
"""Push values from hierarchical custom attributes to non-hierarchical.
|
||||
|
||||
Args:
|
||||
session (ftrack_api.Sessison): Session which queried entities,
|
||||
values and which is used for change propagation.
|
||||
entities_by_obj_id (dict[str, list[str]]): TypedContext
|
||||
ftrack entity ids where the attributes are propagated by their
|
||||
object ids.
|
||||
attrs_by_obj_id (dict[str, ftrack_api.Entity]): Objects of
|
||||
'CustomAttributeConfiguration' by their ids.
|
||||
hier_values_by_entity_id (doc[str, dict[str, Any]]): Attribute
|
||||
values by entity id and by their keys.
|
||||
real_values_by_entity_id (doc[str, dict[str, Any]]): Real attribute
|
||||
values of entities.
|
||||
"""
|
||||
|
||||
for object_id, entity_ids in entities_by_obj_id.items():
|
||||
attrs = attrs_by_obj_id.get(object_id)
|
||||
if not attrs or not entity_ids:
|
||||
continue
|
||||
|
||||
for attr in attrs:
|
||||
for entity_id in entity_ids:
|
||||
value = (
|
||||
hier_values_by_entity_id
|
||||
.get(entity_id, {})
|
||||
.get(attr["key"])
|
||||
)
|
||||
for entity_id in entity_ids:
|
||||
real_values = real_values_by_entity_id.get(entity_id)
|
||||
hier_values = hier_values_by_entity_id.get(entity_id)
|
||||
if hier_values is None:
|
||||
continue
|
||||
|
||||
for attr in attrs:
|
||||
attr_id = attr["id"]
|
||||
attr_key = attr["key"]
|
||||
value = hier_values.get(attr_key)
|
||||
if value is None:
|
||||
continue
|
||||
|
||||
_entity_key = collections.OrderedDict([
|
||||
("configuration_id", attr["id"]),
|
||||
("configuration_id", attr_id),
|
||||
("entity_id", entity_id)
|
||||
])
|
||||
|
||||
session.recorded_operations.push(
|
||||
ftrack_api.operation.UpdateEntityOperation(
|
||||
"ContextCustomAttributeValue",
|
||||
op = None
|
||||
if attr_id not in real_values:
|
||||
op = ftrack_api.operation.CreateEntityOperation(
|
||||
"CustomAttributeValue",
|
||||
_entity_key,
|
||||
{"value": value}
|
||||
)
|
||||
elif real_values[attr_id] != value:
|
||||
op = ftrack_api.operation.UpdateEntityOperation(
|
||||
"CustomAttributeValue",
|
||||
_entity_key,
|
||||
"value",
|
||||
ftrack_api.symbol.NOT_SET,
|
||||
real_values[attr_id],
|
||||
value
|
||||
)
|
||||
)
|
||||
if len(session.recorded_operations) > 100:
|
||||
session.commit()
|
||||
|
||||
if op is not None:
|
||||
session.recorded_operations.push(op)
|
||||
if len(session.recorded_operations) > 100:
|
||||
session.commit()
|
||||
|
||||
session.commit()
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue