ayon-core/openpype/tools/sceneinventory/switch_dialog.py
kaa 91a1fb1cdb
General: We should keep current subset version when we switch only the representation type (#4629)
* keep current subset version when switch repre

* added comment and safe switch repres

* more clearly variable names and revert last feat

* check selected but no change

* fix switch hero version
2024-01-04 15:59:26 +01:00

1349 lines
47 KiB
Python

import collections
import logging
from qtpy import QtWidgets, QtCore
import qtawesome
from openpype.client import (
get_asset_by_name,
get_assets,
get_subset_by_name,
get_subsets,
get_versions,
get_hero_versions,
get_last_versions,
get_representations,
)
from openpype.pipeline import legacy_io
from openpype.pipeline.load import (
discover_loader_plugins,
switch_container,
get_repres_contexts,
loaders_from_repre_context,
LoaderSwitchNotImplementedError,
IncompatibleLoaderError,
LoaderNotFoundError
)
from .widgets import (
ButtonWithMenu,
SearchComboBox
)
log = logging.getLogger("SwitchAssetDialog")
class ValidationState:
def __init__(self):
self.asset_ok = True
self.subset_ok = True
self.repre_ok = True
@property
def all_ok(self):
return (
self.asset_ok
and self.subset_ok
and self.repre_ok
)
class SwitchAssetDialog(QtWidgets.QDialog):
"""Widget to support asset switching"""
MIN_WIDTH = 550
switched = QtCore.Signal()
def __init__(self, parent=None, items=None):
super(SwitchAssetDialog, self).__init__(parent)
self.setWindowTitle("Switch selected items ...")
# Force and keep focus dialog
self.setModal(True)
assets_combox = SearchComboBox(self)
subsets_combox = SearchComboBox(self)
repres_combobox = SearchComboBox(self)
assets_combox.set_placeholder("<asset>")
subsets_combox.set_placeholder("<subset>")
repres_combobox.set_placeholder("<representation>")
asset_label = QtWidgets.QLabel(self)
subset_label = QtWidgets.QLabel(self)
repre_label = QtWidgets.QLabel(self)
current_asset_btn = QtWidgets.QPushButton("Use current asset")
accept_icon = qtawesome.icon("fa.check", color="white")
accept_btn = ButtonWithMenu(self)
accept_btn.setIcon(accept_icon)
main_layout = QtWidgets.QGridLayout(self)
# Asset column
main_layout.addWidget(current_asset_btn, 0, 0)
main_layout.addWidget(assets_combox, 1, 0)
main_layout.addWidget(asset_label, 2, 0)
# Subset column
main_layout.addWidget(subsets_combox, 1, 1)
main_layout.addWidget(subset_label, 2, 1)
# Representation column
main_layout.addWidget(repres_combobox, 1, 2)
main_layout.addWidget(repre_label, 2, 2)
# Btn column
main_layout.addWidget(accept_btn, 1, 3)
main_layout.setColumnStretch(0, 1)
main_layout.setColumnStretch(1, 1)
main_layout.setColumnStretch(2, 1)
main_layout.setColumnStretch(3, 0)
assets_combox.currentIndexChanged.connect(
self._combobox_value_changed
)
subsets_combox.currentIndexChanged.connect(
self._combobox_value_changed
)
repres_combobox.currentIndexChanged.connect(
self._combobox_value_changed
)
accept_btn.clicked.connect(self._on_accept)
current_asset_btn.clicked.connect(self._on_current_asset)
self._current_asset_btn = current_asset_btn
self._assets_box = assets_combox
self._subsets_box = subsets_combox
self._representations_box = repres_combobox
self._asset_label = asset_label
self._subset_label = subset_label
self._repre_label = repre_label
self._accept_btn = accept_btn
self.setMinimumWidth(self.MIN_WIDTH)
# Set default focus to accept button so you don't directly type in
# first asset field, this also allows to see the placeholder value.
accept_btn.setFocus()
self.content_loaders = set()
self.content_assets = {}
self.content_subsets = {}
self.content_versions = {}
self.content_repres = {}
self.hero_version_ids = set()
self.missing_assets = []
self.missing_versions = []
self.missing_subsets = []
self.missing_repres = []
self.missing_docs = False
self.archived_assets = []
self.archived_subsets = []
self.archived_repres = []
self._init_asset_name = None
self._init_subset_name = None
self._init_repre_name = None
self._fill_check = False
self._items = items
self._prepare_content_data()
self.refresh(True)
def active_project(self):
return legacy_io.active_project()
def _prepare_content_data(self):
repre_ids = set()
content_loaders = set()
for item in self._items:
repre_ids.add(str(item["representation"]))
content_loaders.add(item["loader"])
project_name = self.active_project()
repres = list(get_representations(
project_name,
representation_ids=repre_ids,
archived=True
))
repres_by_id = {str(repre["_id"]): repre for repre in repres}
# stash context values, works only for single representation
if len(repres) == 1:
self._init_asset_name = repres[0]["context"]["asset"]
self._init_subset_name = repres[0]["context"]["subset"]
self._init_repre_name = repres[0]["context"]["representation"]
content_repres = {}
archived_repres = []
missing_repres = []
version_ids = set()
for repre_id in repre_ids:
if repre_id not in repres_by_id:
missing_repres.append(repre_id)
elif repres_by_id[repre_id]["type"] == "archived_representation":
repre = repres_by_id[repre_id]
archived_repres.append(repre)
version_ids.add(repre["parent"])
else:
repre = repres_by_id[repre_id]
content_repres[repre_id] = repres_by_id[repre_id]
version_ids.add(repre["parent"])
versions = get_versions(
project_name,
version_ids=version_ids,
hero=True
)
content_versions = {}
hero_version_ids = set()
for version in versions:
content_versions[version["_id"]] = version
if version["type"] == "hero_version":
hero_version_ids.add(version["_id"])
missing_versions = []
subset_ids = []
for version_id in version_ids:
if version_id not in content_versions:
missing_versions.append(version_id)
else:
subset_ids.append(content_versions[version_id]["parent"])
subsets = get_subsets(
project_name, subset_ids=subset_ids, archived=True
)
subsets_by_id = {sub["_id"]: sub for sub in subsets}
asset_ids = []
archived_subsets = []
missing_subsets = []
content_subsets = {}
for subset_id in subset_ids:
if subset_id not in subsets_by_id:
missing_subsets.append(subset_id)
elif subsets_by_id[subset_id]["type"] == "archived_subset":
subset = subsets_by_id[subset_id]
asset_ids.append(subset["parent"])
archived_subsets.append(subset)
else:
subset = subsets_by_id[subset_id]
asset_ids.append(subset["parent"])
content_subsets[subset_id] = subset
assets = get_assets(project_name, asset_ids=asset_ids, archived=True)
assets_by_id = {asset["_id"]: asset for asset in assets}
missing_assets = []
archived_assets = []
content_assets = {}
for asset_id in asset_ids:
if asset_id not in assets_by_id:
missing_assets.append(asset_id)
elif assets_by_id[asset_id]["type"] == "archived_asset":
archived_assets.append(assets_by_id[asset_id])
else:
content_assets[asset_id] = assets_by_id[asset_id]
self.content_loaders = content_loaders
self.content_assets = content_assets
self.content_subsets = content_subsets
self.content_versions = content_versions
self.content_repres = content_repres
self.hero_version_ids = hero_version_ids
self.missing_assets = missing_assets
self.missing_versions = missing_versions
self.missing_subsets = missing_subsets
self.missing_repres = missing_repres
self.missing_docs = (
bool(missing_assets)
or bool(missing_versions)
or bool(missing_subsets)
or bool(missing_repres)
)
self.archived_assets = archived_assets
self.archived_subsets = archived_subsets
self.archived_repres = archived_repres
def _combobox_value_changed(self, *args, **kwargs):
self.refresh()
def refresh(self, init_refresh=False):
"""Build the need comboboxes with content"""
if not self._fill_check and not init_refresh:
return
self._fill_check = False
if init_refresh:
asset_values = self._get_asset_box_values()
self._fill_combobox(asset_values, "asset")
validation_state = ValidationState()
# Set other comboboxes to empty if any document is missing or any asset
# of loaded representations is archived.
self._is_asset_ok(validation_state)
if validation_state.asset_ok:
subset_values = self._get_subset_box_values()
self._fill_combobox(subset_values, "subset")
self._is_subset_ok(validation_state)
if validation_state.asset_ok and validation_state.subset_ok:
repre_values = sorted(self._representations_box_values())
self._fill_combobox(repre_values, "repre")
self._is_repre_ok(validation_state)
# Fill comboboxes with values
self.set_labels()
self.apply_validations(validation_state)
self._build_loaders_menu()
if init_refresh: # pre select context if possible
self._assets_box.set_valid_value(self._init_asset_name)
self._subsets_box.set_valid_value(self._init_subset_name)
self._representations_box.set_valid_value(self._init_repre_name)
self._fill_check = True
def _build_loaders_menu(self):
repre_ids = self._get_current_output_repre_ids()
loaders = self._get_loaders(repre_ids)
# Get and destroy the action group
self._accept_btn.clear_actions()
if not loaders:
return
# Build new action group
group = QtWidgets.QActionGroup(self._accept_btn)
for loader in loaders:
# Label
label = getattr(loader, "label", None)
if label is None:
label = loader.__name__
action = group.addAction(label)
# action = QtWidgets.QAction(label)
action.setData(loader)
# Support font-awesome icons using the `.icon` and `.color`
# attributes on plug-ins.
icon = getattr(loader, "icon", None)
if icon is not None:
try:
key = "fa.{0}".format(icon)
color = getattr(loader, "color", "white")
action.setIcon(qtawesome.icon(key, color=color))
except Exception as exc:
print("Unable to set icon for loader {}: {}".format(
loader, str(exc)
))
self._accept_btn.add_action(action)
group.triggered.connect(self._on_action_clicked)
def _on_action_clicked(self, action):
loader_plugin = action.data()
self._trigger_switch(loader_plugin)
def _get_loaders(self, repre_ids):
repre_contexts = None
if repre_ids:
repre_contexts = get_repres_contexts(repre_ids)
if not repre_contexts:
return list()
available_loaders = []
for loader_plugin in discover_loader_plugins():
# Skip loaders without switch method
if not hasattr(loader_plugin, "switch"):
continue
# Skip utility loaders
if (
hasattr(loader_plugin, "is_utility")
and loader_plugin.is_utility
):
continue
available_loaders.append(loader_plugin)
loaders = None
for repre_context in repre_contexts.values():
_loaders = set(loaders_from_repre_context(
available_loaders, repre_context
))
if loaders is None:
loaders = _loaders
else:
loaders = _loaders.intersection(loaders)
if not loaders:
break
if loaders is None:
loaders = []
else:
loaders = list(loaders)
return loaders
def _fill_combobox(self, values, combobox_type):
if combobox_type == "asset":
combobox_widget = self._assets_box
elif combobox_type == "subset":
combobox_widget = self._subsets_box
elif combobox_type == "repre":
combobox_widget = self._representations_box
else:
return
selected_value = combobox_widget.get_valid_value()
# Fill combobox
if values is not None:
combobox_widget.populate(list(sorted(values)))
if selected_value and selected_value in values:
index = None
for idx in range(combobox_widget.count()):
if selected_value == str(combobox_widget.itemText(idx)):
index = idx
break
if index is not None:
combobox_widget.setCurrentIndex(index)
def set_labels(self):
asset_label = self._assets_box.get_valid_value()
subset_label = self._subsets_box.get_valid_value()
repre_label = self._representations_box.get_valid_value()
default = "*No changes"
self._asset_label.setText(asset_label or default)
self._subset_label.setText(subset_label or default)
self._repre_label.setText(repre_label or default)
def apply_validations(self, validation_state):
error_msg = "*Please select"
error_sheet = "border: 1px solid red;"
asset_sheet = None
subset_sheet = None
repre_sheet = None
accept_state = ""
if validation_state.asset_ok is False:
asset_sheet = error_sheet
self._asset_label.setText(error_msg)
elif validation_state.subset_ok is False:
subset_sheet = error_sheet
self._subset_label.setText(error_msg)
elif validation_state.repre_ok is False:
repre_sheet = error_sheet
self._repre_label.setText(error_msg)
if validation_state.all_ok:
accept_state = "1"
self._assets_box.setStyleSheet(asset_sheet or "")
self._subsets_box.setStyleSheet(subset_sheet or "")
self._representations_box.setStyleSheet(repre_sheet or "")
self._accept_btn.setEnabled(validation_state.all_ok)
self._set_style_property(self._accept_btn, "state", accept_state)
def _set_style_property(self, widget, name, value):
cur_value = widget.property(name)
if cur_value == value:
return
widget.setProperty(name, value)
widget.style().polish(widget)
def _get_current_output_repre_ids(self):
# NOTE hero versions are not used because it is expected that
# hero version has same representations as latests
selected_asset = self._assets_box.currentText()
selected_subset = self._subsets_box.currentText()
selected_repre = self._representations_box.currentText()
# Nothing is selected
# [ ] [ ] [ ]
if not selected_asset and not selected_subset and not selected_repre:
return list(self.content_repres.keys())
# Prepare asset document if asset is selected
asset_doc = None
if selected_asset:
asset_doc = get_asset_by_name(
self.active_project(),
selected_asset,
fields=["_id"]
)
if not asset_doc:
return []
# Everything is selected
# [x] [x] [x]
if selected_asset and selected_subset and selected_repre:
return self._get_current_output_repre_ids_xxx(
asset_doc, selected_subset, selected_repre
)
# [x] [x] [ ]
# If asset and subset is selected
if selected_asset and selected_subset:
return self._get_current_output_repre_ids_xxo(
asset_doc, selected_subset
)
# [x] [ ] [x]
# If asset and repre is selected
if selected_asset and selected_repre:
return self._get_current_output_repre_ids_xox(
asset_doc, selected_repre
)
# [x] [ ] [ ]
# If asset and subset is selected
if selected_asset:
return self._get_current_output_repre_ids_xoo(asset_doc)
# [ ] [x] [x]
if selected_subset and selected_repre:
return self._get_current_output_repre_ids_oxx(
selected_subset, selected_repre
)
# [ ] [x] [ ]
if selected_subset:
return self._get_current_output_repre_ids_oxo(
selected_subset
)
# [ ] [ ] [x]
return self._get_current_output_repre_ids_oox(selected_repre)
def _get_current_output_repre_ids_xxx(
self, asset_doc, selected_subset, selected_repre
):
project_name = self.active_project()
subset_doc = get_subset_by_name(
project_name,
selected_subset,
asset_doc["_id"],
fields=["_id"]
)
subset_id = subset_doc["_id"]
last_versions_by_subset_id = self.find_last_versions([subset_id])
version_doc = last_versions_by_subset_id.get(subset_id)
if not version_doc:
return []
repre_docs = get_representations(
project_name,
version_ids=[version_doc["_id"]],
representation_names=[selected_repre],
fields=["_id"]
)
return [repre_doc["_id"] for repre_doc in repre_docs]
def _get_current_output_repre_ids_xxo(self, asset_doc, selected_subset):
project_name = self.active_project()
subset_doc = get_subset_by_name(
project_name,
selected_subset,
asset_doc["_id"],
fields=["_id"]
)
if not subset_doc:
return []
repre_names = set()
for repre_doc in self.content_repres.values():
repre_names.add(repre_doc["name"])
# TODO where to take version ids?
version_ids = []
repre_docs = get_representations(
project_name,
representation_names=repre_names,
version_ids=version_ids,
fields=["_id"]
)
return [repre_doc["_id"] for repre_doc in repre_docs]
def _get_current_output_repre_ids_xox(self, asset_doc, selected_repre):
subset_names = set()
for subset_doc in self.content_subsets.values():
subset_names.add(subset_doc["name"])
project_name = self.active_project()
subset_docs = get_subsets(
project_name,
asset_ids=[asset_doc["_id"]],
subset_names=subset_names,
fields=["_id", "name"]
)
subset_name_by_id = {
subset_doc["_id"]: subset_doc["name"]
for subset_doc in subset_docs
}
subset_ids = list(subset_name_by_id.keys())
last_versions_by_subset_id = self.find_last_versions(subset_ids)
last_version_id_by_subset_name = {}
for subset_id, last_version in last_versions_by_subset_id.items():
subset_name = subset_name_by_id[subset_id]
last_version_id_by_subset_name[subset_name] = (
last_version["_id"]
)
repre_docs = get_representations(
project_name,
version_ids=last_version_id_by_subset_name.values(),
representation_names=[selected_repre],
fields=["_id"]
)
return [repre_doc["_id"] for repre_doc in repre_docs]
def _get_current_output_repre_ids_xoo(self, asset_doc):
project_name = self.active_project()
repres_by_subset_name = collections.defaultdict(set)
for repre_doc in self.content_repres.values():
repre_name = repre_doc["name"]
version_doc = self.content_versions[repre_doc["parent"]]
subset_doc = self.content_subsets[version_doc["parent"]]
subset_name = subset_doc["name"]
repres_by_subset_name[subset_name].add(repre_name)
subset_docs = list(get_subsets(
project_name,
asset_ids=[asset_doc["_id"]],
subset_names=repres_by_subset_name.keys(),
fields=["_id", "name"]
))
subset_name_by_id = {
subset_doc["_id"]: subset_doc["name"]
for subset_doc in subset_docs
}
subset_ids = list(subset_name_by_id.keys())
last_versions_by_subset_id = self.find_last_versions(subset_ids)
last_version_id_by_subset_name = {}
for subset_id, last_version in last_versions_by_subset_id.items():
subset_name = subset_name_by_id[subset_id]
last_version_id_by_subset_name[subset_name] = (
last_version["_id"]
)
repre_names_by_version_id = {}
for subset_name, repre_names in repres_by_subset_name.items():
version_id = last_version_id_by_subset_name.get(subset_name)
# This should not happen but why to crash?
if version_id is not None:
repre_names_by_version_id[version_id] = list(repre_names)
repre_docs = get_representations(
project_name,
names_by_version_ids=repre_names_by_version_id,
fields=["_id"]
)
return [repre_doc["_id"] for repre_doc in repre_docs]
def _get_current_output_repre_ids_oxx(
self, selected_subset, selected_repre
):
project_name = self.active_project()
subset_docs = get_subsets(
project_name,
asset_ids=self.content_assets.keys(),
subset_names=[selected_subset],
fields=["_id"]
)
subset_ids = [subset_doc["_id"] for subset_doc in subset_docs]
last_versions_by_subset_id = self.find_last_versions(subset_ids)
last_version_ids = [
last_version["_id"]
for last_version in last_versions_by_subset_id.values()
]
repre_docs = get_representations(
project_name,
version_ids=last_version_ids,
representation_names=[selected_repre],
fields=["_id"]
)
return [repre_doc["_id"] for repre_doc in repre_docs]
def _get_current_output_repre_ids_oxo(self, selected_subset):
project_name = self.active_project()
subset_docs = get_subsets(
project_name,
asset_ids=self.content_assets.keys(),
subset_names=[selected_subset],
fields=["_id", "parent"]
)
subset_docs_by_id = {
subset_doc["_id"]: subset_doc
for subset_doc in subset_docs
}
if not subset_docs:
return list()
last_versions_by_subset_id = self.find_last_versions(
subset_docs_by_id.keys()
)
subset_id_by_version_id = {}
for subset_id, last_version in last_versions_by_subset_id.items():
version_id = last_version["_id"]
subset_id_by_version_id[version_id] = subset_id
if not subset_id_by_version_id:
return list()
repre_names_by_asset_id = collections.defaultdict(set)
for repre_doc in self.content_repres.values():
version_doc = self.content_versions[repre_doc["parent"]]
subset_doc = self.content_subsets[version_doc["parent"]]
asset_doc = self.content_assets[subset_doc["parent"]]
repre_name = repre_doc["name"]
asset_id = asset_doc["_id"]
repre_names_by_asset_id[asset_id].add(repre_name)
repre_names_by_version_id = {}
for last_version_id, subset_id in subset_id_by_version_id.items():
subset_doc = subset_docs_by_id[subset_id]
asset_id = subset_doc["parent"]
repre_names = repre_names_by_asset_id.get(asset_id)
if not repre_names:
continue
repre_names_by_version_id[last_version_id] = repre_names
repre_docs = get_representations(
project_name,
names_by_version_ids=repre_names_by_version_id,
fields=["_id"]
)
return [repre_doc["_id"] for repre_doc in repre_docs]
def _get_current_output_repre_ids_oox(self, selected_repre):
project_name = self.active_project()
repre_docs = get_representations(
project_name,
representation_names=[selected_repre],
version_ids=self.content_versions.keys(),
fields=["_id"]
)
return [repre_doc["_id"] for repre_doc in repre_docs]
def _get_asset_box_values(self):
project_name = self.active_project()
asset_docs = get_assets(project_name, fields=["_id", "name"])
asset_names_by_id = {
asset_doc["_id"]: asset_doc["name"]
for asset_doc in asset_docs
}
subsets = get_subsets(
project_name,
asset_ids=asset_names_by_id.keys(),
fields=["parent"]
)
filtered_assets = []
for subset in subsets:
asset_name = asset_names_by_id[subset["parent"]]
if asset_name not in filtered_assets:
filtered_assets.append(asset_name)
return sorted(filtered_assets)
def _get_subset_box_values(self):
project_name = self.active_project()
selected_asset = self._assets_box.get_valid_value()
if selected_asset:
asset_doc = get_asset_by_name(
project_name, selected_asset, fields=["_id"]
)
asset_ids = [asset_doc["_id"]]
else:
asset_ids = list(self.content_assets.keys())
subsets = get_subsets(
project_name,
asset_ids=asset_ids,
fields=["parent", "name"]
)
subset_names_by_parent_id = collections.defaultdict(set)
for subset in subsets:
subset_names_by_parent_id[subset["parent"]].add(subset["name"])
possible_subsets = None
for subset_names in subset_names_by_parent_id.values():
if possible_subsets is None:
possible_subsets = subset_names
else:
possible_subsets = (possible_subsets & subset_names)
if not possible_subsets:
break
return list(possible_subsets or list())
def _representations_box_values(self):
# NOTE hero versions are not used because it is expected that
# hero version has same representations as latests
project_name = self.active_project()
selected_asset = self._assets_box.currentText()
selected_subset = self._subsets_box.currentText()
# If nothing is selected
# [ ] [ ] [?]
if not selected_asset and not selected_subset:
# Find all representations of selection's subsets
possible_repres = get_representations(
project_name,
version_ids=self.content_versions.keys(),
fields=["parent", "name"]
)
possible_repres_by_parent = collections.defaultdict(set)
for repre in possible_repres:
possible_repres_by_parent[repre["parent"]].add(repre["name"])
output_repres = None
for repre_names in possible_repres_by_parent.values():
if output_repres is None:
output_repres = repre_names
else:
output_repres = (output_repres & repre_names)
if not output_repres:
break
return list(output_repres or list())
# [x] [x] [?]
if selected_asset and selected_subset:
asset_doc = get_asset_by_name(
project_name, selected_asset, fields=["_id"]
)
subset_doc = get_subset_by_name(
project_name,
selected_subset,
asset_doc["_id"],
fields=["_id"]
)
subset_id = subset_doc["_id"]
last_versions_by_subset_id = self.find_last_versions([subset_id])
version_doc = last_versions_by_subset_id.get(subset_id)
repre_docs = get_representations(
project_name,
version_ids=[version_doc["_id"]],
fields=["name"]
)
return [
repre_doc["name"]
for repre_doc in repre_docs
]
# [x] [ ] [?]
# If asset only is selected
if selected_asset:
asset_doc = get_asset_by_name(
project_name, selected_asset, fields=["_id"]
)
if not asset_doc:
return list()
# Filter subsets by subset names from content
subset_names = set()
for subset_doc in self.content_subsets.values():
subset_names.add(subset_doc["name"])
subset_docs = get_subsets(
project_name,
asset_ids=[asset_doc["_id"]],
subset_names=subset_names,
fields=["_id"]
)
subset_ids = [
subset_doc["_id"]
for subset_doc in subset_docs
]
if not subset_ids:
return list()
last_versions_by_subset_id = self.find_last_versions(subset_ids)
subset_id_by_version_id = {}
for subset_id, last_version in last_versions_by_subset_id.items():
version_id = last_version["_id"]
subset_id_by_version_id[version_id] = subset_id
if not subset_id_by_version_id:
return list()
repre_docs = list(get_representations(
project_name,
version_ids=subset_id_by_version_id.keys(),
fields=["name", "parent"]
))
if not repre_docs:
return list()
repre_names_by_parent = collections.defaultdict(set)
for repre_doc in repre_docs:
repre_names_by_parent[repre_doc["parent"]].add(
repre_doc["name"]
)
available_repres = None
for repre_names in repre_names_by_parent.values():
if available_repres is None:
available_repres = repre_names
continue
available_repres = available_repres.intersection(repre_names)
return list(available_repres)
# [ ] [x] [?]
subset_docs = list(get_subsets(
project_name,
asset_ids=self.content_assets.keys(),
subset_names=[selected_subset],
fields=["_id", "parent"]
))
if not subset_docs:
return list()
subset_docs_by_id = {
subset_doc["_id"]: subset_doc
for subset_doc in subset_docs
}
last_versions_by_subset_id = self.find_last_versions(
subset_docs_by_id.keys()
)
subset_id_by_version_id = {}
for subset_id, last_version in last_versions_by_subset_id.items():
version_id = last_version["_id"]
subset_id_by_version_id[version_id] = subset_id
if not subset_id_by_version_id:
return list()
repre_docs = list(
get_representations(
project_name,
version_ids=subset_id_by_version_id.keys(),
fields=["name", "parent"]
)
)
if not repre_docs:
return list()
repre_names_by_asset_id = {}
for repre_doc in repre_docs:
subset_id = subset_id_by_version_id[repre_doc["parent"]]
asset_id = subset_docs_by_id[subset_id]["parent"]
if asset_id not in repre_names_by_asset_id:
repre_names_by_asset_id[asset_id] = set()
repre_names_by_asset_id[asset_id].add(repre_doc["name"])
available_repres = None
for repre_names in repre_names_by_asset_id.values():
if available_repres is None:
available_repres = repre_names
continue
available_repres = available_repres.intersection(repre_names)
return list(available_repres)
def _is_asset_ok(self, validation_state):
selected_asset = self._assets_box.get_valid_value()
if (
selected_asset is None
and (self.missing_docs or self.archived_assets)
):
validation_state.asset_ok = False
def _is_subset_ok(self, validation_state):
selected_asset = self._assets_box.get_valid_value()
selected_subset = self._subsets_box.get_valid_value()
# [?] [x] [?]
# If subset is selected then must be ok
if selected_subset is not None:
return
# [ ] [ ] [?]
if selected_asset is None:
# If there were archived subsets and asset is not selected
if self.archived_subsets:
validation_state.subset_ok = False
return
# [x] [ ] [?]
project_name = self.active_project()
asset_doc = get_asset_by_name(
project_name, selected_asset, fields=["_id"]
)
subset_docs = get_subsets(
project_name, asset_ids=[asset_doc["_id"]], fields=["name"]
)
subset_names = set(
subset_doc["name"]
for subset_doc in subset_docs
)
for subset_doc in self.content_subsets.values():
if subset_doc["name"] not in subset_names:
validation_state.subset_ok = False
break
def find_last_versions(self, subset_ids):
project_name = self.active_project()
return get_last_versions(
project_name,
subset_ids=subset_ids,
fields=["_id", "parent", "type"]
)
def _is_repre_ok(self, validation_state):
selected_asset = self._assets_box.get_valid_value()
selected_subset = self._subsets_box.get_valid_value()
selected_repre = self._representations_box.get_valid_value()
# [?] [?] [x]
# If subset is selected then must be ok
if selected_repre is not None:
return
# [ ] [ ] [ ]
if selected_asset is None and selected_subset is None:
if (
self.archived_repres
or self.missing_versions
or self.missing_repres
):
validation_state.repre_ok = False
return
# [x] [x] [ ]
project_name = self.active_project()
if selected_asset is not None and selected_subset is not None:
asset_doc = get_asset_by_name(
project_name, selected_asset, fields=["_id"]
)
subset_doc = get_subset_by_name(
project_name,
selected_subset,
asset_doc["_id"],
fields=["_id"]
)
subset_id = subset_doc["_id"]
last_versions_by_subset_id = self.find_last_versions([subset_id])
last_version = last_versions_by_subset_id.get(subset_id)
if not last_version:
validation_state.repre_ok = False
return
repre_docs = get_representations(
project_name,
version_ids=[last_version["_id"]],
fields=["name"]
)
repre_names = set(
repre_doc["name"]
for repre_doc in repre_docs
)
for repre_doc in self.content_repres.values():
if repre_doc["name"] not in repre_names:
validation_state.repre_ok = False
break
return
# [x] [ ] [ ]
if selected_asset is not None:
asset_doc = get_asset_by_name(
project_name, selected_asset, fields=["_id"]
)
subset_docs = list(get_subsets(
project_name,
asset_ids=[asset_doc["_id"]],
fields=["_id", "name"]
))
subset_name_by_id = {}
subset_ids = set()
for subset_doc in subset_docs:
subset_id = subset_doc["_id"]
subset_ids.add(subset_id)
subset_name_by_id[subset_id] = subset_doc["name"]
last_versions_by_subset_id = self.find_last_versions(subset_ids)
subset_id_by_version_id = {}
for subset_id, last_version in last_versions_by_subset_id.items():
version_id = last_version["_id"]
subset_id_by_version_id[version_id] = subset_id
repre_docs = get_representations(
project_name,
version_ids=subset_id_by_version_id.keys(),
fields=["name", "parent"]
)
repres_by_subset_name = {}
for repre_doc in repre_docs:
subset_id = subset_id_by_version_id[repre_doc["parent"]]
subset_name = subset_name_by_id[subset_id]
if subset_name not in repres_by_subset_name:
repres_by_subset_name[subset_name] = set()
repres_by_subset_name[subset_name].add(repre_doc["name"])
for repre_doc in self.content_repres.values():
version_doc = self.content_versions[repre_doc["parent"]]
subset_doc = self.content_subsets[version_doc["parent"]]
repre_names = (
repres_by_subset_name.get(subset_doc["name"]) or []
)
if repre_doc["name"] not in repre_names:
validation_state.repre_ok = False
break
return
# [ ] [x] [ ]
# Subset documents
subset_docs = get_subsets(
project_name,
asset_ids=self.content_assets.keys(),
subset_names=[selected_subset],
fields=["_id", "name", "parent"]
)
subset_docs_by_id = {}
for subset_doc in subset_docs:
subset_docs_by_id[subset_doc["_id"]] = subset_doc
last_versions_by_subset_id = self.find_last_versions(
subset_docs_by_id.keys()
)
subset_id_by_version_id = {}
for subset_id, last_version in last_versions_by_subset_id.items():
version_id = last_version["_id"]
subset_id_by_version_id[version_id] = subset_id
repre_docs = get_representations(
project_name,
version_ids=subset_id_by_version_id.keys(),
fields=["name", "parent"]
)
repres_by_asset_id = {}
for repre_doc in repre_docs:
subset_id = subset_id_by_version_id[repre_doc["parent"]]
asset_id = subset_docs_by_id[subset_id]["parent"]
if asset_id not in repres_by_asset_id:
repres_by_asset_id[asset_id] = set()
repres_by_asset_id[asset_id].add(repre_doc["name"])
for repre_doc in self.content_repres.values():
version_doc = self.content_versions[repre_doc["parent"]]
subset_doc = self.content_subsets[version_doc["parent"]]
asset_id = subset_doc["parent"]
repre_names = (
repres_by_asset_id.get(asset_id) or []
)
if repre_doc["name"] not in repre_names:
validation_state.repre_ok = False
break
def _on_current_asset(self):
# Set initial asset as current.
asset_name = legacy_io.Session["AVALON_ASSET"]
index = self._assets_box.findText(
asset_name, QtCore.Qt.MatchFixedString
)
if index >= 0:
print("Setting asset to {}".format(asset_name))
self._assets_box.setCurrentIndex(index)
def _on_accept(self):
self._trigger_switch()
def _trigger_switch(self, loader=None):
# Use None when not a valid value or when placeholder value
selected_asset = self._assets_box.get_valid_value()
selected_subset = self._subsets_box.get_valid_value()
selected_representation = self._representations_box.get_valid_value()
project_name = self.active_project()
if selected_asset:
asset_doc = get_asset_by_name(project_name, selected_asset)
asset_docs_by_id = {asset_doc["_id"]: asset_doc}
else:
asset_docs_by_id = self.content_assets
asset_docs_by_name = {
asset_doc["name"]: asset_doc
for asset_doc in asset_docs_by_id.values()
}
subset_names = None
if selected_subset:
subset_names = [selected_subset]
subset_docs = list(get_subsets(
project_name,
subset_names=subset_names,
asset_ids=asset_docs_by_id.keys()
))
subset_ids = []
subset_docs_by_parent_and_name = collections.defaultdict(dict)
for subset in subset_docs:
subset_ids.append(subset["_id"])
parent_id = subset["parent"]
name = subset["name"]
subset_docs_by_parent_and_name[parent_id][name] = subset
# versions
_version_docs = get_versions(project_name, subset_ids=subset_ids)
version_docs = list(reversed(
sorted(_version_docs, key=lambda item: item["name"])
))
hero_version_docs = list(get_hero_versions(
project_name, subset_ids=subset_ids
))
version_ids = list()
version_docs_by_parent_id_and_name = collections.defaultdict(dict)
for version_doc in version_docs:
parent_id = version_doc["parent"]
version_ids.append(version_doc["_id"])
name = version_doc["name"]
version_docs_by_parent_id_and_name[parent_id][name] = version_doc
hero_version_docs_by_parent_id = {}
for hero_version_doc in hero_version_docs:
version_ids.append(hero_version_doc["_id"])
parent_id = hero_version_doc["parent"]
hero_version_docs_by_parent_id[parent_id] = hero_version_doc
repre_docs = get_representations(project_name, version_ids=version_ids)
repre_docs_by_parent_id_by_name = collections.defaultdict(dict)
for repre_doc in repre_docs:
parent_id = repre_doc["parent"]
name = repre_doc["name"]
repre_docs_by_parent_id_by_name[parent_id][name] = repre_doc
for container in self._items:
container_repre_id = container["representation"]
container_repre = self.content_repres[container_repre_id]
container_repre_name = container_repre["name"]
container_version_id = container_repre["parent"]
container_version = self.content_versions[container_version_id]
container_subset_id = container_version["parent"]
container_subset = self.content_subsets[container_subset_id]
container_subset_name = container_subset["name"]
container_asset_id = container_subset["parent"]
container_asset = self.content_assets[container_asset_id]
container_asset_name = container_asset["name"]
if selected_asset:
asset_doc = asset_docs_by_name[selected_asset]
else:
asset_doc = asset_docs_by_name[container_asset_name]
subsets_by_name = subset_docs_by_parent_and_name[asset_doc["_id"]]
if selected_subset:
subset_doc = subsets_by_name[selected_subset]
else:
subset_doc = subsets_by_name[container_subset_name]
repre_doc = None
subset_id = subset_doc["_id"]
if container_version["type"] == "hero_version":
hero_version = hero_version_docs_by_parent_id.get(
subset_id
)
if hero_version:
_repres = repre_docs_by_parent_id_by_name.get(
hero_version["_id"]
)
if selected_representation:
repre_doc = _repres.get(selected_representation)
else:
repre_doc = _repres.get(container_repre_name)
if not repre_doc:
version_docs_by_name = version_docs_by_parent_id_and_name[
subset_id
]
# If asset or subset are selected for switching, we use latest
# version else we try to keep the current container version.
if (
selected_asset not in (None, container_asset_name)
or selected_subset not in (None, container_subset_name)
):
version_name = max(version_docs_by_name)
else:
version_name = container_version["name"]
version_doc = version_docs_by_name[version_name]
version_id = version_doc["_id"]
repres_docs_by_name = repre_docs_by_parent_id_by_name[
version_id
]
if selected_representation:
repres_name = selected_representation
else:
repres_name = container_repre_name
repre_doc = repres_docs_by_name[repres_name]
error = None
try:
switch_container(container, repre_doc, loader)
except (
LoaderSwitchNotImplementedError,
IncompatibleLoaderError,
LoaderNotFoundError,
) as exc:
error = str(exc)
except Exception:
error = (
"Switch asset failed. "
"Search console log for more details."
)
if error is not None:
log.warning((
"Couldn't switch asset."
"See traceback for more information."
), exc_info=True)
dialog = QtWidgets.QMessageBox(self)
dialog.setWindowTitle("Switch asset failed")
dialog.setText(error)
dialog.exec_()
self.switched.emit()
self.close()