Merge pull request #2836 from pypeclub/enhancement/OP-2823_Ftrack-can-sync-fps-as-string

Ftrack: Can sync fps as string
This commit is contained in:
Jakub Trllo 2022-03-03 19:10:53 +01:00 committed by GitHub
commit 1ccc2ad458
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 223 additions and 11 deletions

View file

@ -20,11 +20,16 @@ from openpype_modules.ftrack.lib import (
query_custom_attributes,
CUST_ATTR_ID_KEY,
CUST_ATTR_AUTO_SYNC,
FPS_KEYS,
avalon_sync,
BaseEvent
)
from openpype_modules.ftrack.lib.avalon_sync import (
convert_to_fps,
InvalidFpsValue
)
from openpype.lib import CURRENT_DOC_SCHEMAS
@ -1149,12 +1154,31 @@ class SyncToAvalonEvent(BaseEvent):
"description": ftrack_ent["description"]
}
}
invalid_fps_items = []
cust_attrs = self.get_cust_attr_values(ftrack_ent)
for key, val in cust_attrs.items():
if key.startswith("avalon_"):
continue
if key in FPS_KEYS:
try:
val = convert_to_fps(val)
except InvalidFpsValue:
invalid_fps_items.append((ftrack_ent["id"], val))
continue
final_entity["data"][key] = val
if invalid_fps_items:
fps_msg = (
"These entities have invalid fps value in custom attributes"
)
items = []
for entity_id, value in invalid_fps_items:
ent_path = self.get_ent_path(entity_id)
items.append("{} - \"{}\"".format(ent_path, value))
self.report_items["error"][fps_msg] = items
_mongo_id_str = cust_attrs.get(CUST_ATTR_ID_KEY)
if _mongo_id_str:
try:
@ -2155,11 +2179,19 @@ class SyncToAvalonEvent(BaseEvent):
)
convert_types_by_id[attr_id] = convert_type
default_value = attr["default"]
if key in FPS_KEYS:
try:
default_value = convert_to_fps(default_value)
except InvalidFpsValue:
pass
entities_dict[ftrack_project_id]["hier_attrs"][key] = (
attr["default"]
)
# PREPARE DATA BEFORE THIS
invalid_fps_items = []
avalon_hier = []
for item in values:
value = item["value"]
@ -2173,8 +2205,25 @@ class SyncToAvalonEvent(BaseEvent):
if convert_type:
value = convert_type(value)
if key in FPS_KEYS:
try:
value = convert_to_fps(value)
except InvalidFpsValue:
invalid_fps_items.append((entity_id, value))
continue
entities_dict[entity_id]["hier_attrs"][key] = value
if invalid_fps_items:
fps_msg = (
"These entities have invalid fps value in custom attributes"
)
items = []
for entity_id, value in invalid_fps_items:
ent_path = self.get_ent_path(entity_id)
items.append("{} - \"{}\"".format(ent_path, value))
self.report_items["error"][fps_msg] = items
# Get dictionary with not None hierarchical values to pull to childs
project_values = {}
for key, value in (

View file

@ -11,6 +11,7 @@ from openpype_modules.ftrack.lib import (
CUST_ATTR_TOOLS,
CUST_ATTR_APPLICATIONS,
CUST_ATTR_INTENT,
FPS_KEYS,
default_custom_attributes_definition,
app_definitions_from_app_manager,
@ -519,20 +520,28 @@ class CustomAttributes(BaseAction):
self.show_message(event, msg)
def process_attribute(self, data):
existing_attrs = self.session.query(
"CustomAttributeConfiguration"
).all()
existing_attrs = self.session.query((
"select is_hierarchical, key, type, entity_type, object_type_id"
" from CustomAttributeConfiguration"
)).all()
matching = []
is_hierarchical = data.get("is_hierarchical", False)
for attr in existing_attrs:
if (
attr["key"] != data["key"] or
attr["type"]["name"] != data["type"]["name"]
is_hierarchical != attr["is_hierarchical"]
or attr["key"] != data["key"]
):
continue
if data.get("is_hierarchical") is True:
if attr["is_hierarchical"] is True:
matching.append(attr)
if attr["type"]["name"] != data["type"]["name"]:
if data["key"] in FPS_KEYS and attr["type"]["name"] == "text":
self.log.info("Kept 'fps' as text custom attribute.")
return
continue
if is_hierarchical:
matching.append(attr)
elif "object_type_id" in data:
if (
attr["entity_type"] == data["entity_type"] and

View file

@ -4,7 +4,8 @@ from .constants import (
CUST_ATTR_GROUP,
CUST_ATTR_TOOLS,
CUST_ATTR_APPLICATIONS,
CUST_ATTR_INTENT
CUST_ATTR_INTENT,
FPS_KEYS
)
from .settings import (
get_ftrack_event_mongo_info
@ -30,6 +31,8 @@ __all__ = (
"CUST_ATTR_GROUP",
"CUST_ATTR_TOOLS",
"CUST_ATTR_APPLICATIONS",
"CUST_ATTR_INTENT",
"FPS_KEYS",
"get_ftrack_event_mongo_info",

View file

@ -2,6 +2,9 @@ import re
import json
import collections
import copy
import numbers
import six
from avalon.api import AvalonMongoDB
@ -14,7 +17,7 @@ from openpype.api import (
)
from openpype.lib import ApplicationManager
from .constants import CUST_ATTR_ID_KEY
from .constants import CUST_ATTR_ID_KEY, FPS_KEYS
from .custom_attributes import get_openpype_attr, query_custom_attributes
from bson.objectid import ObjectId
@ -33,6 +36,106 @@ CURRENT_DOC_SCHEMAS = {
}
class InvalidFpsValue(Exception):
pass
def is_string_number(value):
"""Can string value be converted to number (float)."""
if not isinstance(value, six.string_types):
raise TypeError("Expected {} got {}".format(
", ".join(str(t) for t in six.string_types), str(type(value))
))
if value == ".":
return False
if value.startswith("."):
value = "0" + value
elif value.endswith("."):
value = value + "0"
if re.match(r"^\d+(\.\d+)?$", value) is None:
return False
return True
def convert_to_fps(source_value):
"""Convert value into fps value.
Non string values are kept untouched. String is tried to convert.
Valid values:
"1000"
"1000.05"
"1000,05"
",05"
".05"
"1000,"
"1000."
"1000/1000"
"1000.05/1000"
"1000/1000.05"
"1000.05/1000.05"
"1000,05/1000"
"1000/1000,05"
"1000,05/1000,05"
Invalid values:
"/"
"/1000"
"1000/"
","
"."
...any other string
Returns:
float: Converted value.
Raises:
InvalidFpsValue: When value can't be converted to float.
"""
if not isinstance(source_value, six.string_types):
if isinstance(source_value, numbers.Number):
return float(source_value)
return source_value
value = source_value.strip().replace(",", ".")
if not value:
raise InvalidFpsValue("Got empty value")
subs = value.split("/")
if len(subs) == 1:
str_value = subs[0]
if not is_string_number(str_value):
raise InvalidFpsValue(
"Value \"{}\" can't be converted to number.".format(value)
)
return float(str_value)
elif len(subs) == 2:
divident, divisor = subs
if not divident or not is_string_number(divident):
raise InvalidFpsValue(
"Divident value \"{}\" can't be converted to number".format(
divident
)
)
if not divisor or not is_string_number(divisor):
raise InvalidFpsValue(
"Divisor value \"{}\" can't be converted to number".format(
divident
)
)
divisor_float = float(divisor)
if divisor_float == 0.0:
raise InvalidFpsValue("Can't divide by zero")
return float(divident) / divisor_float
raise InvalidFpsValue(
"Value can't be converted to number \"{}\"".format(source_value)
)
def create_chunks(iterable, chunk_size=None):
"""Separate iterable into multiple chunks by size.
@ -980,6 +1083,7 @@ class SyncEntitiesFactory:
sync_ids
)
invalid_fps_items = []
for item in items:
entity_id = item["entity_id"]
attr_id = item["configuration_id"]
@ -992,8 +1096,24 @@ class SyncEntitiesFactory:
value = item["value"]
if convert_type:
value = convert_type(value)
if key in FPS_KEYS:
try:
value = convert_to_fps(value)
except InvalidFpsValue:
invalid_fps_items.append((entity_id, value))
self.entities_dict[entity_id][store_key][key] = value
if invalid_fps_items:
fps_msg = (
"These entities have invalid fps value in custom attributes"
)
items = []
for entity_id, value in invalid_fps_items:
ent_path = self.get_ent_path(entity_id)
items.append("{} - \"{}\"".format(ent_path, value))
self.report_items["error"][fps_msg] = items
# process hierarchical attributes
self.set_hierarchical_attribute(
hier_attrs, sync_ids, cust_attr_type_name_by_id
@ -1026,8 +1146,15 @@ class SyncEntitiesFactory:
if key.startswith("avalon_"):
store_key = "avalon_attrs"
default_value = attr["default"]
if key in FPS_KEYS:
try:
default_value = convert_to_fps(default_value)
except InvalidFpsValue:
pass
self.entities_dict[self.ft_project_id][store_key][key] = (
attr["default"]
default_value
)
# Add attribute ids to entities dictionary
@ -1069,6 +1196,7 @@ class SyncEntitiesFactory:
True
)
invalid_fps_items = []
avalon_hier = []
for item in items:
value = item["value"]
@ -1088,6 +1216,13 @@ class SyncEntitiesFactory:
entity_id = item["entity_id"]
key = attribute_key_by_id[attr_id]
if key in FPS_KEYS:
try:
value = convert_to_fps(value)
except InvalidFpsValue:
invalid_fps_items.append((entity_id, value))
continue
if key.startswith("avalon_"):
store_key = "avalon_attrs"
avalon_hier.append(key)
@ -1095,6 +1230,16 @@ class SyncEntitiesFactory:
store_key = "hier_attrs"
self.entities_dict[entity_id][store_key][key] = value
if invalid_fps_items:
fps_msg = (
"These entities have invalid fps value in custom attributes"
)
items = []
for entity_id, value in invalid_fps_items:
ent_path = self.get_ent_path(entity_id)
items.append("{} - \"{}\"".format(ent_path, value))
self.report_items["error"][fps_msg] = items
# Get dictionary with not None hierarchical values to pull to childs
top_id = self.ft_project_id
project_values = {}

View file

@ -12,3 +12,9 @@ CUST_ATTR_APPLICATIONS = "applications"
CUST_ATTR_TOOLS = "tools_env"
# Intent custom attribute name
CUST_ATTR_INTENT = "intent"
FPS_KEYS = {
"fps",
# For development purposes
"fps_string"
}