From eabd6b601f3ce8e980a053714548d9f9d3466bc5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 22 Sep 2025 19:11:02 +0200 Subject: [PATCH 01/13] small changes or logic order --- client/ayon_core/pipeline/load/utils.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index 836fc5e096..de79ad4d52 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -1042,13 +1042,13 @@ def filter_containers(containers, project_name): hero=True, fields={"id", "productId", "version"} ) - verisons_by_id = {} + versions_by_id = {} versions_by_product_id = collections.defaultdict(list) hero_version_ids = set() for version_entity in version_entities: version_id = version_entity["id"] # Store versions by their ids - verisons_by_id[version_id] = version_entity + versions_by_id[version_id] = version_entity # There's no need to query products for hero versions # - they are considered as latest? if version_entity["version"] < 0: @@ -1083,24 +1083,23 @@ def filter_containers(containers, project_name): repre_entity = repre_entities_by_id.get(repre_id) if not repre_entity: - log.debug(( - "Container '{}' has an invalid representation." + log.debug( + f"Container '{container_name}' has an invalid representation." " It is missing in the database." - ).format(container_name)) + ) not_found_containers.append(container) continue version_id = repre_entity["versionId"] - if version_id in outdated_version_ids: - outdated_containers.append(container) - - elif version_id not in verisons_by_id: - log.debug(( - "Representation on container '{}' has an invalid version." - " It is missing in the database." - ).format(container_name)) + if version_id not in versions_by_id: + log.debug( + f"Representation on container '{container_name}' has an" + " invalid version. It is missing in the database." + ) not_found_containers.append(container) + elif version_id in outdated_version_ids: + outdated_containers.append(container) else: uptodate_containers.append(container) From 0748d659d71291f5ec326086201891640c4ec265 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 22 Sep 2025 19:11:33 +0200 Subject: [PATCH 02/13] do not consider locked containers in 'get_outdated_containers' as outdated --- client/ayon_core/pipeline/load/utils.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index de79ad4d52..7dab889ec5 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -964,7 +964,12 @@ def get_outdated_containers(host=None, project_name=None): containers = host.get_containers() else: containers = host.ls() - return filter_containers(containers, project_name).outdated + outdated_containers = [] + for container in filter_containers(containers, project_name).outdated: + if container.get("locked_version") is True: + continue + outdated_containers.append(container) + return outdated_containers def _is_valid_representation_id(repre_id: Any) -> bool: From ace6a84f5e9759f83a7f915fafb0f5ee1830b4a1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 22 Sep 2025 19:12:03 +0200 Subject: [PATCH 03/13] look for locked version in container --- client/ayon_core/tools/sceneinventory/models/containers.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index 47f74476de..0e19f381cd 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -95,7 +95,8 @@ class ContainerItem: namespace, object_name, item_id, - project_name + project_name, + version_locked, ): self.representation_id = representation_id self.loader_name = loader_name @@ -103,6 +104,7 @@ class ContainerItem: self.namespace = namespace self.item_id = item_id self.project_name = project_name + self.version_locked = version_locked @classmethod def from_container_data(cls, current_project_name, container): @@ -114,7 +116,8 @@ class ContainerItem: item_id=uuid.uuid4().hex, project_name=container.get( "project_name", current_project_name - ) + ), + version_locked=container.get("version_locked", False), ) From 1f41e03fe00c57fcb341d6c82677184e8e80a1a4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 22 Sep 2025 19:12:26 +0200 Subject: [PATCH 04/13] store the information to the model item --- client/ayon_core/tools/sceneinventory/model.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index 9977acea21..27211165bf 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -37,6 +37,7 @@ REMOTE_SITE_ICON_ROLE = QtCore.Qt.UserRole + 23 # containers inbetween refresh. ITEM_UNIQUE_NAME_ROLE = QtCore.Qt.UserRole + 24 PROJECT_NAME_ROLE = QtCore.Qt.UserRole + 25 +CONTAINER_VERSION_LOCKED_ROLE = QtCore.Qt.UserRole + 26 class InventoryModel(QtGui.QStandardItemModel): @@ -291,6 +292,10 @@ class InventoryModel(QtGui.QStandardItemModel): item.setData(container_item.object_name, OBJECT_NAME_ROLE) item.setData(True, IS_CONTAINER_ITEM_ROLE) item.setData(unique_name, ITEM_UNIQUE_NAME_ROLE) + item.setData( + container_item.version_locked, + CONTAINER_VERSION_LOCKED_ROLE + ) container_model_items.append(item) progress = progress_by_id[repre_id] From 2fbb6c279be98a6d7c8c110db53d0c53a8f51b04 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 22 Sep 2025 19:13:11 +0200 Subject: [PATCH 05/13] allow more options for icons --- client/ayon_core/tools/sceneinventory/view.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index fdd1bdbe75..ead10f9e62 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -524,7 +524,15 @@ class SceneInventoryView(QtWidgets.QTreeView): submenu = QtWidgets.QMenu("Actions", self) for action in custom_actions: color = action.color or DEFAULT_COLOR - icon = qtawesome.icon("fa.%s" % action.icon, color=color) + icon_def = action.icon + if not isinstance(action.icon, dict): + icon_def = { + "type": "awesome-font", + "name": icon_def, + "color": color, + } + icon = get_qt_icon(icon_def) + # icon = qtawesome.icon("fa.%s" % action.icon, color=color) action_item = QtWidgets.QAction(icon, action.label, submenu) action_item.triggered.connect( partial( From d96e8087ec63676be751b8618d79c3ea7a5c2a03 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 22 Sep 2025 19:13:22 +0200 Subject: [PATCH 06/13] draw a lock next to version if is locked --- .../tools/sceneinventory/delegates.py | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/delegates.py b/client/ayon_core/tools/sceneinventory/delegates.py index 6f91587613..9bc4294fda 100644 --- a/client/ayon_core/tools/sceneinventory/delegates.py +++ b/client/ayon_core/tools/sceneinventory/delegates.py @@ -1,10 +1,14 @@ from qtpy import QtWidgets, QtCore, QtGui -from .model import VERSION_LABEL_ROLE +from ayon_core.tools.utils import get_qt_icon + +from .model import VERSION_LABEL_ROLE, CONTAINER_VERSION_LOCKED_ROLE class VersionDelegate(QtWidgets.QStyledItemDelegate): """A delegate that display version integer formatted as version string.""" + _locked_icon = None + def paint(self, painter, option, index): fg_color = index.data(QtCore.Qt.ForegroundRole) if fg_color: @@ -45,10 +49,35 @@ class VersionDelegate(QtWidgets.QStyledItemDelegate): QtWidgets.QStyle.PM_FocusFrameHMargin, option, option.widget ) + 1 + text_rect_f = text_rect.adjusted( + text_margin, 0, - text_margin, 0 + ) + painter.drawText( - text_rect.adjusted(text_margin, 0, - text_margin, 0), + text_rect_f, option.displayAlignment, text ) + if index.data(CONTAINER_VERSION_LOCKED_ROLE) is True: + icon = self._get_locked_icon() + size = max(text_rect_f.height() // 2, 16) + margin = (text_rect_f.height() - size) // 2 + + icon_rect = QtCore.QRect( + text_rect_f.right() - size, + text_rect_f.top() + margin, + size, + size + ) + icon.paint(painter, icon_rect) painter.restore() + + def _get_locked_icon(cls): + if cls._locked_icon is None: + cls._locked_icon = get_qt_icon({ + "type": "material-symbols", + "name": "lock", + "color": "white", + }) + return cls._locked_icon From bb64f3c2a5e9e2f36111d722b4e677334c388e26 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 22 Sep 2025 19:13:41 +0200 Subject: [PATCH 07/13] make sure 'data_changed' is triggered --- client/ayon_core/tools/sceneinventory/view.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index ead10f9e62..b1e378f343 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -17,6 +17,7 @@ from ayon_core.tools.utils.lib import ( format_version, preserve_expanded_rows, preserve_selection, + get_qt_icon, ) from ayon_core.tools.utils.delegates import StatusDelegate @@ -46,7 +47,7 @@ class SceneInventoryView(QtWidgets.QTreeView): hierarchy_view_changed = QtCore.Signal(bool) def __init__(self, controller, parent): - super(SceneInventoryView, self).__init__(parent=parent) + super().__init__(parent=parent) # view settings self.setIndentation(12) @@ -623,17 +624,20 @@ class SceneInventoryView(QtWidgets.QTreeView): containers_by_id = self._controller.get_containers_by_item_ids( item_ids ) - result = action.process(list(containers_by_id.values())) - if result: - self.data_changed.emit() + try: + result = action.process(list(containers_by_id.values())) + if not result: + pass - if isinstance(result, (list, set)): + elif isinstance(result, (list, set)): self._select_items_by_action(result) - if isinstance(result, dict): + elif isinstance(result, dict): self._select_items_by_action( result["objectNames"], result["options"] ) + finally: + self.data_changed.emit() def _select_items_by_action(self, object_names, options=None): """Select view items by the result of action From 2656e0c7d860a1468900c5bf2a528e39a11fbe90 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 23 Sep 2025 12:11:41 +0200 Subject: [PATCH 08/13] remove commented line Co-authored-by: Roy Nieterau --- client/ayon_core/tools/sceneinventory/view.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index b1e378f343..6a825a2ca4 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -533,7 +533,6 @@ class SceneInventoryView(QtWidgets.QTreeView): "color": color, } icon = get_qt_icon(icon_def) - # icon = qtawesome.icon("fa.%s" % action.icon, color=color) action_item = QtWidgets.QAction(icon, action.label, submenu) action_item.triggered.connect( partial( From 0122686522aad4dfa8baf144bb8e06bc83fcf7be Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 23 Sep 2025 12:39:32 +0200 Subject: [PATCH 09/13] allow to ignore locked versions --- client/ayon_core/pipeline/load/utils.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index 7dab889ec5..6b751dec30 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -942,7 +942,11 @@ def any_outdated_containers(host=None, project_name=None): return False -def get_outdated_containers(host=None, project_name=None): +def get_outdated_containers( + host=None, + project_name=None, + ignore_locked_versions: bool = False, +): """Collect outdated containers from host scene. Currently registered host and project in global session are used if @@ -951,6 +955,8 @@ def get_outdated_containers(host=None, project_name=None): Args: host (ModuleType): Host implementation with 'ls' function available. project_name (str): Name of project in which context we are. + ignore_locked_versions (bool): Locked versions are ignored. + """ from ayon_core.pipeline import registered_host, get_current_project_name @@ -964,9 +970,13 @@ def get_outdated_containers(host=None, project_name=None): containers = host.get_containers() else: containers = host.ls() + outdated_containers = [] for container in filter_containers(containers, project_name).outdated: - if container.get("locked_version") is True: + if ( + not ignore_locked_versions + and container.get("locked_version") is True + ): continue outdated_containers.append(container) return outdated_containers From 88db0b46e83bbddb1a112af2947715172986d45a Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 23 Sep 2025 12:39:48 +0200 Subject: [PATCH 10/13] added typehints --- client/ayon_core/pipeline/load/utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index 6b751dec30..0cfe004572 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -9,7 +9,7 @@ from typing import Optional, Union, Any import ayon_api -from ayon_core.host import ILoadHost +from ayon_core.host import ILoadHost, AbstractHost from ayon_core.lib import ( StringTemplate, TemplateUnsolved, @@ -943,8 +943,8 @@ def any_outdated_containers(host=None, project_name=None): def get_outdated_containers( - host=None, - project_name=None, + host: Optional[AbstractHost] = None, + project_name: Optional[str] = None, ignore_locked_versions: bool = False, ): """Collect outdated containers from host scene. @@ -953,8 +953,8 @@ def get_outdated_containers( arguments are not passed. Args: - host (ModuleType): Host implementation with 'ls' function available. - project_name (str): Name of project in which context we are. + host (Optional[AbstractHost]): Host implementation. + project_name (Optional[str]): Name of project in which context we are. ignore_locked_versions (bool): Locked versions are ignored. """ @@ -1008,8 +1008,8 @@ def filter_containers(containers, project_name): Returns: ContainersFilterResult: Named tuple with 'latest', 'outdated', 'invalid' and 'not_found' containers. - """ + """ # Make sure containers is list that won't change containers = list(containers) From 740f0276e25b3ec9130ad3346c04d3cb4bda54c9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 23 Sep 2025 12:39:59 +0200 Subject: [PATCH 11/13] add a todo to 'filter_containers' --- client/ayon_core/pipeline/load/utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index 0cfe004572..a111444d48 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -1000,6 +1000,9 @@ def filter_containers(containers, project_name): 'invalid' are invalid containers (invalid content) and 'not_found' has some missing entity in database. + Todos: + Respect 'project_name' on containers if is available. + Args: containers (Iterable[dict]): List of containers referenced into scene. project_name (str): Name of project in which context shoud look for From 3a524844609d375656e5eed3e2fe71a9ce565203 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 23 Sep 2025 12:42:06 +0200 Subject: [PATCH 12/13] revert back output handling --- client/ayon_core/tools/sceneinventory/view.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index 6a825a2ca4..22bc170230 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -623,20 +623,17 @@ class SceneInventoryView(QtWidgets.QTreeView): containers_by_id = self._controller.get_containers_by_item_ids( item_ids ) - try: - result = action.process(list(containers_by_id.values())) - if not result: - pass + result = action.process(list(containers_by_id.values())) + if result: + self.data_changed.emit() - elif isinstance(result, (list, set)): + if isinstance(result, (list, set)): self._select_items_by_action(result) elif isinstance(result, dict): self._select_items_by_action( result["objectNames"], result["options"] ) - finally: - self.data_changed.emit() def _select_items_by_action(self, object_names, options=None): """Select view items by the result of action From 0b6e171558ee3846b72f2e182de93a94d41aeaa8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 23 Sep 2025 13:27:21 +0200 Subject: [PATCH 13/13] Fix wrong key --- client/ayon_core/pipeline/load/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index a111444d48..d1731d4cf9 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -975,7 +975,7 @@ def get_outdated_containers( for container in filter_containers(containers, project_name).outdated: if ( not ignore_locked_versions - and container.get("locked_version") is True + and container.get("version_locked") is True ): continue outdated_containers.append(container)