Merge pull request #1005 from ynput/enhancement/Load-and-manage-products-from-a-library-project

Load and manage products from a library project
This commit is contained in:
Jakub Trllo 2024-12-05 14:18:51 +01:00 committed by GitHub
commit 38aa810542
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 526 additions and 352 deletions

View file

@ -465,7 +465,9 @@ def update_container(container, version=-1):
from ayon_core.pipeline import get_current_project_name from ayon_core.pipeline import get_current_project_name
# Compute the different version from 'representation' # 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"] repre_id = container["representation"]
if not _is_valid_representation_id(repre_id): if not _is_valid_representation_id(repre_id):
raise ValueError( 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) project_entity = ayon_api.get_project(project_name)
context = { context = {
"project": project_entity, "project": project_entity,
@ -553,6 +552,9 @@ def update_container(container, version=-1):
"version": new_version, "version": new_version,
"representation": new_representation, "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) return Loader().update(container, context)
@ -588,7 +590,9 @@ def switch_container(container, representation, loader_plugin=None):
) )
# Get the new representation to switch to # 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( context = get_representation_context(
project_name, representation["id"] project_name, representation["id"]

View file

@ -86,8 +86,9 @@ class SceneInventoryController:
self._current_folder_set = True self._current_folder_set = True
return self._current_folder_id return self._current_folder_id
def get_project_status_items(self): def get_project_status_items(self, project_name=None):
project_name = self.get_current_project_name() if project_name is None:
project_name = self.get_current_project_name()
return self._projects_model.get_project_status_items( return self._projects_model.get_project_status_items(
project_name, None project_name, None
) )
@ -105,32 +106,39 @@ class SceneInventoryController:
def get_container_items_by_id(self, item_ids): def get_container_items_by_id(self, item_ids):
return self._containers_model.get_container_items_by_id(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( return self._containers_model.get_representation_info_items(
representation_ids project_name, representation_ids
) )
def get_version_items(self, product_ids): def get_version_items(self, project_name, product_ids):
return self._containers_model.get_version_items(product_ids) return self._containers_model.get_version_items(
project_name, product_ids)
# Site Sync methods # Site Sync methods
def is_sitesync_enabled(self): def is_sitesync_enabled(self):
return self._sitesync_model.is_sitesync_enabled() return self._sitesync_model.is_sitesync_enabled()
def get_sites_information(self): def get_sites_information(self, project_name):
return self._sitesync_model.get_sites_information() return self._sitesync_model.get_sites_information(project_name)
def get_site_provider_icons(self): def get_site_provider_icons(self):
return self._sitesync_model.get_site_provider_icons() 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( 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( return self._sitesync_model.resync_representations(
representation_ids, site_type project_name,
representation_ids,
site_type
) )
# Switch dialog methods # Switch dialog methods

View file

@ -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 # This value hold unique value of container that should be used to identify
# containers inbetween refresh. # containers inbetween refresh.
ITEM_UNIQUE_NAME_ROLE = QtCore.Qt.UserRole + 24 ITEM_UNIQUE_NAME_ROLE = QtCore.Qt.UserRole + 24
PROJECT_NAME_ROLE = QtCore.Qt.UserRole + 25
class InventoryModel(QtGui.QStandardItemModel): class InventoryModel(QtGui.QStandardItemModel):
@ -52,6 +53,7 @@ class InventoryModel(QtGui.QStandardItemModel):
"Object name", "Object name",
"Active site", "Active site",
"Remote site", "Remote site",
"Project",
] ]
name_col = column_labels.index("Name") name_col = column_labels.index("Name")
version_col = column_labels.index("Version") version_col = column_labels.index("Version")
@ -63,6 +65,7 @@ class InventoryModel(QtGui.QStandardItemModel):
object_name_col = column_labels.index("Object name") object_name_col = column_labels.index("Object name")
active_site_col = column_labels.index("Active site") active_site_col = column_labels.index("Active site")
remote_site_col = column_labels.index("Remote site") remote_site_col = column_labels.index("Remote site")
project_col = column_labels.index("Project")
display_role_by_column = { display_role_by_column = {
name_col: QtCore.Qt.DisplayRole, name_col: QtCore.Qt.DisplayRole,
version_col: VERSION_LABEL_ROLE, version_col: VERSION_LABEL_ROLE,
@ -72,6 +75,7 @@ class InventoryModel(QtGui.QStandardItemModel):
product_group_col: PRODUCT_GROUP_NAME_ROLE, product_group_col: PRODUCT_GROUP_NAME_ROLE,
loader_col: LOADER_NAME_ROLE, loader_col: LOADER_NAME_ROLE,
object_name_col: OBJECT_NAME_ROLE, object_name_col: OBJECT_NAME_ROLE,
project_col: PROJECT_NAME_ROLE,
active_site_col: ACTIVE_SITE_PROGRESS_ROLE, active_site_col: ACTIVE_SITE_PROGRESS_ROLE,
remote_site_col: REMOTE_SITE_PROGRESS_ROLE, remote_site_col: REMOTE_SITE_PROGRESS_ROLE,
} }
@ -85,7 +89,7 @@ class InventoryModel(QtGui.QStandardItemModel):
foreground_role_by_column = { foreground_role_by_column = {
name_col: NAME_COLOR_ROLE, name_col: NAME_COLOR_ROLE,
version_col: VERSION_COLOR_ROLE, version_col: VERSION_COLOR_ROLE,
status_col: STATUS_COLOR_ROLE status_col: STATUS_COLOR_ROLE,
} }
width_by_column = { width_by_column = {
name_col: 250, name_col: 250,
@ -95,6 +99,7 @@ class InventoryModel(QtGui.QStandardItemModel):
product_type_col: 150, product_type_col: 150,
product_group_col: 120, product_group_col: 120,
loader_col: 150, loader_col: 150,
project_col: 150,
} }
OUTDATED_COLOR = QtGui.QColor(235, 30, 30) 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._default_icon_color = get_default_entity_icon_color()
self._last_project_statuses = {} self._last_project_statuses = collections.defaultdict(dict)
self._last_status_icons_by_name = {} self._last_status_icons_by_name = collections.defaultdict(dict)
def outdated(self, item): def outdated(self, item):
return item.get("isOutdated", True) return item.get("isOutdated", True)
@ -129,45 +134,73 @@ class InventoryModel(QtGui.QStandardItemModel):
self._clear_items() 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: for container_item in container_items:
# if ( # if (
# selected is not None # selected is not None
# and container_item.item_id not in selected # and container_item.item_id not in selected
# ): # ):
# continue # continue
repre_id = container_item.representation_id project_name = container_item.project_name
items = items_by_repre_id.setdefault(repre_id, []) representation_id = container_item.representation_id
items.append(container_item) 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 # SiteSync addon information
progress_by_id = self._controller.get_representations_site_progress( progress_by_project = {
repre_id project_name: self._controller.get_representations_site_progress(
) project_name, repre_ids
sites_info = self._controller.get_sites_information() )
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 = { site_icons = {
provider: get_qt_icon(icon_def) provider: get_qt_icon(icon_def)
for provider, icon_def in ( for provider, icon_def in (
self._controller.get_site_provider_icons().items() self._controller.get_site_provider_icons().items()
) )
} }
self._last_project_statuses = { last_project_statuses = collections.defaultdict(dict)
status_item.name: status_item for project_name in project_names:
for status_item in self._controller.get_project_status_items() status_items_by_name = {
} status_item.name: status_item
self._last_status_icons_by_name = {} 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( group_item_icon = qtawesome.icon(
"fa.folder", color=self._default_icon_color "fa.folder", color=self._default_icon_color
@ -187,118 +220,130 @@ class InventoryModel(QtGui.QStandardItemModel):
group_item_font = QtGui.QFont() group_item_font = QtGui.QFont()
group_item_font.setBold(True) 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() root_item = self.invisibleRootItem()
group_items = [] group_items = []
for repre_id, container_items in items_by_repre_id.items(): for project_name, items_by_repre_id in (
repre_info = repre_info_by_id[repre_id] item_by_repre_id_by_project.items()
version_color = None ):
if not repre_info.is_valid: sites_info = sites_info_by_project_name[project_name]
version_label = "N/A" active_site_icon = site_icons.get(
group_name = "< Entity N/A >" sites_info["active_site_provider"]
item_icon = invalid_item_icon )
is_latest = False remote_site_icon = site_icons.get(
is_hero = False sites_info["remote_site_provider"]
status_name = None )
else: progress_by_id = progress_by_project[project_name]
group_name = "{}_{}: ({})".format( repre_info_by_id = repre_info_by_id_by_project[project_name]
repre_info.folder_path.rsplit("/")[-1], version_items_by_product_id = (
repre_info.product_name, version_items_by_project[project_name]
repre_info.representation_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 = ( progress = progress_by_id[repre_id]
version_items_by_product_id[repre_info.product_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( group_item = QtGui.QStandardItem()
status_name 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( 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) if version_color is not None:
group_items.append(group_item) 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: if group_items:
root_item.appendRows(group_items) root_item.appendRows(group_items)
@ -359,17 +404,21 @@ class InventoryModel(QtGui.QStandardItemModel):
root_item = self.invisibleRootItem() root_item = self.invisibleRootItem()
root_item.removeRows(0, root_item.rowCount()) root_item.removeRows(0, root_item.rowCount())
def _get_status_data(self, status_name): def _get_status_data(self, project_name, status_name):
status_item = self._last_project_statuses.get(status_name) status_item = self._last_project_statuses[project_name].get(
status_icon = self._get_status_icon(status_name, status_item) status_name
)
status_icon = self._get_status_icon(
project_name, status_name, status_item
)
status_color = status_short = None status_color = status_short = None
if status_item is not None: if status_item is not None:
status_color = status_item.color status_color = status_item.color
status_short = status_item.short status_short = status_item.short
return status_color, status_short, status_icon return status_color, status_short, status_icon
def _get_status_icon(self, status_name, status_item): def _get_status_icon(self, project_name, status_name, status_item):
icon = self._last_status_icons_by_name.get(status_name) icon = self._last_status_icons_by_name[project_name].get(status_name)
if icon is not None: if icon is not None:
return icon return icon
@ -382,7 +431,7 @@ class InventoryModel(QtGui.QStandardItemModel):
}) })
if icon is None: if icon is None:
icon = QtGui.QIcon() 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 return icon

View file

@ -93,22 +93,27 @@ class ContainerItem:
loader_name, loader_name,
namespace, namespace,
object_name, object_name,
item_id item_id,
project_name
): ):
self.representation_id = representation_id self.representation_id = representation_id
self.loader_name = loader_name self.loader_name = loader_name
self.object_name = object_name self.object_name = object_name
self.namespace = namespace self.namespace = namespace
self.item_id = item_id self.item_id = item_id
self.project_name = project_name
@classmethod @classmethod
def from_container_data(cls, container): def from_container_data(cls, current_project_name, container):
return cls( return cls(
representation_id=container["representation"], representation_id=container["representation"],
loader_name=container["loader"], loader_name=container["loader"],
namespace=container["namespace"], namespace=container["namespace"],
object_name=container["objectName"], object_name=container["objectName"],
item_id=uuid.uuid4().hex, 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 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 = {} output = {}
missing_repre_ids = set() missing_repre_ids = set()
for repre_id in representation_ids: for repre_id in representation_ids:
@ -228,17 +233,14 @@ class ContainersModel:
except ValueError: except ValueError:
output[repre_id] = RepresentationInfo.new_invalid() output[repre_id] = RepresentationInfo.new_invalid()
continue continue
repre_info = self._repre_info_by_id.get(repre_id) repre_info = self._repre_info_by_id.get(repre_id)
if repre_info is None: if repre_info is None:
missing_repre_ids.add(repre_id) missing_repre_ids.add(repre_id)
else: else:
output[repre_id] = repre_info output[repre_id] = repre_info
if not missing_repre_ids: if not missing_repre_ids:
return output return output
project_name = self._controller.get_current_project_name()
repre_hierarchy_by_id = get_representations_hierarchy( repre_hierarchy_by_id = get_representations_hierarchy(
project_name, missing_repre_ids project_name, missing_repre_ids
) )
@ -276,10 +278,9 @@ class ContainersModel:
output[repre_id] = repre_info output[repre_id] = repre_info
return output return output
def get_version_items(self, product_ids): def get_version_items(self, project_name, product_ids):
if not product_ids: if not product_ids:
return {} return {}
missing_ids = { missing_ids = {
product_id product_id
for product_id in product_ids for product_id in product_ids
@ -294,7 +295,6 @@ class ContainersModel:
def version_sorted(entity): def version_sorted(entity):
return entity["version"] return entity["version"]
project_name = self._controller.get_current_project_name()
version_entities_by_product_id = { version_entities_by_product_id = {
product_id: [] product_id: []
for product_id in missing_ids for product_id in missing_ids
@ -359,9 +359,11 @@ class ContainersModel:
containers_by_id = {} containers_by_id = {}
container_items_by_id = {} container_items_by_id = {}
invalid_ids_mapping = {} invalid_ids_mapping = {}
current_project_name = self._controller.get_current_project_name()
for container in containers: for container in containers:
try: try:
item = ContainerItem.from_container_data(container) item = ContainerItem.from_container_data(
current_project_name, container)
repre_id = item.representation_id repre_id = item.representation_id
try: try:
uuid.UUID(repre_id) uuid.UUID(repre_id)

View file

@ -11,18 +11,18 @@ class SiteSyncModel:
self._sitesync_addon = NOT_SET self._sitesync_addon = NOT_SET
self._sitesync_enabled = None self._sitesync_enabled = None
self._active_site = NOT_SET self._active_site = {}
self._remote_site = NOT_SET self._remote_site = {}
self._active_site_provider = NOT_SET self._active_site_provider = {}
self._remote_site_provider = NOT_SET self._remote_site_provider = {}
def reset(self): def reset(self):
self._sitesync_addon = NOT_SET self._sitesync_addon = NOT_SET
self._sitesync_enabled = None self._sitesync_enabled = None
self._active_site = NOT_SET self._active_site = {}
self._remote_site = NOT_SET self._remote_site = {}
self._active_site_provider = NOT_SET self._active_site_provider = {}
self._remote_site_provider = NOT_SET self._remote_site_provider = {}
def is_sitesync_enabled(self): def is_sitesync_enabled(self):
"""Site sync is enabled. """Site sync is enabled.
@ -46,15 +46,21 @@ class SiteSyncModel:
sitesync_addon = self._get_sitesync_addon() sitesync_addon = self._get_sitesync_addon()
return sitesync_addon.get_site_icons() return sitesync_addon.get_site_icons()
def get_sites_information(self): def get_sites_information(self, project_name):
return { return {
"active_site": self._get_active_site(), "active_site": self._get_active_site(project_name),
"active_site_provider": self._get_active_site_provider(), "remote_site": self._get_remote_site(project_name),
"remote_site": self._get_remote_site(), "active_site_provider": self._get_active_site_provider(
"remote_site_provider": self._get_remote_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.""" """Get progress of representations sync."""
representation_ids = set(representation_ids) representation_ids = set(representation_ids)
@ -68,13 +74,12 @@ class SiteSyncModel:
if not self.is_sitesync_enabled(): if not self.is_sitesync_enabled():
return output return output
project_name = self._controller.get_current_project_name()
sitesync_addon = self._get_sitesync_addon() sitesync_addon = self._get_sitesync_addon()
repre_entities = ayon_api.get_representations( repre_entities = ayon_api.get_representations(
project_name, representation_ids project_name, representation_ids
) )
active_site = self._get_active_site() active_site = self._get_active_site(project_name)
remote_site = self._get_remote_site() remote_site = self._get_remote_site(project_name)
for repre_entity in repre_entities: for repre_entity in repre_entities:
repre_output = output[repre_entity["id"]] repre_output = output[repre_entity["id"]]
@ -86,20 +91,21 @@ class SiteSyncModel:
return output return output
def resync_representations(self, representation_ids, site_type): def resync_representations(
self, project_name, representation_ids, site_type
):
""" """
Args: Args:
project_name (str): Project name.
representation_ids (Iterable[str]): Representation ids. representation_ids (Iterable[str]): Representation ids.
site_type (Literal[active_site, remote_site]): Site type. site_type (Literal[active_site, remote_site]): Site type.
""" """
project_name = self._controller.get_current_project_name()
sitesync_addon = self._get_sitesync_addon() sitesync_addon = self._get_sitesync_addon()
active_site = self._get_active_site() active_site = self._get_active_site(project_name)
remote_site = self._get_remote_site() remote_site = self._get_remote_site(project_name)
progress = self.get_representations_site_progress( progress = self.get_representations_site_progress(
representation_ids project_name, representation_ids
) )
for repre_id in representation_ids: for repre_id in representation_ids:
repre_progress = progress.get(repre_id) repre_progress = progress.get(repre_id)
@ -132,48 +138,49 @@ class SiteSyncModel:
self._sitesync_addon = sitesync_addon self._sitesync_addon = sitesync_addon
self._sitesync_enabled = sync_enabled self._sitesync_enabled = sync_enabled
def _get_active_site(self): def _get_active_site(self, project_name):
if self._active_site is NOT_SET: if project_name not in self._active_site:
self._cache_sites() self._cache_sites(project_name)
return self._active_site return self._active_site[project_name]
def _get_remote_site(self): def _get_remote_site(self, project_name):
if self._remote_site is NOT_SET: if project_name not in self._remote_site:
self._cache_sites() self._cache_sites(project_name)
return self._remote_site return self._remote_site[project_name]
def _get_active_site_provider(self): def _get_active_site_provider(self, project_name):
if self._active_site_provider is NOT_SET: if project_name not in self._active_site_provider:
self._cache_sites() self._cache_sites(project_name)
return self._active_site_provider return self._active_site_provider[project_name]
def _get_remote_site_provider(self): def _get_remote_site_provider(self, project_name):
if self._remote_site_provider is NOT_SET: if project_name not in self._remote_site_provider:
self._cache_sites() self._cache_sites(project_name)
return self._remote_site_provider return self._remote_site_provider[project_name]
def _cache_sites(self): def _cache_sites(self, project_name):
active_site = None self._active_site[project_name] = None
remote_site = None self._remote_site[project_name] = None
active_site_provider = None self._active_site_provider[project_name] = None
remote_site_provider = None self._remote_site_provider[project_name] = None
if self.is_sitesync_enabled(): if not self.is_sitesync_enabled():
sitesync_addon = self._get_sitesync_addon() return
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
)
self._active_site = active_site sitesync_addon = self._get_sitesync_addon()
self._remote_site = remote_site active_site = sitesync_addon.get_active_site(project_name)
self._active_site_provider = active_site_provider remote_site = sitesync_addon.get_remote_site(project_name)
self._remote_site_provider = remote_site_provider 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

View file

@ -46,8 +46,13 @@ class SwitchAssetDialog(QtWidgets.QDialog):
switched = QtCore.Signal() switched = QtCore.Signal()
def __init__(self, controller, parent=None, items=None): def __init__(self, controller, project_name, items, parent=None):
super(SwitchAssetDialog, self).__init__(parent) 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 ...") self.setWindowTitle("Switch selected items ...")
@ -147,11 +152,10 @@ class SwitchAssetDialog(QtWidgets.QDialog):
self._init_repre_name = None self._init_repre_name = None
self._fill_check = False self._fill_check = False
self._project_name = project_name
self._folder_id = folder_id
self._project_name = controller.get_current_project_name() self._current_folder_btn.setEnabled(folder_id is not None)
self._folder_id = controller.get_current_folder_id()
self._current_folder_btn.setEnabled(self._folder_id is not None)
self._controller = controller self._controller = controller
@ -159,7 +163,7 @@ class SwitchAssetDialog(QtWidgets.QDialog):
self._prepare_content_data() self._prepare_content_data()
def showEvent(self, event): def showEvent(self, event):
super(SwitchAssetDialog, self).showEvent(event) super().showEvent(event)
self._show_timer.start() self._show_timer.start()
def refresh(self, init_refresh=False): def refresh(self, init_refresh=False):

View file

@ -192,29 +192,46 @@ class SceneInventoryView(QtWidgets.QTreeView):
container_item = container_items_by_id[item_id] container_item = container_items_by_id[item_id]
active_repre_id = container_item.representation_id active_repre_id = container_item.representation_id
break 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({ repre_info_by_project = {}
container_item.representation_id repre_ids_by_project_name = {}
for container_item in container_items_by_id.values() version_ids_by_project = {}
}) product_ids_by_project = {}
valid_repre_ids = { for project_name, repre_ids in repre_ids_by_project.items():
repre_id repres_info = self._controller.get_representation_info_items(
for repre_id, repre_info in repre_info_by_id.items() project_name, repre_ids
if repre_info.is_valid )
}
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 # Exclude items that are "NOT FOUND" since setting versions, updating
# and removal won't work for those items. # and removal won't work for those items.
filtered_items = [] filtered_items = []
product_ids = set()
version_ids = set()
for container_item in container_items_by_id.values(): for container_item in container_items_by_id.values():
project_name = container_item.project_name
repre_id = container_item.representation_id 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) repre_info = repre_info_by_id.get(repre_id)
if repre_info and repre_info.is_valid: if repre_info and repre_info.is_valid:
filtered_items.append(container_item) filtered_items.append(container_item)
version_ids.add(repre_info.version_id)
product_ids.add(repre_info.product_id)
# remove # remove
remove_icon = qtawesome.icon("fa.remove", color=DEFAULT_COLOR) remove_icon = qtawesome.icon("fa.remove", color=DEFAULT_COLOR)
@ -227,43 +244,51 @@ class SceneInventoryView(QtWidgets.QTreeView):
menu.addAction(remove_action) menu.addAction(remove_action)
return return
version_items_by_product_id = self._controller.get_version_items( version_items_by_project = {
product_ids 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_outdated = False
has_loaded_hero_versions = False has_loaded_hero_versions = False
has_available_hero_version = False has_available_hero_version = False
has_outdated_approved = False has_outdated_approved = False
last_version_by_product_id = {} last_version_by_product_id = {}
for product_id, version_items_by_id in ( for project_name, version_items_by_product_id in (
version_items_by_product_id.items() version_items_by_project.items()
): ):
_has_outdated_approved = False version_ids = version_ids_by_project[project_name]
_last_approved_version_item = None for product_id, version_items_by_id in (
for version_item in version_items_by_id.values(): version_items_by_product_id.items()
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] = ( _has_outdated_approved = False
_last_approved_version_item _last_approved_version_item = None
) for version_item in version_items_by_id.values():
has_outdated_approved = True 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 switch_to_versioned = None
if has_loaded_hero_versions: if has_loaded_hero_versions:
@ -284,8 +309,9 @@ class SceneInventoryView(QtWidgets.QTreeView):
approved_version_by_item_id = {} approved_version_by_item_id = {}
if has_outdated_approved: if has_outdated_approved:
for container_item in container_items_by_id.values(): for container_item in container_items_by_id.values():
project_name = container_item.project_name
repre_id = container_item.representation_id 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: if not repre_info or not repre_info.is_valid:
continue continue
version_item = last_version_by_product_id.get( version_item = last_version_by_product_id.get(
@ -397,14 +423,15 @@ class SceneInventoryView(QtWidgets.QTreeView):
menu.addAction(remove_action) 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 """Adds actions for download/upload when SyncServer is enabled
Args: Args:
menu (OptionMenu) menu (OptionMenu)
repre_ids (list) of object_ids repre_ids_by_project_name (Dict[str, Set[str]]): Representation
ids by project name.
Returns: Returns:
(OptionMenu) (OptionMenu)
@ -413,7 +440,7 @@ class SceneInventoryView(QtWidgets.QTreeView):
if not self._controller.is_sitesync_enabled(): if not self._controller.is_sitesync_enabled():
return return
if not repre_ids: if not repre_ids_by_project_name:
return return
menu.addSeparator() menu.addSeparator()
@ -425,7 +452,10 @@ class SceneInventoryView(QtWidgets.QTreeView):
menu menu
) )
download_active_action.triggered.connect( 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_icon = qtawesome.icon("fa.upload", color=DEFAULT_COLOR)
upload_remote_action = QtWidgets.QAction( upload_remote_action = QtWidgets.QAction(
@ -434,23 +464,30 @@ class SceneInventoryView(QtWidgets.QTreeView):
menu menu
) )
upload_remote_action.triggered.connect( 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(download_active_action)
menu.addAction(upload_remote_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. """(Re)sync all 'repre_ids' to specific site.
It checks if opposite site has fully available content to limit It checks if opposite site has fully available content to limit
accidents. (ReSync active when no remote >> losing active content) accidents. (ReSync active when no remote >> losing active content)
Args: 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. 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() self.data_changed.emit()
@ -735,42 +772,68 @@ class SceneInventoryView(QtWidgets.QTreeView):
container_items_by_id = self._controller.get_container_items_by_id( container_items_by_id = self._controller.get_container_items_by_id(
item_ids item_ids
) )
repre_ids = { project_names = set()
container_item.representation_id repre_ids_by_project = collections.defaultdict(set)
for container_item in container_items_by_id.values() for container_item in container_items_by_id.values():
} repre_id = container_item.representation_id
repre_info_by_id = self._controller.get_representation_info_items( project_name = container_item.project_name
repre_ids 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_version_id = active_repre_info.version_id
active_product_id = active_repre_info.product_id # active_product_id = active_repre_info.product_id
version_items_by_product_id = self._controller.get_version_items(
product_ids versions = set()
) product_ids = set()
version_items = list( version_items = []
version_items_by_product_id[active_product_id].values() product_ids_by_version_by_project = {}
) for project_name, version_items_by_product_id in (
versions = {version_item.version for version_item in version_items} version_items_by_project.items()
product_ids_by_version = collections.defaultdict(set) ):
for version_items_by_id in version_items_by_product_id.values(): product_ids_by_version = collections.defaultdict(set)
for version_item in version_items_by_id.values(): product_ids_by_version_by_project[project_name] = (
version = version_item.version product_ids_by_version
_prod_version = version )
if _prod_version < 0: for version_items_by_id in version_items_by_product_id.values():
_prod_version = -1 for version_item in version_items_by_id.values():
product_ids_by_version[_prod_version].add( version = version_item.version
version_item.product_id _prod_version = version
) if _prod_version < 0:
if version in versions: _prod_version = -1
continue product_ids_by_version[_prod_version].add(
versions.add(version) version_item.product_id
version_items.append(version_item) )
product_ids.add(version_item.product_id)
if version in versions:
continue
versions.add(version)
version_items.append(version_item)
def version_sorter(item): def version_sorter(item):
hero_value = 0 hero_value = 0
@ -831,12 +894,15 @@ class SceneInventoryView(QtWidgets.QTreeView):
product_version = -1 product_version = -1
version = HeroVersionType(version) version = HeroVersionType(version)
product_ids = product_ids_by_version[product_version]
filtered_item_ids = set() filtered_item_ids = set()
for container_item in container_items_by_id.values(): 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_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: if repre_info.product_id in product_ids:
filtered_item_ids.add(container_item.item_id) filtered_item_ids.add(container_item.item_id)
@ -846,14 +912,28 @@ class SceneInventoryView(QtWidgets.QTreeView):
def _show_switch_dialog(self, item_ids): def _show_switch_dialog(self, item_ids):
"""Display Switch dialog""" """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 item_ids
) )
dialog = SwitchAssetDialog( container_ids_by_project_name = collections.defaultdict(set)
self._controller, self, list(containers_by_id.values()) for container_id, container_item in container_items_by_id.items():
) project_name = container_item.project_name
dialog.switched.connect(self.data_changed.emit) container_ids_by_project_name[project_name].add(container_id)
dialog.show()
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): def _show_remove_warning_dialog(self, item_ids):
"""Prompt a dialog to inform the user the action will remove items""" """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) self._update_containers_to_version(item_ids, version=-1)
def _on_switch_to_versioned(self, item_ids): def _on_switch_to_versioned(self, item_ids):
# Get container items by ID
containers_items_by_id = self._controller.get_container_items_by_id( containers_items_by_id = self._controller.get_container_items_by_id(
item_ids item_ids)
) # Extract project names and their corresponding representation IDs
repre_ids = { repre_ids_by_project = collections.defaultdict(set)
container_item.representation_id for container_item in containers_items_by_id.values():
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 = self._controller.get_representation_info_items( repre_ids_by_project[project_name].add(repre_id)
repre_ids
) # Get representation info items by ID
product_ids = { repres_info_by_project = {}
repre_info.product_id version_items_by_project = {}
for repre_info in repre_info_by_id.values() for project_name, repre_ids in repre_ids_by_project.items():
if repre_info.is_valid repre_info_by_id = self._controller.get_representation_info_items(
} project_name, repre_ids
version_items_by_product_id = self._controller.get_version_items( )
product_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_containers = []
update_versions = [] 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_id = container_item.representation_id
repre_info_by_id = repres_info_by_project[project_name]
repre_info = repre_info_by_id[repre_id] 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 product_id = repre_info.product_id
version_items_id = version_items_by_product_id[product_id] version_items_by_id = version_items_by_product_id[product_id]
version_item = version_items_id.get(repre_info.version_id, {}) version_item = version_items_by_id.get(repre_info.version_id, {})
if not version_item or not version_item.is_hero: if not version_item or not version_item.is_hero:
continue continue
version = abs(version_item.version) version = abs(version_item.version)
version_found = False 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: if version_item.is_hero:
continue continue
if version_item.version == version: if version_item.version == version:
@ -971,8 +1071,8 @@ class SceneInventoryView(QtWidgets.QTreeView):
update_containers.append(container_item.item_id) update_containers.append(container_item.item_id)
update_versions.append(version) update_versions.append(version)
# Specify version per item to update to # Specify version per item to update to
self._update_containers(update_containers, update_versions) self._update_containers(update_containers, update_versions)
def _update_containers(self, item_ids, versions): def _update_containers(self, item_ids, versions):
"""Helper to update items to given version (or version per item) """Helper to update items to given version (or version per item)