mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge branch 'develop' into enhancement/review_collect_otio_subset_resource
This commit is contained in:
commit
13e0ad9cdd
10 changed files with 762 additions and 482 deletions
|
|
@ -1,9 +1,15 @@
|
|||
import os
|
||||
import re
|
||||
import copy
|
||||
import numbers
|
||||
import warnings
|
||||
from string import Formatter
|
||||
import typing
|
||||
from typing import List, Dict, Any, Set
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from typing import Union
|
||||
|
||||
KEY_PATTERN = re.compile(r"(\{.*?[^{0]*\})")
|
||||
KEY_PADDING_PATTERN = re.compile(r"([^:]+)\S+[><]\S+")
|
||||
SUB_DICT_PATTERN = re.compile(r"([^\[\]]+)")
|
||||
OPTIONAL_PATTERN = re.compile(r"(<.*?[^{0]*>)[^0-9]*?")
|
||||
|
||||
|
|
@ -18,9 +24,7 @@ class TemplateUnsolved(Exception):
|
|||
def __init__(self, template, missing_keys, invalid_types):
|
||||
invalid_type_items = []
|
||||
for _key, _type in invalid_types.items():
|
||||
invalid_type_items.append(
|
||||
"\"{0}\" {1}".format(_key, str(_type))
|
||||
)
|
||||
invalid_type_items.append(f"\"{_key}\" {str(_type)}")
|
||||
|
||||
invalid_types_msg = ""
|
||||
if invalid_type_items:
|
||||
|
|
@ -33,31 +37,32 @@ class TemplateUnsolved(Exception):
|
|||
missing_keys_msg = self.missing_keys_msg.format(
|
||||
", ".join(missing_keys)
|
||||
)
|
||||
super(TemplateUnsolved, self).__init__(
|
||||
super().__init__(
|
||||
self.msg.format(template, missing_keys_msg, invalid_types_msg)
|
||||
)
|
||||
|
||||
|
||||
class StringTemplate:
|
||||
"""String that can be formatted."""
|
||||
def __init__(self, template):
|
||||
def __init__(self, template: str):
|
||||
if not isinstance(template, str):
|
||||
raise TypeError("<{}> argument must be a string, not {}.".format(
|
||||
self.__class__.__name__, str(type(template))
|
||||
))
|
||||
raise TypeError(
|
||||
f"<{self.__class__.__name__}> argument must be a string,"
|
||||
f" not {str(type(template))}."
|
||||
)
|
||||
|
||||
self._template = template
|
||||
self._template: str = template
|
||||
parts = []
|
||||
last_end_idx = 0
|
||||
for item in KEY_PATTERN.finditer(template):
|
||||
start, end = item.span()
|
||||
if start > last_end_idx:
|
||||
parts.append(template[last_end_idx:start])
|
||||
parts.append(FormattingPart(template[start:end]))
|
||||
last_end_idx = end
|
||||
formatter = Formatter()
|
||||
|
||||
if last_end_idx < len(template):
|
||||
parts.append(template[last_end_idx:len(template)])
|
||||
for item in formatter.parse(template):
|
||||
literal_text, field_name, format_spec, conversion = item
|
||||
if literal_text:
|
||||
parts.append(literal_text)
|
||||
if field_name:
|
||||
parts.append(
|
||||
FormattingPart(field_name, format_spec, conversion)
|
||||
)
|
||||
|
||||
new_parts = []
|
||||
for part in parts:
|
||||
|
|
@ -77,15 +82,17 @@ class StringTemplate:
|
|||
if substr:
|
||||
new_parts.append(substr)
|
||||
|
||||
self._parts = self.find_optional_parts(new_parts)
|
||||
self._parts: List["Union[str, OptionalPart, FormattingPart]"] = (
|
||||
self.find_optional_parts(new_parts)
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return self.template
|
||||
|
||||
def __repr__(self):
|
||||
return "<{}> {}".format(self.__class__.__name__, self.template)
|
||||
def __repr__(self) -> str:
|
||||
return f"<{self.__class__.__name__}> {self.template}"
|
||||
|
||||
def __contains__(self, other):
|
||||
def __contains__(self, other: str) -> bool:
|
||||
return other in self.template
|
||||
|
||||
def replace(self, *args, **kwargs):
|
||||
|
|
@ -93,10 +100,10 @@ class StringTemplate:
|
|||
return self
|
||||
|
||||
@property
|
||||
def template(self):
|
||||
def template(self) -> str:
|
||||
return self._template
|
||||
|
||||
def format(self, data):
|
||||
def format(self, data: Dict[str, Any]) -> "TemplateResult":
|
||||
""" Figure out with whole formatting.
|
||||
|
||||
Separate advanced keys (*Like '{project[name]}') from string which must
|
||||
|
|
@ -108,6 +115,7 @@ class StringTemplate:
|
|||
Returns:
|
||||
TemplateResult: Filled or partially filled template containing all
|
||||
data needed or missing for filling template.
|
||||
|
||||
"""
|
||||
result = TemplatePartResult()
|
||||
for part in self._parts:
|
||||
|
|
@ -135,23 +143,29 @@ class StringTemplate:
|
|||
invalid_types
|
||||
)
|
||||
|
||||
def format_strict(self, *args, **kwargs):
|
||||
result = self.format(*args, **kwargs)
|
||||
def format_strict(self, data: Dict[str, Any]) -> "TemplateResult":
|
||||
result = self.format(data)
|
||||
result.validate()
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def format_template(cls, template, data):
|
||||
def format_template(
|
||||
cls, template: str, data: Dict[str, Any]
|
||||
) -> "TemplateResult":
|
||||
objected_template = cls(template)
|
||||
return objected_template.format(data)
|
||||
|
||||
@classmethod
|
||||
def format_strict_template(cls, template, data):
|
||||
def format_strict_template(
|
||||
cls, template: str, data: Dict[str, Any]
|
||||
) -> "TemplateResult":
|
||||
objected_template = cls(template)
|
||||
return objected_template.format_strict(data)
|
||||
|
||||
@staticmethod
|
||||
def find_optional_parts(parts):
|
||||
def find_optional_parts(
|
||||
parts: List["Union[str, FormattingPart]"]
|
||||
) -> List["Union[str, OptionalPart, FormattingPart]"]:
|
||||
new_parts = []
|
||||
tmp_parts = {}
|
||||
counted_symb = -1
|
||||
|
|
@ -216,11 +230,11 @@ class TemplateResult(str):
|
|||
of number.
|
||||
"""
|
||||
|
||||
used_values = None
|
||||
solved = None
|
||||
template = None
|
||||
missing_keys = None
|
||||
invalid_types = None
|
||||
used_values: Dict[str, Any] = None
|
||||
solved: bool = None
|
||||
template: str = None
|
||||
missing_keys: List[str] = None
|
||||
invalid_types: Dict[str, Any] = None
|
||||
|
||||
def __new__(
|
||||
cls, filled_template, template, solved,
|
||||
|
|
@ -248,7 +262,7 @@ class TemplateResult(str):
|
|||
self.invalid_types
|
||||
)
|
||||
|
||||
def copy(self):
|
||||
def copy(self) -> "TemplateResult":
|
||||
cls = self.__class__
|
||||
return cls(
|
||||
str(self),
|
||||
|
|
@ -259,7 +273,7 @@ class TemplateResult(str):
|
|||
self.invalid_types
|
||||
)
|
||||
|
||||
def normalized(self):
|
||||
def normalized(self) -> "TemplateResult":
|
||||
"""Convert to normalized path."""
|
||||
|
||||
cls = self.__class__
|
||||
|
|
@ -275,27 +289,28 @@ class TemplateResult(str):
|
|||
|
||||
class TemplatePartResult:
|
||||
"""Result to store result of template parts."""
|
||||
def __init__(self, optional=False):
|
||||
def __init__(self, optional: bool = False):
|
||||
# Missing keys or invalid value types of required keys
|
||||
self._missing_keys = set()
|
||||
self._invalid_types = {}
|
||||
self._missing_keys: Set[str] = set()
|
||||
self._invalid_types: Dict[str, Any] = {}
|
||||
# Missing keys or invalid value types of optional keys
|
||||
self._missing_optional_keys = set()
|
||||
self._invalid_optional_types = {}
|
||||
self._missing_optional_keys: Set[str] = set()
|
||||
self._invalid_optional_types: Dict[str, Any] = {}
|
||||
|
||||
# Used values stored by key with origin type
|
||||
# - key without any padding or key modifiers
|
||||
# - value from filling data
|
||||
# Example: {"version": 1}
|
||||
self._used_values = {}
|
||||
self._used_values: Dict[str, Any] = {}
|
||||
# Used values stored by key with all modifirs
|
||||
# - value is already formatted string
|
||||
# Example: {"version:0>3": "001"}
|
||||
self._realy_used_values = {}
|
||||
self._really_used_values: Dict[str, Any] = {}
|
||||
# Concatenated string output after formatting
|
||||
self._output = ""
|
||||
self._output: str = ""
|
||||
# Is this result from optional part
|
||||
self._optional = True
|
||||
# TODO find out why we don't use 'optional' from args
|
||||
self._optional: bool = True
|
||||
|
||||
def add_output(self, other):
|
||||
if isinstance(other, str):
|
||||
|
|
@ -313,7 +328,7 @@ class TemplatePartResult:
|
|||
if other.optional and not other.solved:
|
||||
return
|
||||
self._used_values.update(other.used_values)
|
||||
self._realy_used_values.update(other.realy_used_values)
|
||||
self._really_used_values.update(other.really_used_values)
|
||||
|
||||
else:
|
||||
raise TypeError("Cannot add data from \"{}\" to \"{}\"".format(
|
||||
|
|
@ -321,7 +336,7 @@ class TemplatePartResult:
|
|||
)
|
||||
|
||||
@property
|
||||
def solved(self):
|
||||
def solved(self) -> bool:
|
||||
if self.optional:
|
||||
if (
|
||||
len(self.missing_optional_keys) > 0
|
||||
|
|
@ -334,45 +349,53 @@ class TemplatePartResult:
|
|||
)
|
||||
|
||||
@property
|
||||
def optional(self):
|
||||
def optional(self) -> bool:
|
||||
return self._optional
|
||||
|
||||
@property
|
||||
def output(self):
|
||||
def output(self) -> str:
|
||||
return self._output
|
||||
|
||||
@property
|
||||
def missing_keys(self):
|
||||
def missing_keys(self) -> Set[str]:
|
||||
return self._missing_keys
|
||||
|
||||
@property
|
||||
def missing_optional_keys(self):
|
||||
def missing_optional_keys(self) -> Set[str]:
|
||||
return self._missing_optional_keys
|
||||
|
||||
@property
|
||||
def invalid_types(self):
|
||||
def invalid_types(self) -> Dict[str, Any]:
|
||||
return self._invalid_types
|
||||
|
||||
@property
|
||||
def invalid_optional_types(self):
|
||||
def invalid_optional_types(self) -> Dict[str, Any]:
|
||||
return self._invalid_optional_types
|
||||
|
||||
@property
|
||||
def realy_used_values(self):
|
||||
return self._realy_used_values
|
||||
def really_used_values(self) -> Dict[str, Any]:
|
||||
return self._really_used_values
|
||||
|
||||
@property
|
||||
def used_values(self):
|
||||
def realy_used_values(self) -> Dict[str, Any]:
|
||||
warnings.warn(
|
||||
"Property 'realy_used_values' is deprecated."
|
||||
" Use 'really_used_values' instead.",
|
||||
DeprecationWarning
|
||||
)
|
||||
return self._really_used_values
|
||||
|
||||
@property
|
||||
def used_values(self) -> Dict[str, Any]:
|
||||
return self._used_values
|
||||
|
||||
@staticmethod
|
||||
def split_keys_to_subdicts(values):
|
||||
def split_keys_to_subdicts(values: Dict[str, Any]) -> Dict[str, Any]:
|
||||
output = {}
|
||||
formatter = Formatter()
|
||||
for key, value in values.items():
|
||||
key_padding = list(KEY_PADDING_PATTERN.findall(key))
|
||||
if key_padding:
|
||||
key = key_padding[0]
|
||||
key_subdict = list(SUB_DICT_PATTERN.findall(key))
|
||||
_, field_name, _, _ = next(formatter.parse(f"{{{key}}}"))
|
||||
key_subdict = list(SUB_DICT_PATTERN.findall(field_name))
|
||||
data = output
|
||||
last_key = key_subdict.pop(-1)
|
||||
for subkey in key_subdict:
|
||||
|
|
@ -382,7 +405,7 @@ class TemplatePartResult:
|
|||
data[last_key] = value
|
||||
return output
|
||||
|
||||
def get_clean_used_values(self):
|
||||
def get_clean_used_values(self) -> Dict[str, Any]:
|
||||
new_used_values = {}
|
||||
for key, value in self.used_values.items():
|
||||
if isinstance(value, FormatObject):
|
||||
|
|
@ -391,19 +414,27 @@ class TemplatePartResult:
|
|||
|
||||
return self.split_keys_to_subdicts(new_used_values)
|
||||
|
||||
def add_realy_used_value(self, key, value):
|
||||
self._realy_used_values[key] = value
|
||||
def add_really_used_value(self, key: str, value: Any):
|
||||
self._really_used_values[key] = value
|
||||
|
||||
def add_used_value(self, key, value):
|
||||
def add_realy_used_value(self, key: str, value: Any):
|
||||
warnings.warn(
|
||||
"Method 'add_realy_used_value' is deprecated."
|
||||
" Use 'add_really_used_value' instead.",
|
||||
DeprecationWarning
|
||||
)
|
||||
self.add_really_used_value(key, value)
|
||||
|
||||
def add_used_value(self, key: str, value: Any):
|
||||
self._used_values[key] = value
|
||||
|
||||
def add_missing_key(self, key):
|
||||
def add_missing_key(self, key: str):
|
||||
if self._optional:
|
||||
self._missing_optional_keys.add(key)
|
||||
else:
|
||||
self._missing_keys.add(key)
|
||||
|
||||
def add_invalid_type(self, key, value):
|
||||
def add_invalid_type(self, key: str, value: Any):
|
||||
if self._optional:
|
||||
self._invalid_optional_types[key] = type(value)
|
||||
else:
|
||||
|
|
@ -421,10 +452,10 @@ class FormatObject:
|
|||
def __format__(self, *args, **kwargs):
|
||||
return self.value.__format__(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return str(self.value)
|
||||
|
||||
def __repr__(self):
|
||||
def __repr__(self) -> str:
|
||||
return self.__str__()
|
||||
|
||||
|
||||
|
|
@ -434,23 +465,44 @@ class FormattingPart:
|
|||
Containt only single key to format e.g. "{project[name]}".
|
||||
|
||||
Args:
|
||||
template(str): String containing the formatting key.
|
||||
field_name (str): Name of key.
|
||||
format_spec (str): Format specification.
|
||||
conversion (Union[str, None]): Conversion type.
|
||||
|
||||
"""
|
||||
def __init__(self, template):
|
||||
self._template = template
|
||||
def __init__(
|
||||
self,
|
||||
field_name: str,
|
||||
format_spec: str,
|
||||
conversion: "Union[str, None]",
|
||||
):
|
||||
format_spec_v = ""
|
||||
if format_spec:
|
||||
format_spec_v = f":{format_spec}"
|
||||
conversion_v = ""
|
||||
if conversion:
|
||||
conversion_v = f"!{conversion}"
|
||||
|
||||
self._field_name: str = field_name
|
||||
self._format_spec: str = format_spec_v
|
||||
self._conversion: str = conversion_v
|
||||
|
||||
template_base = f"{field_name}{format_spec_v}{conversion_v}"
|
||||
self._template_base: str = template_base
|
||||
self._template: str = f"{{{template_base}}}"
|
||||
|
||||
@property
|
||||
def template(self):
|
||||
def template(self) -> str:
|
||||
return self._template
|
||||
|
||||
def __repr__(self):
|
||||
def __repr__(self) -> str:
|
||||
return "<Format:{}>".format(self._template)
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return self._template
|
||||
|
||||
@staticmethod
|
||||
def validate_value_type(value):
|
||||
def validate_value_type(value: Any) -> bool:
|
||||
"""Check if value can be used for formatting of single key."""
|
||||
if isinstance(value, (numbers.Number, FormatObject)):
|
||||
return True
|
||||
|
|
@ -461,7 +513,7 @@ class FormattingPart:
|
|||
return False
|
||||
|
||||
@staticmethod
|
||||
def validate_key_is_matched(key):
|
||||
def validate_key_is_matched(key: str) -> bool:
|
||||
"""Validate that opening has closing at correct place.
|
||||
Future-proof, only square brackets are currently used in keys.
|
||||
|
||||
|
|
@ -488,16 +540,29 @@ class FormattingPart:
|
|||
return False
|
||||
return not queue
|
||||
|
||||
def format(self, data, result):
|
||||
@staticmethod
|
||||
def keys_to_template_base(keys: List[str]):
|
||||
if not keys:
|
||||
return None
|
||||
# Create copy of keys
|
||||
keys = list(keys)
|
||||
template_base = keys.pop(0)
|
||||
joined_keys = "".join([f"[{key}]" for key in keys])
|
||||
return f"{template_base}{joined_keys}"
|
||||
|
||||
def format(
|
||||
self, data: Dict[str, Any], result: TemplatePartResult
|
||||
) -> TemplatePartResult:
|
||||
"""Format the formattings string.
|
||||
|
||||
Args:
|
||||
data(dict): Data that should be used for formatting.
|
||||
result(TemplatePartResult): Object where result is stored.
|
||||
|
||||
"""
|
||||
key = self.template[1:-1]
|
||||
if key in result.realy_used_values:
|
||||
result.add_output(result.realy_used_values[key])
|
||||
key = self._template_base
|
||||
if key in result.really_used_values:
|
||||
result.add_output(result.really_used_values[key])
|
||||
return result
|
||||
|
||||
# ensure key is properly formed [({})] properly closed.
|
||||
|
|
@ -507,17 +572,38 @@ class FormattingPart:
|
|||
return result
|
||||
|
||||
# check if key expects subdictionary keys (e.g. project[name])
|
||||
existence_check = key
|
||||
key_padding = list(KEY_PADDING_PATTERN.findall(existence_check))
|
||||
if key_padding:
|
||||
existence_check = key_padding[0]
|
||||
key_subdict = list(SUB_DICT_PATTERN.findall(existence_check))
|
||||
key_subdict = list(SUB_DICT_PATTERN.findall(self._field_name))
|
||||
|
||||
value = data
|
||||
missing_key = False
|
||||
invalid_type = False
|
||||
used_keys = []
|
||||
keys_to_value = None
|
||||
used_value = None
|
||||
|
||||
for sub_key in key_subdict:
|
||||
if isinstance(value, list):
|
||||
if not sub_key.lstrip("-").isdigit():
|
||||
invalid_type = True
|
||||
break
|
||||
sub_key = int(sub_key)
|
||||
if sub_key < 0:
|
||||
sub_key = len(value) + sub_key
|
||||
|
||||
invalid = 0 > sub_key < len(data)
|
||||
if invalid:
|
||||
used_keys.append(sub_key)
|
||||
missing_key = True
|
||||
break
|
||||
|
||||
used_keys.append(sub_key)
|
||||
if keys_to_value is None:
|
||||
keys_to_value = list(used_keys)
|
||||
keys_to_value.pop(-1)
|
||||
used_value = copy.deepcopy(value)
|
||||
value = value[sub_key]
|
||||
continue
|
||||
|
||||
if (
|
||||
value is None
|
||||
or (hasattr(value, "items") and sub_key not in value)
|
||||
|
|
@ -533,45 +619,57 @@ class FormattingPart:
|
|||
used_keys.append(sub_key)
|
||||
value = value.get(sub_key)
|
||||
|
||||
if missing_key or invalid_type:
|
||||
if len(used_keys) == 0:
|
||||
invalid_key = key_subdict[0]
|
||||
else:
|
||||
invalid_key = used_keys[0]
|
||||
for idx, sub_key in enumerate(used_keys):
|
||||
if idx == 0:
|
||||
continue
|
||||
invalid_key += "[{0}]".format(sub_key)
|
||||
field_name = key_subdict[0]
|
||||
if used_keys:
|
||||
field_name = self.keys_to_template_base(used_keys)
|
||||
|
||||
if missing_key or invalid_type:
|
||||
if missing_key:
|
||||
result.add_missing_key(invalid_key)
|
||||
result.add_missing_key(field_name)
|
||||
|
||||
elif invalid_type:
|
||||
result.add_invalid_type(invalid_key, value)
|
||||
result.add_invalid_type(field_name, value)
|
||||
|
||||
result.add_output(self.template)
|
||||
return result
|
||||
|
||||
if self.validate_value_type(value):
|
||||
fill_data = {}
|
||||
first_value = True
|
||||
for used_key in reversed(used_keys):
|
||||
if first_value:
|
||||
first_value = False
|
||||
fill_data[used_key] = value
|
||||
else:
|
||||
_fill_data = {used_key: fill_data}
|
||||
fill_data = _fill_data
|
||||
|
||||
formatted_value = self.template.format(**fill_data)
|
||||
result.add_realy_used_value(key, formatted_value)
|
||||
result.add_used_value(existence_check, formatted_value)
|
||||
result.add_output(formatted_value)
|
||||
if not self.validate_value_type(value):
|
||||
result.add_invalid_type(key, value)
|
||||
result.add_output(self.template)
|
||||
return result
|
||||
|
||||
result.add_invalid_type(key, value)
|
||||
result.add_output(self.template)
|
||||
fill_data = root_fill_data = {}
|
||||
parent_fill_data = None
|
||||
parent_key = None
|
||||
fill_value = data
|
||||
value_filled = False
|
||||
for used_key in used_keys:
|
||||
if isinstance(fill_value, list):
|
||||
parent_fill_data[parent_key] = fill_value
|
||||
value_filled = True
|
||||
break
|
||||
fill_value = fill_value[used_key]
|
||||
parent_fill_data = fill_data
|
||||
fill_data = parent_fill_data.setdefault(used_key, {})
|
||||
parent_key = used_key
|
||||
|
||||
if not value_filled:
|
||||
parent_fill_data[used_keys[-1]] = value
|
||||
|
||||
template = f"{{{field_name}{self._format_spec}{self._conversion}}}"
|
||||
formatted_value = template.format(**root_fill_data)
|
||||
used_key = key
|
||||
if keys_to_value is not None:
|
||||
used_key = self.keys_to_template_base(keys_to_value)
|
||||
|
||||
if used_value is None:
|
||||
if isinstance(value, numbers.Number):
|
||||
used_value = value
|
||||
else:
|
||||
used_value = formatted_value
|
||||
result.add_really_used_value(self._field_name, used_value)
|
||||
result.add_used_value(used_key, used_value)
|
||||
result.add_output(formatted_value)
|
||||
return result
|
||||
|
||||
|
||||
|
|
@ -585,20 +683,27 @@ class OptionalPart:
|
|||
'FormattingPart'.
|
||||
"""
|
||||
|
||||
def __init__(self, parts):
|
||||
self._parts = parts
|
||||
def __init__(
|
||||
self,
|
||||
parts: List["Union[str, OptionalPart, FormattingPart]"]
|
||||
):
|
||||
self._parts: List["Union[str, OptionalPart, FormattingPart]"] = parts
|
||||
|
||||
@property
|
||||
def parts(self):
|
||||
def parts(self) -> List["Union[str, OptionalPart, FormattingPart]"]:
|
||||
return self._parts
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return "<{}>".format("".join([str(p) for p in self._parts]))
|
||||
|
||||
def __repr__(self):
|
||||
def __repr__(self) -> str:
|
||||
return "<Optional:{}>".format("".join([str(p) for p in self._parts]))
|
||||
|
||||
def format(self, data, result):
|
||||
def format(
|
||||
self,
|
||||
data: Dict[str, Any],
|
||||
result: TemplatePartResult,
|
||||
) -> TemplatePartResult:
|
||||
new_result = TemplatePartResult(True)
|
||||
for part in self._parts:
|
||||
if isinstance(part, str):
|
||||
|
|
|
|||
|
|
@ -465,7 +465,9 @@ def update_container(container, version=-1):
|
|||
from ayon_core.pipeline import get_current_project_name
|
||||
|
||||
# Compute the different version from 'representation'
|
||||
project_name = get_current_project_name()
|
||||
project_name = container.get("project_name")
|
||||
if project_name is None:
|
||||
project_name = get_current_project_name()
|
||||
repre_id = container["representation"]
|
||||
if not _is_valid_representation_id(repre_id):
|
||||
raise ValueError(
|
||||
|
|
@ -542,9 +544,6 @@ def update_container(container, version=-1):
|
|||
)
|
||||
)
|
||||
|
||||
path = get_representation_path(new_representation)
|
||||
if not path or not os.path.exists(path):
|
||||
raise ValueError("Path {} doesn't exist".format(path))
|
||||
project_entity = ayon_api.get_project(project_name)
|
||||
context = {
|
||||
"project": project_entity,
|
||||
|
|
@ -553,6 +552,9 @@ def update_container(container, version=-1):
|
|||
"version": new_version,
|
||||
"representation": new_representation,
|
||||
}
|
||||
path = get_representation_path_from_context(context)
|
||||
if not path or not os.path.exists(path):
|
||||
raise ValueError("Path {} doesn't exist".format(path))
|
||||
|
||||
return Loader().update(container, context)
|
||||
|
||||
|
|
@ -588,7 +590,9 @@ def switch_container(container, representation, loader_plugin=None):
|
|||
)
|
||||
|
||||
# Get the new representation to switch to
|
||||
project_name = get_current_project_name()
|
||||
project_name = container.get("project_name")
|
||||
if project_name is None:
|
||||
project_name = get_current_project_name()
|
||||
|
||||
context = get_representation_context(
|
||||
project_name, representation["id"]
|
||||
|
|
|
|||
|
|
@ -87,14 +87,13 @@ def get_folder_template_data(folder_entity, project_name):
|
|||
"""
|
||||
|
||||
path = folder_entity["path"]
|
||||
hierarchy_parts = path.split("/")
|
||||
# Remove empty string from the beginning
|
||||
hierarchy_parts.pop(0)
|
||||
# Remove empty string from the beginning and split by '/'
|
||||
parents = path.lstrip("/").split("/")
|
||||
# Remove last part which is folder name
|
||||
folder_name = hierarchy_parts.pop(-1)
|
||||
hierarchy = "/".join(hierarchy_parts)
|
||||
if hierarchy_parts:
|
||||
parent_name = hierarchy_parts[-1]
|
||||
folder_name = parents.pop(-1)
|
||||
hierarchy = "/".join(parents)
|
||||
if parents:
|
||||
parent_name = parents[-1]
|
||||
else:
|
||||
parent_name = project_name
|
||||
|
||||
|
|
@ -103,6 +102,7 @@ def get_folder_template_data(folder_entity, project_name):
|
|||
"name": folder_name,
|
||||
"type": folder_entity["folderType"],
|
||||
"path": path,
|
||||
"parents": parents,
|
||||
},
|
||||
"asset": folder_name,
|
||||
"hierarchy": hierarchy,
|
||||
|
|
|
|||
|
|
@ -434,6 +434,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin):
|
|||
# Using 'Shot' is current default behavior of editorial
|
||||
# (or 'newHierarchyIntegration') publishing.
|
||||
"type": "Shot",
|
||||
"parents": parents,
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -86,8 +86,9 @@ class SceneInventoryController:
|
|||
self._current_folder_set = True
|
||||
return self._current_folder_id
|
||||
|
||||
def get_project_status_items(self):
|
||||
project_name = self.get_current_project_name()
|
||||
def get_project_status_items(self, project_name=None):
|
||||
if project_name is None:
|
||||
project_name = self.get_current_project_name()
|
||||
return self._projects_model.get_project_status_items(
|
||||
project_name, None
|
||||
)
|
||||
|
|
@ -105,32 +106,39 @@ class SceneInventoryController:
|
|||
def get_container_items_by_id(self, item_ids):
|
||||
return self._containers_model.get_container_items_by_id(item_ids)
|
||||
|
||||
def get_representation_info_items(self, representation_ids):
|
||||
def get_representation_info_items(self, project_name, representation_ids):
|
||||
return self._containers_model.get_representation_info_items(
|
||||
representation_ids
|
||||
project_name, representation_ids
|
||||
)
|
||||
|
||||
def get_version_items(self, product_ids):
|
||||
return self._containers_model.get_version_items(product_ids)
|
||||
def get_version_items(self, project_name, product_ids):
|
||||
return self._containers_model.get_version_items(
|
||||
project_name, product_ids)
|
||||
|
||||
# Site Sync methods
|
||||
def is_sitesync_enabled(self):
|
||||
return self._sitesync_model.is_sitesync_enabled()
|
||||
|
||||
def get_sites_information(self):
|
||||
return self._sitesync_model.get_sites_information()
|
||||
def get_sites_information(self, project_name):
|
||||
return self._sitesync_model.get_sites_information(project_name)
|
||||
|
||||
def get_site_provider_icons(self):
|
||||
return self._sitesync_model.get_site_provider_icons()
|
||||
|
||||
def get_representations_site_progress(self, representation_ids):
|
||||
def get_representations_site_progress(
|
||||
self, project_name, representation_ids
|
||||
):
|
||||
return self._sitesync_model.get_representations_site_progress(
|
||||
representation_ids
|
||||
project_name, representation_ids
|
||||
)
|
||||
|
||||
def resync_representations(self, representation_ids, site_type):
|
||||
def resync_representations(
|
||||
self, project_name, representation_ids, site_type
|
||||
):
|
||||
return self._sitesync_model.resync_representations(
|
||||
representation_ids, site_type
|
||||
project_name,
|
||||
representation_ids,
|
||||
site_type
|
||||
)
|
||||
|
||||
# Switch dialog methods
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ REMOTE_SITE_ICON_ROLE = QtCore.Qt.UserRole + 23
|
|||
# This value hold unique value of container that should be used to identify
|
||||
# containers inbetween refresh.
|
||||
ITEM_UNIQUE_NAME_ROLE = QtCore.Qt.UserRole + 24
|
||||
PROJECT_NAME_ROLE = QtCore.Qt.UserRole + 25
|
||||
|
||||
|
||||
class InventoryModel(QtGui.QStandardItemModel):
|
||||
|
|
@ -52,6 +53,7 @@ class InventoryModel(QtGui.QStandardItemModel):
|
|||
"Object name",
|
||||
"Active site",
|
||||
"Remote site",
|
||||
"Project",
|
||||
]
|
||||
name_col = column_labels.index("Name")
|
||||
version_col = column_labels.index("Version")
|
||||
|
|
@ -63,6 +65,7 @@ class InventoryModel(QtGui.QStandardItemModel):
|
|||
object_name_col = column_labels.index("Object name")
|
||||
active_site_col = column_labels.index("Active site")
|
||||
remote_site_col = column_labels.index("Remote site")
|
||||
project_col = column_labels.index("Project")
|
||||
display_role_by_column = {
|
||||
name_col: QtCore.Qt.DisplayRole,
|
||||
version_col: VERSION_LABEL_ROLE,
|
||||
|
|
@ -72,6 +75,7 @@ class InventoryModel(QtGui.QStandardItemModel):
|
|||
product_group_col: PRODUCT_GROUP_NAME_ROLE,
|
||||
loader_col: LOADER_NAME_ROLE,
|
||||
object_name_col: OBJECT_NAME_ROLE,
|
||||
project_col: PROJECT_NAME_ROLE,
|
||||
active_site_col: ACTIVE_SITE_PROGRESS_ROLE,
|
||||
remote_site_col: REMOTE_SITE_PROGRESS_ROLE,
|
||||
}
|
||||
|
|
@ -85,7 +89,7 @@ class InventoryModel(QtGui.QStandardItemModel):
|
|||
foreground_role_by_column = {
|
||||
name_col: NAME_COLOR_ROLE,
|
||||
version_col: VERSION_COLOR_ROLE,
|
||||
status_col: STATUS_COLOR_ROLE
|
||||
status_col: STATUS_COLOR_ROLE,
|
||||
}
|
||||
width_by_column = {
|
||||
name_col: 250,
|
||||
|
|
@ -95,6 +99,7 @@ class InventoryModel(QtGui.QStandardItemModel):
|
|||
product_type_col: 150,
|
||||
product_group_col: 120,
|
||||
loader_col: 150,
|
||||
project_col: 150,
|
||||
}
|
||||
|
||||
OUTDATED_COLOR = QtGui.QColor(235, 30, 30)
|
||||
|
|
@ -116,8 +121,8 @@ class InventoryModel(QtGui.QStandardItemModel):
|
|||
|
||||
self._default_icon_color = get_default_entity_icon_color()
|
||||
|
||||
self._last_project_statuses = {}
|
||||
self._last_status_icons_by_name = {}
|
||||
self._last_project_statuses = collections.defaultdict(dict)
|
||||
self._last_status_icons_by_name = collections.defaultdict(dict)
|
||||
|
||||
def outdated(self, item):
|
||||
return item.get("isOutdated", True)
|
||||
|
|
@ -129,45 +134,73 @@ class InventoryModel(QtGui.QStandardItemModel):
|
|||
|
||||
self._clear_items()
|
||||
|
||||
items_by_repre_id = {}
|
||||
project_names = set()
|
||||
repre_ids_by_project = collections.defaultdict(set)
|
||||
version_items_by_project = collections.defaultdict(dict)
|
||||
repre_info_by_id_by_project = collections.defaultdict(dict)
|
||||
item_by_repre_id_by_project = collections.defaultdict(
|
||||
lambda: collections.defaultdict(list))
|
||||
for container_item in container_items:
|
||||
# if (
|
||||
# selected is not None
|
||||
# and container_item.item_id not in selected
|
||||
# ):
|
||||
# continue
|
||||
repre_id = container_item.representation_id
|
||||
items = items_by_repre_id.setdefault(repre_id, [])
|
||||
items.append(container_item)
|
||||
project_name = container_item.project_name
|
||||
representation_id = container_item.representation_id
|
||||
project_names.add(project_name)
|
||||
repre_ids_by_project[project_name].add(representation_id)
|
||||
(
|
||||
item_by_repre_id_by_project
|
||||
[project_name]
|
||||
[representation_id]
|
||||
).append(container_item)
|
||||
|
||||
for project_name, representation_ids in repre_ids_by_project.items():
|
||||
repre_info = self._controller.get_representation_info_items(
|
||||
project_name, representation_ids
|
||||
)
|
||||
repre_info_by_id_by_project[project_name] = repre_info
|
||||
|
||||
product_ids = {
|
||||
repre_info.product_id
|
||||
for repre_info in repre_info.values()
|
||||
if repre_info.is_valid
|
||||
}
|
||||
version_items = self._controller.get_version_items(
|
||||
project_name, product_ids
|
||||
)
|
||||
version_items_by_project[project_name] = version_items
|
||||
|
||||
repre_id = set(items_by_repre_id.keys())
|
||||
repre_info_by_id = self._controller.get_representation_info_items(
|
||||
repre_id
|
||||
)
|
||||
product_ids = {
|
||||
repre_info.product_id
|
||||
for repre_info in repre_info_by_id.values()
|
||||
if repre_info.is_valid
|
||||
}
|
||||
version_items_by_product_id = self._controller.get_version_items(
|
||||
product_ids
|
||||
)
|
||||
# SiteSync addon information
|
||||
progress_by_id = self._controller.get_representations_site_progress(
|
||||
repre_id
|
||||
)
|
||||
sites_info = self._controller.get_sites_information()
|
||||
progress_by_project = {
|
||||
project_name: self._controller.get_representations_site_progress(
|
||||
project_name, repre_ids
|
||||
)
|
||||
for project_name, repre_ids in repre_ids_by_project.items()
|
||||
}
|
||||
|
||||
sites_info_by_project_name = {
|
||||
project_name: self._controller.get_sites_information(project_name)
|
||||
for project_name in project_names
|
||||
}
|
||||
site_icons = {
|
||||
provider: get_qt_icon(icon_def)
|
||||
for provider, icon_def in (
|
||||
self._controller.get_site_provider_icons().items()
|
||||
)
|
||||
}
|
||||
self._last_project_statuses = {
|
||||
status_item.name: status_item
|
||||
for status_item in self._controller.get_project_status_items()
|
||||
}
|
||||
self._last_status_icons_by_name = {}
|
||||
last_project_statuses = collections.defaultdict(dict)
|
||||
for project_name in project_names:
|
||||
status_items_by_name = {
|
||||
status_item.name: status_item
|
||||
for status_item in self._controller.get_project_status_items(
|
||||
project_name
|
||||
)
|
||||
}
|
||||
last_project_statuses[project_name] = status_items_by_name
|
||||
self._last_project_statuses = last_project_statuses
|
||||
self._last_status_icons_by_name = collections.defaultdict(dict)
|
||||
|
||||
group_item_icon = qtawesome.icon(
|
||||
"fa.folder", color=self._default_icon_color
|
||||
|
|
@ -187,118 +220,130 @@ class InventoryModel(QtGui.QStandardItemModel):
|
|||
group_item_font = QtGui.QFont()
|
||||
group_item_font.setBold(True)
|
||||
|
||||
active_site_icon = site_icons.get(sites_info["active_site_provider"])
|
||||
remote_site_icon = site_icons.get(sites_info["remote_site_provider"])
|
||||
|
||||
root_item = self.invisibleRootItem()
|
||||
group_items = []
|
||||
for repre_id, container_items in items_by_repre_id.items():
|
||||
repre_info = repre_info_by_id[repre_id]
|
||||
version_color = None
|
||||
if not repre_info.is_valid:
|
||||
version_label = "N/A"
|
||||
group_name = "< Entity N/A >"
|
||||
item_icon = invalid_item_icon
|
||||
is_latest = False
|
||||
is_hero = False
|
||||
status_name = None
|
||||
for project_name, items_by_repre_id in (
|
||||
item_by_repre_id_by_project.items()
|
||||
):
|
||||
sites_info = sites_info_by_project_name[project_name]
|
||||
active_site_icon = site_icons.get(
|
||||
sites_info["active_site_provider"]
|
||||
)
|
||||
remote_site_icon = site_icons.get(
|
||||
sites_info["remote_site_provider"]
|
||||
)
|
||||
|
||||
else:
|
||||
group_name = "{}_{}: ({})".format(
|
||||
repre_info.folder_path.rsplit("/")[-1],
|
||||
repre_info.product_name,
|
||||
repre_info.representation_name
|
||||
progress_by_id = progress_by_project[project_name]
|
||||
repre_info_by_id = repre_info_by_id_by_project[project_name]
|
||||
version_items_by_product_id = (
|
||||
version_items_by_project[project_name]
|
||||
)
|
||||
for repre_id, container_items in items_by_repre_id.items():
|
||||
repre_info = repre_info_by_id[repre_id]
|
||||
version_color = None
|
||||
if not repre_info.is_valid:
|
||||
version_label = "N/A"
|
||||
group_name = "< Entity N/A >"
|
||||
item_icon = invalid_item_icon
|
||||
is_latest = False
|
||||
is_hero = False
|
||||
status_name = None
|
||||
|
||||
else:
|
||||
group_name = "{}_{}: ({})".format(
|
||||
repre_info.folder_path.rsplit("/")[-1],
|
||||
repre_info.product_name,
|
||||
repre_info.representation_name
|
||||
)
|
||||
item_icon = valid_item_icon
|
||||
|
||||
version_items = (
|
||||
version_items_by_product_id[repre_info.product_id]
|
||||
)
|
||||
version_item = version_items[repre_info.version_id]
|
||||
version_label = format_version(version_item.version)
|
||||
is_hero = version_item.version < 0
|
||||
is_latest = version_item.is_latest
|
||||
if not version_item.is_latest:
|
||||
version_color = self.OUTDATED_COLOR
|
||||
status_name = version_item.status
|
||||
|
||||
(
|
||||
status_color, status_short, status_icon
|
||||
) = self._get_status_data(project_name, status_name)
|
||||
|
||||
repre_name = (
|
||||
repre_info.representation_name or
|
||||
"<unknown representation>"
|
||||
)
|
||||
item_icon = valid_item_icon
|
||||
container_model_items = []
|
||||
for container_item in container_items:
|
||||
object_name = container_item.object_name or "<none>"
|
||||
unique_name = repre_name + object_name
|
||||
item = QtGui.QStandardItem()
|
||||
item.setColumnCount(root_item.columnCount())
|
||||
item.setData(container_item.namespace,
|
||||
QtCore.Qt.DisplayRole)
|
||||
item.setData(self.GRAYOUT_COLOR, NAME_COLOR_ROLE)
|
||||
item.setData(self.GRAYOUT_COLOR, VERSION_COLOR_ROLE)
|
||||
item.setData(item_icon, QtCore.Qt.DecorationRole)
|
||||
item.setData(repre_info.product_id, PRODUCT_ID_ROLE)
|
||||
item.setData(container_item.item_id, ITEM_ID_ROLE)
|
||||
item.setData(version_label, VERSION_LABEL_ROLE)
|
||||
item.setData(container_item.loader_name, LOADER_NAME_ROLE)
|
||||
item.setData(container_item.object_name, OBJECT_NAME_ROLE)
|
||||
item.setData(True, IS_CONTAINER_ITEM_ROLE)
|
||||
item.setData(unique_name, ITEM_UNIQUE_NAME_ROLE)
|
||||
container_model_items.append(item)
|
||||
|
||||
version_items = (
|
||||
version_items_by_product_id[repre_info.product_id]
|
||||
progress = progress_by_id[repre_id]
|
||||
active_site_progress = "{}%".format(
|
||||
max(progress["active_site"], 0) * 100
|
||||
)
|
||||
remote_site_progress = "{}%".format(
|
||||
max(progress["remote_site"], 0) * 100
|
||||
)
|
||||
version_item = version_items[repre_info.version_id]
|
||||
version_label = format_version(version_item.version)
|
||||
is_hero = version_item.version < 0
|
||||
is_latest = version_item.is_latest
|
||||
if not version_item.is_latest:
|
||||
version_color = self.OUTDATED_COLOR
|
||||
status_name = version_item.status
|
||||
|
||||
status_color, status_short, status_icon = self._get_status_data(
|
||||
status_name
|
||||
)
|
||||
group_item = QtGui.QStandardItem()
|
||||
group_item.setColumnCount(root_item.columnCount())
|
||||
group_item.setData(group_name, QtCore.Qt.DisplayRole)
|
||||
group_item.setData(group_name, ITEM_UNIQUE_NAME_ROLE)
|
||||
group_item.setData(group_item_icon, QtCore.Qt.DecorationRole)
|
||||
group_item.setData(group_item_font, QtCore.Qt.FontRole)
|
||||
group_item.setData(repre_info.product_id, PRODUCT_ID_ROLE)
|
||||
group_item.setData(repre_info.product_type, PRODUCT_TYPE_ROLE)
|
||||
group_item.setData(product_type_icon, PRODUCT_TYPE_ICON_ROLE)
|
||||
group_item.setData(is_latest, VERSION_IS_LATEST_ROLE)
|
||||
group_item.setData(is_hero, VERSION_IS_HERO_ROLE)
|
||||
group_item.setData(version_label, VERSION_LABEL_ROLE)
|
||||
group_item.setData(len(container_items), COUNT_ROLE)
|
||||
group_item.setData(status_name, STATUS_NAME_ROLE)
|
||||
group_item.setData(status_short, STATUS_SHORT_ROLE)
|
||||
group_item.setData(status_color, STATUS_COLOR_ROLE)
|
||||
group_item.setData(status_icon, STATUS_ICON_ROLE)
|
||||
group_item.setData(project_name, PROJECT_NAME_ROLE)
|
||||
|
||||
repre_name = (
|
||||
repre_info.representation_name or "<unknown representation>"
|
||||
)
|
||||
container_model_items = []
|
||||
for container_item in container_items:
|
||||
object_name = container_item.object_name or "<none>"
|
||||
unique_name = repre_name + object_name
|
||||
|
||||
item = QtGui.QStandardItem()
|
||||
item.setColumnCount(root_item.columnCount())
|
||||
item.setData(container_item.namespace, QtCore.Qt.DisplayRole)
|
||||
item.setData(self.GRAYOUT_COLOR, NAME_COLOR_ROLE)
|
||||
item.setData(self.GRAYOUT_COLOR, VERSION_COLOR_ROLE)
|
||||
item.setData(item_icon, QtCore.Qt.DecorationRole)
|
||||
item.setData(repre_info.product_id, PRODUCT_ID_ROLE)
|
||||
item.setData(container_item.item_id, ITEM_ID_ROLE)
|
||||
item.setData(version_label, VERSION_LABEL_ROLE)
|
||||
item.setData(container_item.loader_name, LOADER_NAME_ROLE)
|
||||
item.setData(container_item.object_name, OBJECT_NAME_ROLE)
|
||||
item.setData(True, IS_CONTAINER_ITEM_ROLE)
|
||||
item.setData(unique_name, ITEM_UNIQUE_NAME_ROLE)
|
||||
container_model_items.append(item)
|
||||
|
||||
if not container_model_items:
|
||||
continue
|
||||
|
||||
progress = progress_by_id[repre_id]
|
||||
active_site_progress = "{}%".format(
|
||||
max(progress["active_site"], 0) * 100
|
||||
)
|
||||
remote_site_progress = "{}%".format(
|
||||
max(progress["remote_site"], 0) * 100
|
||||
)
|
||||
|
||||
group_item = QtGui.QStandardItem()
|
||||
group_item.setColumnCount(root_item.columnCount())
|
||||
group_item.setData(group_name, QtCore.Qt.DisplayRole)
|
||||
group_item.setData(group_name, ITEM_UNIQUE_NAME_ROLE)
|
||||
group_item.setData(group_item_icon, QtCore.Qt.DecorationRole)
|
||||
group_item.setData(group_item_font, QtCore.Qt.FontRole)
|
||||
group_item.setData(repre_info.product_id, PRODUCT_ID_ROLE)
|
||||
group_item.setData(repre_info.product_type, PRODUCT_TYPE_ROLE)
|
||||
group_item.setData(product_type_icon, PRODUCT_TYPE_ICON_ROLE)
|
||||
group_item.setData(is_latest, VERSION_IS_LATEST_ROLE)
|
||||
group_item.setData(is_hero, VERSION_IS_HERO_ROLE)
|
||||
group_item.setData(version_label, VERSION_LABEL_ROLE)
|
||||
group_item.setData(len(container_items), COUNT_ROLE)
|
||||
group_item.setData(status_name, STATUS_NAME_ROLE)
|
||||
group_item.setData(status_short, STATUS_SHORT_ROLE)
|
||||
group_item.setData(status_color, STATUS_COLOR_ROLE)
|
||||
group_item.setData(status_icon, STATUS_ICON_ROLE)
|
||||
|
||||
group_item.setData(
|
||||
active_site_progress, ACTIVE_SITE_PROGRESS_ROLE
|
||||
)
|
||||
group_item.setData(
|
||||
remote_site_progress, REMOTE_SITE_PROGRESS_ROLE
|
||||
)
|
||||
group_item.setData(active_site_icon, ACTIVE_SITE_ICON_ROLE)
|
||||
group_item.setData(remote_site_icon, REMOTE_SITE_ICON_ROLE)
|
||||
group_item.setData(False, IS_CONTAINER_ITEM_ROLE)
|
||||
|
||||
if version_color is not None:
|
||||
group_item.setData(version_color, VERSION_COLOR_ROLE)
|
||||
|
||||
if repre_info.product_group:
|
||||
group_item.setData(
|
||||
repre_info.product_group, PRODUCT_GROUP_NAME_ROLE
|
||||
active_site_progress, ACTIVE_SITE_PROGRESS_ROLE
|
||||
)
|
||||
group_item.setData(group_icon, PRODUCT_GROUP_ICON_ROLE)
|
||||
group_item.setData(
|
||||
remote_site_progress, REMOTE_SITE_PROGRESS_ROLE
|
||||
)
|
||||
group_item.setData(active_site_icon, ACTIVE_SITE_ICON_ROLE)
|
||||
group_item.setData(remote_site_icon, REMOTE_SITE_ICON_ROLE)
|
||||
group_item.setData(False, IS_CONTAINER_ITEM_ROLE)
|
||||
|
||||
group_item.appendRows(container_model_items)
|
||||
group_items.append(group_item)
|
||||
if version_color is not None:
|
||||
group_item.setData(version_color, VERSION_COLOR_ROLE)
|
||||
|
||||
if repre_info.product_group:
|
||||
group_item.setData(
|
||||
repre_info.product_group, PRODUCT_GROUP_NAME_ROLE
|
||||
)
|
||||
group_item.setData(group_icon, PRODUCT_GROUP_ICON_ROLE)
|
||||
|
||||
group_item.appendRows(container_model_items)
|
||||
group_items.append(group_item)
|
||||
|
||||
if group_items:
|
||||
root_item.appendRows(group_items)
|
||||
|
|
@ -359,17 +404,21 @@ class InventoryModel(QtGui.QStandardItemModel):
|
|||
root_item = self.invisibleRootItem()
|
||||
root_item.removeRows(0, root_item.rowCount())
|
||||
|
||||
def _get_status_data(self, status_name):
|
||||
status_item = self._last_project_statuses.get(status_name)
|
||||
status_icon = self._get_status_icon(status_name, status_item)
|
||||
def _get_status_data(self, project_name, status_name):
|
||||
status_item = self._last_project_statuses[project_name].get(
|
||||
status_name
|
||||
)
|
||||
status_icon = self._get_status_icon(
|
||||
project_name, status_name, status_item
|
||||
)
|
||||
status_color = status_short = None
|
||||
if status_item is not None:
|
||||
status_color = status_item.color
|
||||
status_short = status_item.short
|
||||
return status_color, status_short, status_icon
|
||||
|
||||
def _get_status_icon(self, status_name, status_item):
|
||||
icon = self._last_status_icons_by_name.get(status_name)
|
||||
def _get_status_icon(self, project_name, status_name, status_item):
|
||||
icon = self._last_status_icons_by_name[project_name].get(status_name)
|
||||
if icon is not None:
|
||||
return icon
|
||||
|
||||
|
|
@ -382,7 +431,7 @@ class InventoryModel(QtGui.QStandardItemModel):
|
|||
})
|
||||
if icon is None:
|
||||
icon = QtGui.QIcon()
|
||||
self._last_status_icons_by_name[status_name] = icon
|
||||
self._last_status_icons_by_name[project_name][status_name] = icon
|
||||
return icon
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -93,22 +93,27 @@ class ContainerItem:
|
|||
loader_name,
|
||||
namespace,
|
||||
object_name,
|
||||
item_id
|
||||
item_id,
|
||||
project_name
|
||||
):
|
||||
self.representation_id = representation_id
|
||||
self.loader_name = loader_name
|
||||
self.object_name = object_name
|
||||
self.namespace = namespace
|
||||
self.item_id = item_id
|
||||
self.project_name = project_name
|
||||
|
||||
@classmethod
|
||||
def from_container_data(cls, container):
|
||||
def from_container_data(cls, current_project_name, container):
|
||||
return cls(
|
||||
representation_id=container["representation"],
|
||||
loader_name=container["loader"],
|
||||
namespace=container["namespace"],
|
||||
object_name=container["objectName"],
|
||||
item_id=uuid.uuid4().hex,
|
||||
project_name=container.get(
|
||||
"project_name", current_project_name
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -219,7 +224,7 @@ class ContainersModel:
|
|||
for item_id in item_ids
|
||||
}
|
||||
|
||||
def get_representation_info_items(self, representation_ids):
|
||||
def get_representation_info_items(self, project_name, representation_ids):
|
||||
output = {}
|
||||
missing_repre_ids = set()
|
||||
for repre_id in representation_ids:
|
||||
|
|
@ -228,17 +233,14 @@ class ContainersModel:
|
|||
except ValueError:
|
||||
output[repre_id] = RepresentationInfo.new_invalid()
|
||||
continue
|
||||
|
||||
repre_info = self._repre_info_by_id.get(repre_id)
|
||||
if repre_info is None:
|
||||
missing_repre_ids.add(repre_id)
|
||||
else:
|
||||
output[repre_id] = repre_info
|
||||
|
||||
if not missing_repre_ids:
|
||||
return output
|
||||
|
||||
project_name = self._controller.get_current_project_name()
|
||||
repre_hierarchy_by_id = get_representations_hierarchy(
|
||||
project_name, missing_repre_ids
|
||||
)
|
||||
|
|
@ -276,10 +278,9 @@ class ContainersModel:
|
|||
output[repre_id] = repre_info
|
||||
return output
|
||||
|
||||
def get_version_items(self, product_ids):
|
||||
def get_version_items(self, project_name, product_ids):
|
||||
if not product_ids:
|
||||
return {}
|
||||
|
||||
missing_ids = {
|
||||
product_id
|
||||
for product_id in product_ids
|
||||
|
|
@ -294,7 +295,6 @@ class ContainersModel:
|
|||
def version_sorted(entity):
|
||||
return entity["version"]
|
||||
|
||||
project_name = self._controller.get_current_project_name()
|
||||
version_entities_by_product_id = {
|
||||
product_id: []
|
||||
for product_id in missing_ids
|
||||
|
|
@ -359,9 +359,11 @@ class ContainersModel:
|
|||
containers_by_id = {}
|
||||
container_items_by_id = {}
|
||||
invalid_ids_mapping = {}
|
||||
current_project_name = self._controller.get_current_project_name()
|
||||
for container in containers:
|
||||
try:
|
||||
item = ContainerItem.from_container_data(container)
|
||||
item = ContainerItem.from_container_data(
|
||||
current_project_name, container)
|
||||
repre_id = item.representation_id
|
||||
try:
|
||||
uuid.UUID(repre_id)
|
||||
|
|
|
|||
|
|
@ -11,18 +11,18 @@ class SiteSyncModel:
|
|||
|
||||
self._sitesync_addon = NOT_SET
|
||||
self._sitesync_enabled = None
|
||||
self._active_site = NOT_SET
|
||||
self._remote_site = NOT_SET
|
||||
self._active_site_provider = NOT_SET
|
||||
self._remote_site_provider = NOT_SET
|
||||
self._active_site = {}
|
||||
self._remote_site = {}
|
||||
self._active_site_provider = {}
|
||||
self._remote_site_provider = {}
|
||||
|
||||
def reset(self):
|
||||
self._sitesync_addon = NOT_SET
|
||||
self._sitesync_enabled = None
|
||||
self._active_site = NOT_SET
|
||||
self._remote_site = NOT_SET
|
||||
self._active_site_provider = NOT_SET
|
||||
self._remote_site_provider = NOT_SET
|
||||
self._active_site = {}
|
||||
self._remote_site = {}
|
||||
self._active_site_provider = {}
|
||||
self._remote_site_provider = {}
|
||||
|
||||
def is_sitesync_enabled(self):
|
||||
"""Site sync is enabled.
|
||||
|
|
@ -46,15 +46,21 @@ class SiteSyncModel:
|
|||
sitesync_addon = self._get_sitesync_addon()
|
||||
return sitesync_addon.get_site_icons()
|
||||
|
||||
def get_sites_information(self):
|
||||
def get_sites_information(self, project_name):
|
||||
return {
|
||||
"active_site": self._get_active_site(),
|
||||
"active_site_provider": self._get_active_site_provider(),
|
||||
"remote_site": self._get_remote_site(),
|
||||
"remote_site_provider": self._get_remote_site_provider()
|
||||
"active_site": self._get_active_site(project_name),
|
||||
"remote_site": self._get_remote_site(project_name),
|
||||
"active_site_provider": self._get_active_site_provider(
|
||||
project_name
|
||||
),
|
||||
"remote_site_provider": self._get_remote_site_provider(
|
||||
project_name
|
||||
)
|
||||
}
|
||||
|
||||
def get_representations_site_progress(self, representation_ids):
|
||||
def get_representations_site_progress(
|
||||
self, project_name, representation_ids
|
||||
):
|
||||
"""Get progress of representations sync."""
|
||||
|
||||
representation_ids = set(representation_ids)
|
||||
|
|
@ -68,13 +74,12 @@ class SiteSyncModel:
|
|||
if not self.is_sitesync_enabled():
|
||||
return output
|
||||
|
||||
project_name = self._controller.get_current_project_name()
|
||||
sitesync_addon = self._get_sitesync_addon()
|
||||
repre_entities = ayon_api.get_representations(
|
||||
project_name, representation_ids
|
||||
)
|
||||
active_site = self._get_active_site()
|
||||
remote_site = self._get_remote_site()
|
||||
active_site = self._get_active_site(project_name)
|
||||
remote_site = self._get_remote_site(project_name)
|
||||
|
||||
for repre_entity in repre_entities:
|
||||
repre_output = output[repre_entity["id"]]
|
||||
|
|
@ -86,20 +91,21 @@ class SiteSyncModel:
|
|||
|
||||
return output
|
||||
|
||||
def resync_representations(self, representation_ids, site_type):
|
||||
def resync_representations(
|
||||
self, project_name, representation_ids, site_type
|
||||
):
|
||||
"""
|
||||
|
||||
Args:
|
||||
project_name (str): Project name.
|
||||
representation_ids (Iterable[str]): Representation ids.
|
||||
site_type (Literal[active_site, remote_site]): Site type.
|
||||
"""
|
||||
|
||||
project_name = self._controller.get_current_project_name()
|
||||
sitesync_addon = self._get_sitesync_addon()
|
||||
active_site = self._get_active_site()
|
||||
remote_site = self._get_remote_site()
|
||||
active_site = self._get_active_site(project_name)
|
||||
remote_site = self._get_remote_site(project_name)
|
||||
progress = self.get_representations_site_progress(
|
||||
representation_ids
|
||||
project_name, representation_ids
|
||||
)
|
||||
for repre_id in representation_ids:
|
||||
repre_progress = progress.get(repre_id)
|
||||
|
|
@ -132,48 +138,49 @@ class SiteSyncModel:
|
|||
self._sitesync_addon = sitesync_addon
|
||||
self._sitesync_enabled = sync_enabled
|
||||
|
||||
def _get_active_site(self):
|
||||
if self._active_site is NOT_SET:
|
||||
self._cache_sites()
|
||||
return self._active_site
|
||||
def _get_active_site(self, project_name):
|
||||
if project_name not in self._active_site:
|
||||
self._cache_sites(project_name)
|
||||
return self._active_site[project_name]
|
||||
|
||||
def _get_remote_site(self):
|
||||
if self._remote_site is NOT_SET:
|
||||
self._cache_sites()
|
||||
return self._remote_site
|
||||
def _get_remote_site(self, project_name):
|
||||
if project_name not in self._remote_site:
|
||||
self._cache_sites(project_name)
|
||||
return self._remote_site[project_name]
|
||||
|
||||
def _get_active_site_provider(self):
|
||||
if self._active_site_provider is NOT_SET:
|
||||
self._cache_sites()
|
||||
return self._active_site_provider
|
||||
def _get_active_site_provider(self, project_name):
|
||||
if project_name not in self._active_site_provider:
|
||||
self._cache_sites(project_name)
|
||||
return self._active_site_provider[project_name]
|
||||
|
||||
def _get_remote_site_provider(self):
|
||||
if self._remote_site_provider is NOT_SET:
|
||||
self._cache_sites()
|
||||
return self._remote_site_provider
|
||||
def _get_remote_site_provider(self, project_name):
|
||||
if project_name not in self._remote_site_provider:
|
||||
self._cache_sites(project_name)
|
||||
return self._remote_site_provider[project_name]
|
||||
|
||||
def _cache_sites(self):
|
||||
active_site = None
|
||||
remote_site = None
|
||||
active_site_provider = None
|
||||
remote_site_provider = None
|
||||
if self.is_sitesync_enabled():
|
||||
sitesync_addon = self._get_sitesync_addon()
|
||||
project_name = self._controller.get_current_project_name()
|
||||
active_site = sitesync_addon.get_active_site(project_name)
|
||||
remote_site = sitesync_addon.get_remote_site(project_name)
|
||||
active_site_provider = "studio"
|
||||
remote_site_provider = "studio"
|
||||
if active_site != "studio":
|
||||
active_site_provider = sitesync_addon.get_provider_for_site(
|
||||
project_name, active_site
|
||||
)
|
||||
if remote_site != "studio":
|
||||
remote_site_provider = sitesync_addon.get_provider_for_site(
|
||||
project_name, remote_site
|
||||
)
|
||||
def _cache_sites(self, project_name):
|
||||
self._active_site[project_name] = None
|
||||
self._remote_site[project_name] = None
|
||||
self._active_site_provider[project_name] = None
|
||||
self._remote_site_provider[project_name] = None
|
||||
if not self.is_sitesync_enabled():
|
||||
return
|
||||
|
||||
self._active_site = active_site
|
||||
self._remote_site = remote_site
|
||||
self._active_site_provider = active_site_provider
|
||||
self._remote_site_provider = remote_site_provider
|
||||
sitesync_addon = self._get_sitesync_addon()
|
||||
active_site = sitesync_addon.get_active_site(project_name)
|
||||
remote_site = sitesync_addon.get_remote_site(project_name)
|
||||
active_site_provider = "studio"
|
||||
remote_site_provider = "studio"
|
||||
if active_site != "studio":
|
||||
active_site_provider = sitesync_addon.get_provider_for_site(
|
||||
project_name, active_site
|
||||
)
|
||||
if remote_site != "studio":
|
||||
remote_site_provider = sitesync_addon.get_provider_for_site(
|
||||
project_name, remote_site
|
||||
)
|
||||
|
||||
self._active_site[project_name] = active_site
|
||||
self._remote_site[project_name] = remote_site
|
||||
self._active_site_provider[project_name] = active_site_provider
|
||||
self._remote_site_provider[project_name] = remote_site_provider
|
||||
|
|
|
|||
|
|
@ -46,8 +46,13 @@ class SwitchAssetDialog(QtWidgets.QDialog):
|
|||
|
||||
switched = QtCore.Signal()
|
||||
|
||||
def __init__(self, controller, parent=None, items=None):
|
||||
super(SwitchAssetDialog, self).__init__(parent)
|
||||
def __init__(self, controller, project_name, items, parent=None):
|
||||
super().__init__(parent)
|
||||
|
||||
current_project_name = controller.get_current_project_name()
|
||||
folder_id = None
|
||||
if current_project_name == project_name:
|
||||
folder_id = controller.get_current_folder_id()
|
||||
|
||||
self.setWindowTitle("Switch selected items ...")
|
||||
|
||||
|
|
@ -147,11 +152,10 @@ class SwitchAssetDialog(QtWidgets.QDialog):
|
|||
self._init_repre_name = None
|
||||
|
||||
self._fill_check = False
|
||||
self._project_name = project_name
|
||||
self._folder_id = folder_id
|
||||
|
||||
self._project_name = controller.get_current_project_name()
|
||||
self._folder_id = controller.get_current_folder_id()
|
||||
|
||||
self._current_folder_btn.setEnabled(self._folder_id is not None)
|
||||
self._current_folder_btn.setEnabled(folder_id is not None)
|
||||
|
||||
self._controller = controller
|
||||
|
||||
|
|
@ -159,7 +163,7 @@ class SwitchAssetDialog(QtWidgets.QDialog):
|
|||
self._prepare_content_data()
|
||||
|
||||
def showEvent(self, event):
|
||||
super(SwitchAssetDialog, self).showEvent(event)
|
||||
super().showEvent(event)
|
||||
self._show_timer.start()
|
||||
|
||||
def refresh(self, init_refresh=False):
|
||||
|
|
|
|||
|
|
@ -192,29 +192,46 @@ class SceneInventoryView(QtWidgets.QTreeView):
|
|||
container_item = container_items_by_id[item_id]
|
||||
active_repre_id = container_item.representation_id
|
||||
break
|
||||
repre_ids_by_project = collections.defaultdict(set)
|
||||
for container_item in container_items_by_id.values():
|
||||
repre_id = container_item.representation_id
|
||||
project_name = container_item.project_name
|
||||
repre_ids_by_project[project_name].add(repre_id)
|
||||
|
||||
repre_info_by_id = self._controller.get_representation_info_items({
|
||||
container_item.representation_id
|
||||
for container_item in container_items_by_id.values()
|
||||
})
|
||||
valid_repre_ids = {
|
||||
repre_id
|
||||
for repre_id, repre_info in repre_info_by_id.items()
|
||||
if repre_info.is_valid
|
||||
}
|
||||
repre_info_by_project = {}
|
||||
repre_ids_by_project_name = {}
|
||||
version_ids_by_project = {}
|
||||
product_ids_by_project = {}
|
||||
for project_name, repre_ids in repre_ids_by_project.items():
|
||||
repres_info = self._controller.get_representation_info_items(
|
||||
project_name, repre_ids
|
||||
)
|
||||
|
||||
repre_info_by_project[project_name] = repres_info
|
||||
repre_ids = set()
|
||||
version_ids = set()
|
||||
product_ids = set()
|
||||
for repre_id, repre_info in repres_info.items():
|
||||
if not repre_info.is_valid:
|
||||
continue
|
||||
repre_ids.add(repre_id)
|
||||
version_ids.add(repre_info.version_id)
|
||||
product_ids.add(repre_info.product_id)
|
||||
|
||||
repre_ids_by_project_name[project_name] = repre_ids
|
||||
version_ids_by_project[project_name] = version_ids
|
||||
product_ids_by_project[project_name] = product_ids
|
||||
|
||||
# Exclude items that are "NOT FOUND" since setting versions, updating
|
||||
# and removal won't work for those items.
|
||||
filtered_items = []
|
||||
product_ids = set()
|
||||
version_ids = set()
|
||||
for container_item in container_items_by_id.values():
|
||||
project_name = container_item.project_name
|
||||
repre_id = container_item.representation_id
|
||||
repre_info_by_id = repre_info_by_project.get(project_name, {})
|
||||
repre_info = repre_info_by_id.get(repre_id)
|
||||
if repre_info and repre_info.is_valid:
|
||||
filtered_items.append(container_item)
|
||||
version_ids.add(repre_info.version_id)
|
||||
product_ids.add(repre_info.product_id)
|
||||
|
||||
# remove
|
||||
remove_icon = qtawesome.icon("fa.remove", color=DEFAULT_COLOR)
|
||||
|
|
@ -227,43 +244,51 @@ class SceneInventoryView(QtWidgets.QTreeView):
|
|||
menu.addAction(remove_action)
|
||||
return
|
||||
|
||||
version_items_by_product_id = self._controller.get_version_items(
|
||||
product_ids
|
||||
)
|
||||
version_items_by_project = {
|
||||
project_name: self._controller.get_version_items(
|
||||
project_name, product_ids
|
||||
)
|
||||
for project_name, product_ids in product_ids_by_project.items()
|
||||
}
|
||||
|
||||
has_outdated = False
|
||||
has_loaded_hero_versions = False
|
||||
has_available_hero_version = False
|
||||
has_outdated_approved = False
|
||||
last_version_by_product_id = {}
|
||||
for product_id, version_items_by_id in (
|
||||
version_items_by_product_id.items()
|
||||
for project_name, version_items_by_product_id in (
|
||||
version_items_by_project.items()
|
||||
):
|
||||
_has_outdated_approved = False
|
||||
_last_approved_version_item = None
|
||||
for version_item in version_items_by_id.values():
|
||||
if version_item.is_hero:
|
||||
has_available_hero_version = True
|
||||
|
||||
elif version_item.is_last_approved:
|
||||
_last_approved_version_item = version_item
|
||||
_has_outdated_approved = True
|
||||
|
||||
if version_item.version_id not in version_ids:
|
||||
continue
|
||||
|
||||
if version_item.is_hero:
|
||||
has_loaded_hero_versions = True
|
||||
elif not version_item.is_latest:
|
||||
has_outdated = True
|
||||
|
||||
if (
|
||||
_has_outdated_approved
|
||||
and _last_approved_version_item is not None
|
||||
version_ids = version_ids_by_project[project_name]
|
||||
for product_id, version_items_by_id in (
|
||||
version_items_by_product_id.items()
|
||||
):
|
||||
last_version_by_product_id[product_id] = (
|
||||
_last_approved_version_item
|
||||
)
|
||||
has_outdated_approved = True
|
||||
_has_outdated_approved = False
|
||||
_last_approved_version_item = None
|
||||
for version_item in version_items_by_id.values():
|
||||
if version_item.is_hero:
|
||||
has_available_hero_version = True
|
||||
|
||||
elif version_item.is_last_approved:
|
||||
_last_approved_version_item = version_item
|
||||
_has_outdated_approved = True
|
||||
|
||||
if version_item.version_id not in version_ids:
|
||||
continue
|
||||
|
||||
if version_item.is_hero:
|
||||
has_loaded_hero_versions = True
|
||||
elif not version_item.is_latest:
|
||||
has_outdated = True
|
||||
|
||||
if (
|
||||
_has_outdated_approved
|
||||
and _last_approved_version_item is not None
|
||||
):
|
||||
last_version_by_product_id[product_id] = (
|
||||
_last_approved_version_item
|
||||
)
|
||||
has_outdated_approved = True
|
||||
|
||||
switch_to_versioned = None
|
||||
if has_loaded_hero_versions:
|
||||
|
|
@ -284,8 +309,9 @@ class SceneInventoryView(QtWidgets.QTreeView):
|
|||
approved_version_by_item_id = {}
|
||||
if has_outdated_approved:
|
||||
for container_item in container_items_by_id.values():
|
||||
project_name = container_item.project_name
|
||||
repre_id = container_item.representation_id
|
||||
repre_info = repre_info_by_id.get(repre_id)
|
||||
repre_info = repre_info_by_project[project_name][repre_id]
|
||||
if not repre_info or not repre_info.is_valid:
|
||||
continue
|
||||
version_item = last_version_by_product_id.get(
|
||||
|
|
@ -397,14 +423,15 @@ class SceneInventoryView(QtWidgets.QTreeView):
|
|||
|
||||
menu.addAction(remove_action)
|
||||
|
||||
self._handle_sitesync(menu, valid_repre_ids)
|
||||
self._handle_sitesync(menu, repre_ids_by_project_name)
|
||||
|
||||
def _handle_sitesync(self, menu, repre_ids):
|
||||
def _handle_sitesync(self, menu, repre_ids_by_project_name):
|
||||
"""Adds actions for download/upload when SyncServer is enabled
|
||||
|
||||
Args:
|
||||
menu (OptionMenu)
|
||||
repre_ids (list) of object_ids
|
||||
repre_ids_by_project_name (Dict[str, Set[str]]): Representation
|
||||
ids by project name.
|
||||
|
||||
Returns:
|
||||
(OptionMenu)
|
||||
|
|
@ -413,7 +440,7 @@ class SceneInventoryView(QtWidgets.QTreeView):
|
|||
if not self._controller.is_sitesync_enabled():
|
||||
return
|
||||
|
||||
if not repre_ids:
|
||||
if not repre_ids_by_project_name:
|
||||
return
|
||||
|
||||
menu.addSeparator()
|
||||
|
|
@ -425,7 +452,10 @@ class SceneInventoryView(QtWidgets.QTreeView):
|
|||
menu
|
||||
)
|
||||
download_active_action.triggered.connect(
|
||||
lambda: self._add_sites(repre_ids, "active_site"))
|
||||
lambda: self._add_sites(
|
||||
repre_ids_by_project_name, "active_site"
|
||||
)
|
||||
)
|
||||
|
||||
upload_icon = qtawesome.icon("fa.upload", color=DEFAULT_COLOR)
|
||||
upload_remote_action = QtWidgets.QAction(
|
||||
|
|
@ -434,23 +464,30 @@ class SceneInventoryView(QtWidgets.QTreeView):
|
|||
menu
|
||||
)
|
||||
upload_remote_action.triggered.connect(
|
||||
lambda: self._add_sites(repre_ids, "remote_site"))
|
||||
lambda: self._add_sites(
|
||||
repre_ids_by_project_name, "remote_site"
|
||||
)
|
||||
)
|
||||
|
||||
menu.addAction(download_active_action)
|
||||
menu.addAction(upload_remote_action)
|
||||
|
||||
def _add_sites(self, repre_ids, site_type):
|
||||
def _add_sites(self, repre_ids_by_project_name, site_type):
|
||||
"""(Re)sync all 'repre_ids' to specific site.
|
||||
|
||||
It checks if opposite site has fully available content to limit
|
||||
accidents. (ReSync active when no remote >> losing active content)
|
||||
|
||||
Args:
|
||||
repre_ids (list)
|
||||
repre_ids_by_project_name (Dict[str, Set[str]]): Representation
|
||||
ids by project name.
|
||||
site_type (Literal[active_site, remote_site]): Site type.
|
||||
"""
|
||||
|
||||
self._controller.resync_representations(repre_ids, site_type)
|
||||
"""
|
||||
for project_name, repre_ids in repre_ids_by_project_name.items():
|
||||
self._controller.resync_representations(
|
||||
project_name, repre_ids, site_type
|
||||
)
|
||||
|
||||
self.data_changed.emit()
|
||||
|
||||
|
|
@ -735,42 +772,68 @@ class SceneInventoryView(QtWidgets.QTreeView):
|
|||
container_items_by_id = self._controller.get_container_items_by_id(
|
||||
item_ids
|
||||
)
|
||||
repre_ids = {
|
||||
container_item.representation_id
|
||||
for container_item in container_items_by_id.values()
|
||||
}
|
||||
repre_info_by_id = self._controller.get_representation_info_items(
|
||||
repre_ids
|
||||
)
|
||||
project_names = set()
|
||||
repre_ids_by_project = collections.defaultdict(set)
|
||||
for container_item in container_items_by_id.values():
|
||||
repre_id = container_item.representation_id
|
||||
project_name = container_item.project_name
|
||||
project_names.add(project_name)
|
||||
repre_ids_by_project[project_name].add(repre_id)
|
||||
|
||||
# active_project_name = None
|
||||
active_repre_info = None
|
||||
repre_info_by_project = {}
|
||||
version_items_by_project = {}
|
||||
for project_name, repre_ids in repre_ids_by_project.items():
|
||||
repres_info = self._controller.get_representation_info_items(
|
||||
project_name, repre_ids
|
||||
)
|
||||
if active_repre_info is None:
|
||||
# active_project_name = project_name
|
||||
active_repre_info = repres_info.get(active_repre_id)
|
||||
|
||||
product_ids = {
|
||||
repre_info.product_id
|
||||
for repre_info in repres_info.values()
|
||||
if repre_info.is_valid
|
||||
}
|
||||
version_items_by_product_id = self._controller.get_version_items(
|
||||
project_name, product_ids
|
||||
)
|
||||
|
||||
repre_info_by_project[project_name] = repres_info
|
||||
version_items_by_project[project_name] = (
|
||||
version_items_by_product_id
|
||||
)
|
||||
|
||||
product_ids = {
|
||||
repre_info.product_id
|
||||
for repre_info in repre_info_by_id.values()
|
||||
}
|
||||
active_repre_info = repre_info_by_id[active_repre_id]
|
||||
active_version_id = active_repre_info.version_id
|
||||
active_product_id = active_repre_info.product_id
|
||||
version_items_by_product_id = self._controller.get_version_items(
|
||||
product_ids
|
||||
)
|
||||
version_items = list(
|
||||
version_items_by_product_id[active_product_id].values()
|
||||
)
|
||||
versions = {version_item.version for version_item in version_items}
|
||||
product_ids_by_version = collections.defaultdict(set)
|
||||
for version_items_by_id in version_items_by_product_id.values():
|
||||
for version_item in version_items_by_id.values():
|
||||
version = version_item.version
|
||||
_prod_version = version
|
||||
if _prod_version < 0:
|
||||
_prod_version = -1
|
||||
product_ids_by_version[_prod_version].add(
|
||||
version_item.product_id
|
||||
)
|
||||
if version in versions:
|
||||
continue
|
||||
versions.add(version)
|
||||
version_items.append(version_item)
|
||||
# active_product_id = active_repre_info.product_id
|
||||
|
||||
versions = set()
|
||||
product_ids = set()
|
||||
version_items = []
|
||||
product_ids_by_version_by_project = {}
|
||||
for project_name, version_items_by_product_id in (
|
||||
version_items_by_project.items()
|
||||
):
|
||||
product_ids_by_version = collections.defaultdict(set)
|
||||
product_ids_by_version_by_project[project_name] = (
|
||||
product_ids_by_version
|
||||
)
|
||||
for version_items_by_id in version_items_by_product_id.values():
|
||||
for version_item in version_items_by_id.values():
|
||||
version = version_item.version
|
||||
_prod_version = version
|
||||
if _prod_version < 0:
|
||||
_prod_version = -1
|
||||
product_ids_by_version[_prod_version].add(
|
||||
version_item.product_id
|
||||
)
|
||||
product_ids.add(version_item.product_id)
|
||||
if version in versions:
|
||||
continue
|
||||
versions.add(version)
|
||||
version_items.append(version_item)
|
||||
|
||||
def version_sorter(item):
|
||||
hero_value = 0
|
||||
|
|
@ -831,12 +894,15 @@ class SceneInventoryView(QtWidgets.QTreeView):
|
|||
product_version = -1
|
||||
version = HeroVersionType(version)
|
||||
|
||||
product_ids = product_ids_by_version[product_version]
|
||||
|
||||
filtered_item_ids = set()
|
||||
for container_item in container_items_by_id.values():
|
||||
project_name = container_item.project_name
|
||||
product_ids_by_version = (
|
||||
product_ids_by_version_by_project[project_name]
|
||||
)
|
||||
product_ids = product_ids_by_version[product_version]
|
||||
repre_id = container_item.representation_id
|
||||
repre_info = repre_info_by_id[repre_id]
|
||||
repre_info = repre_info_by_project[project_name][repre_id]
|
||||
if repre_info.product_id in product_ids:
|
||||
filtered_item_ids.add(container_item.item_id)
|
||||
|
||||
|
|
@ -846,14 +912,28 @@ class SceneInventoryView(QtWidgets.QTreeView):
|
|||
|
||||
def _show_switch_dialog(self, item_ids):
|
||||
"""Display Switch dialog"""
|
||||
containers_by_id = self._controller.get_containers_by_item_ids(
|
||||
container_items_by_id = self._controller.get_container_items_by_id(
|
||||
item_ids
|
||||
)
|
||||
dialog = SwitchAssetDialog(
|
||||
self._controller, self, list(containers_by_id.values())
|
||||
)
|
||||
dialog.switched.connect(self.data_changed.emit)
|
||||
dialog.show()
|
||||
container_ids_by_project_name = collections.defaultdict(set)
|
||||
for container_id, container_item in container_items_by_id.items():
|
||||
project_name = container_item.project_name
|
||||
container_ids_by_project_name[project_name].add(container_id)
|
||||
|
||||
for project_name, container_ids in (
|
||||
container_ids_by_project_name.items()
|
||||
):
|
||||
containers_by_id = self._controller.get_containers_by_item_ids(
|
||||
container_ids
|
||||
)
|
||||
dialog = SwitchAssetDialog(
|
||||
self._controller,
|
||||
project_name,
|
||||
list(containers_by_id.values()),
|
||||
self
|
||||
)
|
||||
dialog.switched.connect(self.data_changed.emit)
|
||||
dialog.show()
|
||||
|
||||
def _show_remove_warning_dialog(self, item_ids):
|
||||
"""Prompt a dialog to inform the user the action will remove items"""
|
||||
|
|
@ -927,38 +1007,58 @@ class SceneInventoryView(QtWidgets.QTreeView):
|
|||
self._update_containers_to_version(item_ids, version=-1)
|
||||
|
||||
def _on_switch_to_versioned(self, item_ids):
|
||||
# Get container items by ID
|
||||
containers_items_by_id = self._controller.get_container_items_by_id(
|
||||
item_ids
|
||||
)
|
||||
repre_ids = {
|
||||
container_item.representation_id
|
||||
for container_item in containers_items_by_id.values()
|
||||
}
|
||||
repre_info_by_id = self._controller.get_representation_info_items(
|
||||
repre_ids
|
||||
)
|
||||
product_ids = {
|
||||
repre_info.product_id
|
||||
for repre_info in repre_info_by_id.values()
|
||||
if repre_info.is_valid
|
||||
}
|
||||
version_items_by_product_id = self._controller.get_version_items(
|
||||
product_ids
|
||||
)
|
||||
item_ids)
|
||||
# Extract project names and their corresponding representation IDs
|
||||
repre_ids_by_project = collections.defaultdict(set)
|
||||
for container_item in containers_items_by_id.values():
|
||||
project_name = container_item.project_name
|
||||
repre_id = container_item.representation_id
|
||||
repre_ids_by_project[project_name].add(repre_id)
|
||||
|
||||
# Get representation info items by ID
|
||||
repres_info_by_project = {}
|
||||
version_items_by_project = {}
|
||||
for project_name, repre_ids in repre_ids_by_project.items():
|
||||
repre_info_by_id = self._controller.get_representation_info_items(
|
||||
project_name, repre_ids
|
||||
)
|
||||
repres_info_by_project[project_name] = repre_info_by_id
|
||||
|
||||
product_ids = {
|
||||
repre_info.product_id
|
||||
for repre_info in repre_info_by_id.values()
|
||||
if repre_info.is_valid
|
||||
}
|
||||
version_items_by_product_id = self._controller.get_version_items(
|
||||
project_name, product_ids
|
||||
)
|
||||
version_items_by_project[project_name] = (
|
||||
version_items_by_product_id
|
||||
)
|
||||
|
||||
update_containers = []
|
||||
update_versions = []
|
||||
for item_id, container_item in containers_items_by_id.items():
|
||||
for container_item in containers_items_by_id.values():
|
||||
project_name = container_item.project_name
|
||||
repre_id = container_item.representation_id
|
||||
|
||||
repre_info_by_id = repres_info_by_project[project_name]
|
||||
repre_info = repre_info_by_id[repre_id]
|
||||
|
||||
version_items_by_product_id = (
|
||||
version_items_by_project[project_name]
|
||||
)
|
||||
product_id = repre_info.product_id
|
||||
version_items_id = version_items_by_product_id[product_id]
|
||||
version_item = version_items_id.get(repre_info.version_id, {})
|
||||
version_items_by_id = version_items_by_product_id[product_id]
|
||||
version_item = version_items_by_id.get(repre_info.version_id, {})
|
||||
if not version_item or not version_item.is_hero:
|
||||
continue
|
||||
|
||||
version = abs(version_item.version)
|
||||
version_found = False
|
||||
for version_item in version_items_id.values():
|
||||
for version_item in version_items_by_id.values():
|
||||
if version_item.is_hero:
|
||||
continue
|
||||
if version_item.version == version:
|
||||
|
|
@ -971,8 +1071,8 @@ class SceneInventoryView(QtWidgets.QTreeView):
|
|||
update_containers.append(container_item.item_id)
|
||||
update_versions.append(version)
|
||||
|
||||
# Specify version per item to update to
|
||||
self._update_containers(update_containers, update_versions)
|
||||
# Specify version per item to update to
|
||||
self._update_containers(update_containers, update_versions)
|
||||
|
||||
def _update_containers(self, item_ids, versions):
|
||||
"""Helper to update items to given version (or version per item)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue