From 51d63d849335257098d0791703ca0484db8f9ed6 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 24 Jun 2024 09:29:06 +0200 Subject: [PATCH 01/12] Houdini: Remove the legacy creator from before new publisher UI --- .../houdini/client/ayon_houdini/api/plugin.py | 82 ++----------------- 1 file changed, 5 insertions(+), 77 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/api/plugin.py b/server_addon/houdini/client/ayon_houdini/api/plugin.py index 9c6bba925a..c78d85924e 100644 --- a/server_addon/houdini/client/ayon_houdini/api/plugin.py +++ b/server_addon/houdini/client/ayon_houdini/api/plugin.py @@ -10,8 +10,7 @@ import hou import pyblish.api from ayon_core.pipeline import ( CreatorError, - LegacyCreator, - Creator as NewCreator, + Creator, CreatedInstance, AYON_INSTANCE_ID, AVALON_INSTANCE_ID, @@ -22,84 +21,13 @@ from ayon_core.lib import BoolDef from .lib import imprint, read, lsattr, add_self_publish_button +# Backwards compatibility +NewCreator = Creator + SETTINGS_CATEGORY = "houdini" -class Creator(LegacyCreator): - """Creator plugin to create instances in Houdini - - To support the wide range of node types for render output (Alembic, VDB, - Mantra) the Creator needs a node type to create the correct instance - - By default, if none is given, is `geometry`. An example of accepted node - types: geometry, alembic, ifd (mantra) - - Please check the Houdini documentation for more node types. - - Tip: to find the exact node type to create press the `i` left of the node - when hovering over a node. The information is visible under the name of - the node. - - Deprecated: - This creator is deprecated and will be removed in future version. - - """ - defaults = ['Main'] - - def __init__(self, *args, **kwargs): - super(Creator, self).__init__(*args, **kwargs) - self.nodes = [] - - def process(self): - """This is the base functionality to create instances in Houdini - - The selected nodes are stored in self to be used in an override method. - This is currently necessary in order to support the multiple output - types in Houdini which can only be rendered through their own node. - - Default node type if none is given is `geometry` - - It also makes it easier to apply custom settings per instance type - - Example of override method for Alembic: - - def process(self): - instance = super(CreateEpicNode, self, process() - # Set parameters for Alembic node - instance.setParms( - {"sop_path": "$HIP/%s.abc" % self.nodes[0]} - ) - - Returns: - hou.Node - - """ - try: - if (self.options or {}).get("useSelection"): - self.nodes = hou.selectedNodes() - - # Get the node type and remove it from the data, not needed - node_type = self.data.pop("node_type", None) - if node_type is None: - node_type = "geometry" - - # Get out node - out = hou.node("/out") - instance = out.createNode(node_type, node_name=self.name) - instance.moveToGoodPosition() - - imprint(instance, self.data) - - self._process(instance) - - except hou.Error as er: - six.reraise( - CreatorError, - CreatorError("Creator error: {}".format(er)), - sys.exc_info()[2]) - - class HoudiniCreatorBase(object): @staticmethod def cache_instance_data(shared_data): @@ -170,7 +98,7 @@ class HoudiniCreatorBase(object): @six.add_metaclass(ABCMeta) -class HoudiniCreator(NewCreator, HoudiniCreatorBase): +class HoudiniCreator(Creator, HoudiniCreatorBase): """Base class for most of the Houdini creator plugins.""" selected_nodes = [] settings_name = None From 61074623d498f6191f19934df546d4586239b133 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 24 Jun 2024 10:01:55 +0200 Subject: [PATCH 02/12] Remove import of `Creator` which isn't actually the Houdini specific Creator anyway --- server_addon/houdini/client/ayon_houdini/api/__init__.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/api/__init__.py b/server_addon/houdini/client/ayon_houdini/api/__init__.py index 2663a55f6f..358113a555 100644 --- a/server_addon/houdini/client/ayon_houdini/api/__init__.py +++ b/server_addon/houdini/client/ayon_houdini/api/__init__.py @@ -4,10 +4,6 @@ from .pipeline import ( containerise ) -from .plugin import ( - Creator, -) - from .lib import ( lsattr, lsattrs, @@ -23,8 +19,6 @@ __all__ = [ "ls", "containerise", - "Creator", - # Utility functions "lsattr", "lsattrs", From b0701dc6d5b54178ebbeb7c8d38a792c96bde6f5 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 24 Jun 2024 11:23:36 +0200 Subject: [PATCH 03/12] Remove backwards compatibility --- server_addon/houdini/client/ayon_houdini/api/plugin.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/api/plugin.py b/server_addon/houdini/client/ayon_houdini/api/plugin.py index c78d85924e..b841b57617 100644 --- a/server_addon/houdini/client/ayon_houdini/api/plugin.py +++ b/server_addon/houdini/client/ayon_houdini/api/plugin.py @@ -21,9 +21,6 @@ from ayon_core.lib import BoolDef from .lib import imprint, read, lsattr, add_self_publish_button -# Backwards compatibility -NewCreator = Creator - SETTINGS_CATEGORY = "houdini" From 016ad0e0299ce8c411823c9f0b54aba31456f5b8 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 24 Jun 2024 15:43:13 +0200 Subject: [PATCH 04/12] Bump addon version --- server_addon/houdini/client/ayon_houdini/version.py | 2 +- server_addon/houdini/package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/version.py b/server_addon/houdini/client/ayon_houdini/version.py index af2c4557db..66f3ac59e7 100644 --- a/server_addon/houdini/client/ayon_houdini/version.py +++ b/server_addon/houdini/client/ayon_houdini/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'houdini' version.""" -__version__ = "0.3.3" +__version__ = "0.3.4" diff --git a/server_addon/houdini/package.py b/server_addon/houdini/package.py index da13bee9c7..0c1b1fcf9b 100644 --- a/server_addon/houdini/package.py +++ b/server_addon/houdini/package.py @@ -1,6 +1,6 @@ name = "houdini" title = "Houdini" -version = "0.3.3" +version = "0.3.4" client_dir = "ayon_houdini" From cb80c8fa1a59da05cb131511f4575d7e22560830 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 24 Jun 2024 15:44:19 +0200 Subject: [PATCH 05/12] Bump addon version --- server_addon/houdini/client/ayon_houdini/version.py | 2 +- server_addon/houdini/package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/version.py b/server_addon/houdini/client/ayon_houdini/version.py index 66f3ac59e7..b6b644f30e 100644 --- a/server_addon/houdini/client/ayon_houdini/version.py +++ b/server_addon/houdini/client/ayon_houdini/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'houdini' version.""" -__version__ = "0.3.4" +__version__ = "0.3.5" diff --git a/server_addon/houdini/package.py b/server_addon/houdini/package.py index 0c1b1fcf9b..5bdde038c2 100644 --- a/server_addon/houdini/package.py +++ b/server_addon/houdini/package.py @@ -1,6 +1,6 @@ name = "houdini" title = "Houdini" -version = "0.3.4" +version = "0.3.5" client_dir = "ayon_houdini" From 34bdab72ee28d27567fa81c0b21cb1eb58fedf95 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 24 Jun 2024 17:29:16 +0200 Subject: [PATCH 06/12] Don't use six to define ABC class --- client/ayon_core/tools/common_models/__init__.py | 4 ++++ client/ayon_core/tools/common_models/projects.py | 6 ++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/tools/common_models/__init__.py b/client/ayon_core/tools/common_models/__init__.py index f09edfeab2..40394c4732 100644 --- a/client/ayon_core/tools/common_models/__init__.py +++ b/client/ayon_core/tools/common_models/__init__.py @@ -2,6 +2,8 @@ from .cache import CacheItem, NestedCacheItem from .projects import ( + StatusItem, + StatusStates, ProjectItem, ProjectsModel, PROJECTS_MODEL_SENDER, @@ -21,6 +23,8 @@ __all__ = ( "CacheItem", "NestedCacheItem", + "StatusItem", + "StatusStates", "ProjectItem", "ProjectsModel", "PROJECTS_MODEL_SENDER", diff --git a/client/ayon_core/tools/common_models/projects.py b/client/ayon_core/tools/common_models/projects.py index 4e8925388d..1c455a9619 100644 --- a/client/ayon_core/tools/common_models/projects.py +++ b/client/ayon_core/tools/common_models/projects.py @@ -1,8 +1,7 @@ import contextlib -from abc import ABCMeta, abstractmethod +from abc import ABC, abstractmethod import ayon_api -import six from ayon_core.style import get_default_entity_icon_color from ayon_core.lib import CacheItem, NestedCacheItem @@ -10,8 +9,7 @@ from ayon_core.lib import CacheItem, NestedCacheItem PROJECTS_MODEL_SENDER = "projects.model" -@six.add_metaclass(ABCMeta) -class AbstractHierarchyController: +class AbstractHierarchyController(ABC): @abstractmethod def emit_event(self, topic, data, source): pass From 386a627abe9e71783d67929da23f076503940eb7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 24 Jun 2024 17:31:58 +0200 Subject: [PATCH 07/12] Added helper classes and hints for state --- .../ayon_core/tools/common_models/projects.py | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/tools/common_models/projects.py b/client/ayon_core/tools/common_models/projects.py index 1c455a9619..17599c27f6 100644 --- a/client/ayon_core/tools/common_models/projects.py +++ b/client/ayon_core/tools/common_models/projects.py @@ -1,5 +1,6 @@ import contextlib from abc import ABC, abstractmethod +from typing import Literal, Dict, Any import ayon_api @@ -7,6 +8,14 @@ from ayon_core.style import get_default_entity_icon_color from ayon_core.lib import CacheItem, NestedCacheItem PROJECTS_MODEL_SENDER = "projects.model" +StatusStatesType = Literal["not_started", "in_progress", "done", "blocked"] + + +class StatusStates: + not_started = "not_started" + in_progress = "in_progress" + done = "done" + blocked = "blocked" class AbstractHierarchyController(ABC): @@ -23,18 +32,24 @@ class StatusItem: color (str): Status color in hex ("#434a56"). short (str): Short status name ("NRD"). icon (str): Icon name in MaterialIcons ("fiber_new"). - state (Literal["not_started", "in_progress", "done", "blocked"]): - Status state. + state (StatusStatesType): Status state. """ - def __init__(self, name, color, short, icon, state): - self.name = name - self.color = color - self.short = short - self.icon = icon - self.state = state + def __init__( + self, + name: str, + color: str, + short: str, + icon: str, + state: StatusStatesType + ): + self.name: str = name + self.color: str = color + self.short: str = short + self.icon: str = icon + self.state: StatusStatesType = state - def to_data(self): + def to_data(self) -> Dict[str, Any]: return { "name": self.name, "color": self.color, From 14fc4ae187eb48ab169bc94d8b66b3a475d2028f Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 24 Jun 2024 17:32:21 +0200 Subject: [PATCH 08/12] added 'is_last_approved' attribute to 'VersionItem' --- .../tools/sceneinventory/models/containers.py | 46 +++++++++++++++---- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index 95c5322343..c3881ea40d 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -3,7 +3,9 @@ import collections import ayon_api from ayon_api.graphql import GraphQlQuery + from ayon_core.host import ILoadHost +from ayon_core.tools.common_models.projects import StatusStates # --- Implementation that should be in ayon-python-api --- @@ -149,26 +151,35 @@ class RepresentationInfo: class VersionItem: - def __init__(self, version_id, product_id, version, status, is_latest): - self.version = version - self.version_id = version_id - self.product_id = product_id - self.version = version - self.status = status - self.is_latest = is_latest + def __init__( + self, + version_id: str, + product_id: str, + version: int, + status: str, + is_latest: bool, + is_last_approved: bool, + ): + self.version_id: str = version_id + self.product_id: str = product_id + self.version: int = version + self.status: str = status + self.is_latest: bool = is_latest + self.is_last_approved: bool = is_last_approved @property def is_hero(self): return self.version < 0 @classmethod - def from_entity(cls, version_entity, is_latest): + def from_entity(cls, version_entity, is_latest, is_last_approved): return cls( version_id=version_entity["id"], product_id=version_entity["productId"], version=version_entity["version"], status=version_entity["status"], is_latest=is_latest, + is_last_approved=is_last_approved, ) @@ -275,6 +286,11 @@ class ContainersModel: if product_id not in self._version_items_by_product_id } if missing_ids: + status_items_by_name = { + status_item.name: status_item + for status_item in self._controller.get_project_status_items() + } + def version_sorted(entity): return entity["version"] @@ -300,9 +316,21 @@ class ContainersModel: version_entities_by_product_id.items() ): last_version = abs(version_entities[-1]["version"]) + last_approved_id = None + for version_entity in version_entities: + status_item = status_items_by_name.get( + version_entity["status"] + ) + if status_item is None: + continue + if status_item.state == StatusStates.done: + last_approved_id = version_entity["id"] + version_items_by_id = { entity["id"]: VersionItem.from_entity( - entity, abs(entity["version"]) == last_version + entity, + abs(entity["version"]) == last_version, + entity["id"] == last_approved_id ) for entity in version_entities } From db3f5c60c68e47b210dcd644ea69f754c554b8ee Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 24 Jun 2024 17:38:18 +0200 Subject: [PATCH 09/12] implemented logic to update to latest approved --- client/ayon_core/tools/sceneinventory/view.py | 84 ++++++++++++++++++- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index c8cc3299a2..017a9597f1 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -233,10 +233,18 @@ class SceneInventoryView(QtWidgets.QTreeView): has_outdated = False has_loaded_hero_versions = False has_available_hero_version = False - for version_items_by_id in version_items_by_product_id.values(): + has_outdated_approved = False + last_version_by_product_id = {} + for product_id, version_items_by_id in ( + version_items_by_product_id.items() + ): + _has_outdated_approved = False + _last_approved_version_item = None for version_item in version_items_by_id.values(): if version_item.is_hero: has_available_hero_version = True + if version_item.is_last_approved: + _last_approved_version_item = version_item if version_item.version_id not in version_ids: continue @@ -245,6 +253,17 @@ class SceneInventoryView(QtWidgets.QTreeView): elif not version_item.is_latest: has_outdated = True + elif not version_item.is_last_approved: + _has_outdated_approved = True + + if ( + _has_outdated_approved + and _last_approved_version_item is not None + ): + last_version_by_product_id[product_id] = ( + _last_approved_version_item + ) + has_outdated_approved = True switch_to_versioned = None if has_loaded_hero_versions: @@ -261,6 +280,41 @@ class SceneInventoryView(QtWidgets.QTreeView): lambda: self._on_switch_to_versioned(item_ids) ) + update_to_last_approved_action = None + if has_outdated_approved: + approved_version_by_item_id = {} + for container_item in container_items_by_id.values(): + repre_id = container_item.representation_id + repre_info = repre_info_by_id.get(repre_id) + if not repre_info or not repre_info.is_valid: + continue + version_item = last_version_by_product_id.get( + repre_info.product_id + ) + if ( + version_item is None + or version_item.id == repre_info.version_id + ): + continue + approved_version_by_item_id[container_item.item_id] = ( + version_item.version + ) + + update_icon = qtawesome.icon( + "fa.angle-double-up", + color="#00f0b4" + ) + update_to_last_approved_action = QtWidgets.QAction( + update_icon, + "Update to last approved", + menu + ) + update_to_last_approved_action.triggered.connect( + lambda: self._update_containers_to_approved_versions( + approved_version_by_item_id + ) + ) + update_to_latest_action = None if has_outdated or has_loaded_hero_versions: update_icon = qtawesome.icon( @@ -299,7 +353,9 @@ class SceneInventoryView(QtWidgets.QTreeView): # set version set_version_action = None if active_repre_id is not None: - set_version_icon = qtawesome.icon("fa.hashtag", color=DEFAULT_COLOR) + set_version_icon = qtawesome.icon( + "fa.hashtag", color=DEFAULT_COLOR + ) set_version_action = QtWidgets.QAction( set_version_icon, "Set version", @@ -323,6 +379,9 @@ class SceneInventoryView(QtWidgets.QTreeView): if switch_to_versioned: menu.addAction(switch_to_versioned) + if update_to_last_approved_action: + menu.addAction(update_to_last_approved_action) + if update_to_latest_action: menu.addAction(update_to_latest_action) @@ -970,3 +1029,24 @@ class SceneInventoryView(QtWidgets.QTreeView): """ versions = [version for _ in range(len(item_ids))] self._update_containers(item_ids, versions) + + def _update_containers_to_approved_versions( + self, approved_version_by_item_id + ): + """Helper to update items to given version (or version per item) + + If at least one item is specified this will always try to refresh + the inventory even if errors occurred on any of the items. + + Arguments: + approved_version_by_item_id (Dict[str, int]): Version to set by + item id. + + """ + versions = [] + item_ids = [] + for item_id, version in approved_version_by_item_id.items(): + item_ids.append(item_id) + versions.append(version) + + self._update_containers(item_ids, versions) From b710af2662b2df77f672d4d8c84261433caeab5b Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 24 Jun 2024 18:00:54 +0200 Subject: [PATCH 10/12] removed 'Literal' --- client/ayon_core/tools/common_models/projects.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/tools/common_models/projects.py b/client/ayon_core/tools/common_models/projects.py index 17599c27f6..7ec941e6bd 100644 --- a/client/ayon_core/tools/common_models/projects.py +++ b/client/ayon_core/tools/common_models/projects.py @@ -1,6 +1,6 @@ import contextlib from abc import ABC, abstractmethod -from typing import Literal, Dict, Any +from typing import Dict, Any import ayon_api @@ -8,7 +8,6 @@ from ayon_core.style import get_default_entity_icon_color from ayon_core.lib import CacheItem, NestedCacheItem PROJECTS_MODEL_SENDER = "projects.model" -StatusStatesType = Literal["not_started", "in_progress", "done", "blocked"] class StatusStates: @@ -32,7 +31,7 @@ class StatusItem: color (str): Status color in hex ("#434a56"). short (str): Short status name ("NRD"). icon (str): Icon name in MaterialIcons ("fiber_new"). - state (StatusStatesType): Status state. + state (str): Status state. """ def __init__( @@ -41,13 +40,13 @@ class StatusItem: color: str, short: str, icon: str, - state: StatusStatesType + state: str ): self.name: str = name self.color: str = color self.short: str = short self.icon: str = icon - self.state: StatusStatesType = state + self.state: str = state def to_data(self) -> Dict[str, Any]: return { From 47fafbdc05f726d2d11ad2971ae393acbc28f6f7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 24 Jun 2024 18:15:04 +0200 Subject: [PATCH 11/12] fix logic of action discovery --- client/ayon_core/tools/sceneinventory/view.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index 017a9597f1..22ba15fda8 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -243,18 +243,18 @@ class SceneInventoryView(QtWidgets.QTreeView): for version_item in version_items_by_id.values(): if version_item.is_hero: has_available_hero_version = True - if version_item.is_last_approved: + + 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 - elif not version_item.is_last_approved: - _has_outdated_approved = True if ( _has_outdated_approved @@ -281,8 +281,8 @@ class SceneInventoryView(QtWidgets.QTreeView): ) update_to_last_approved_action = None + approved_version_by_item_id = {} if has_outdated_approved: - approved_version_by_item_id = {} for container_item in container_items_by_id.values(): repre_id = container_item.representation_id repre_info = repre_info_by_id.get(repre_id) @@ -293,13 +293,14 @@ class SceneInventoryView(QtWidgets.QTreeView): ) if ( version_item is None - or version_item.id == repre_info.version_id + or version_item.version_id == repre_info.version_id ): continue approved_version_by_item_id[container_item.item_id] = ( version_item.version ) + if approved_version_by_item_id: update_icon = qtawesome.icon( "fa.angle-double-up", color="#00f0b4" From 403442b281b0645952fc3b9f6513e8c8c27ba88c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 24 Jun 2024 18:20:40 +0200 Subject: [PATCH 12/12] fix color of loaded version --- client/ayon_core/tools/sceneinventory/model.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index 3e0c361535..335df87b95 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -217,7 +217,9 @@ class InventoryModel(QtGui.QStandardItemModel): version_label = format_version(version_item.version) is_hero = version_item.version < 0 is_latest = version_item.is_latest - if not is_latest: + # TODO maybe use different colors for last approved and last + # version? Or don't care about color at all? + if not is_latest and not version_item.is_last_approved: version_color = self.OUTDATED_COLOR status_name = version_item.status