From 10c66f496e911b105c7ce34818d10bd471059a14 Mon Sep 17 00:00:00 2001 From: Philippe Leprince Date: Fri, 9 May 2025 14:39:26 +0200 Subject: [PATCH 01/26] first version Signed-off-by: Philippe Leprince --- .github/workflows/deploy_mkdocs.yml | 78 +++++++++++++++++++++++++++++ docs/mkdocs_requirements.txt | 9 ++++ 2 files changed, 87 insertions(+) create mode 100644 .github/workflows/deploy_mkdocs.yml create mode 100644 docs/mkdocs_requirements.txt diff --git a/.github/workflows/deploy_mkdocs.yml b/.github/workflows/deploy_mkdocs.yml new file mode 100644 index 0000000000..9fd97c1e82 --- /dev/null +++ b/.github/workflows/deploy_mkdocs.yml @@ -0,0 +1,78 @@ +name: Deploy MkDocs + +on: + workflow_dispatch: + workflow_call: + inputs: + repo: + type: string + required: true + branch_name: + type: string + required: true + default: "main" + tag: + type: string + required: true + secrets: + token: + required: true + user: + required: true + email: + required: true + +env: + GH_TOKEN: ${{ secrets.token || secrets.YNPUT_BOT_TOKEN }} + GH_USER: ${{ secrets.user || secrets.CI_USER }} + GH_EMAIL: ${{ secrets.email || secrets.CI_EMAIL }} + +jobs: + + verify-latest-release: + uses: ynput/ops-repo-automation/.github/workflows/verify_latest_release.yml@main + with: + repo: ${{ github.repository }} + expect_release: true + secrets: + gh_token: ${{ secrets.token }} + + verify-repo-secrets: + uses: ynput/ops-repo-automation/.github/workflows/verify_secrets.yml@main + with: + repo: ${{ github.repository }} + secrets: + gh_token: ${{ secrets.token }} + gh_user: ${{ secrets.user }} + gh_email: ${{ secrets.email }} + + verify-repo-vars: + uses: ynput/ops-repo-automation/.github/workflows/verify_variables.yml@main + with: + variables: "MAIN_BRANCH,MINOR_BUMP_LABEL,PATCH_BUMP_LABEL,PROJECT_NAME" + repo: ${{ github.repository }} + secrets: + gh_token: ${{ secrets.token }} + + deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout ${{ inputs.branch_name }} + uses: actions/checkout@v4 + with: + ref: ${{ inputs.branch_name }} + fetch-depth: 0 + submodules: true + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.x" + + - name: Install dependencies + run: | + python3 -m pip install -r ./docs/mkdocs_requirements.txt + + - name: Mike deploy + run: mike deploy --update-aliases ${{ inputs.tag }} latest diff --git a/docs/mkdocs_requirements.txt b/docs/mkdocs_requirements.txt new file mode 100644 index 0000000000..829d02951a --- /dev/null +++ b/docs/mkdocs_requirements.txt @@ -0,0 +1,9 @@ +mkdocs-material >= 9.6.7 +mkdocs-autoapi >= 0.4.0 +mkdocstrings-python >= 1.16.2 +mkdocs-minify-plugin >= 0.8.0 +markdown-checklist >= 0.4.4 +mdx-gh-links >= 0.4 +pymdown-extensions >= 10.14.3 +mike >= 2.1.3 +mkdocstrings-shell >= 1.0.2 From bd1dd87d242ee3d637133bea0e84ee64c2b76172 Mon Sep 17 00:00:00 2001 From: Philippe Leprince Date: Fri, 9 May 2025 16:41:16 +0200 Subject: [PATCH 02/26] can I test this ? Signed-off-by: Philippe Leprince --- .github/workflows/deploy_mkdocs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy_mkdocs.yml b/.github/workflows/deploy_mkdocs.yml index 9fd97c1e82..fcee1dd3c2 100644 --- a/.github/workflows/deploy_mkdocs.yml +++ b/.github/workflows/deploy_mkdocs.yml @@ -1,6 +1,7 @@ name: Deploy MkDocs on: + pull_request: workflow_dispatch: workflow_call: inputs: From 4977e771224ed202d76d11c6bfd6187d37812240 Mon Sep 17 00:00:00 2001 From: Philippe Leprince Date: Fri, 9 May 2025 16:44:05 +0200 Subject: [PATCH 03/26] remove some tests Signed-off-by: Philippe Leprince --- .github/workflows/deploy_mkdocs.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/deploy_mkdocs.yml b/.github/workflows/deploy_mkdocs.yml index fcee1dd3c2..80e69b8fa1 100644 --- a/.github/workflows/deploy_mkdocs.yml +++ b/.github/workflows/deploy_mkdocs.yml @@ -30,13 +30,13 @@ env: jobs: - verify-latest-release: - uses: ynput/ops-repo-automation/.github/workflows/verify_latest_release.yml@main - with: - repo: ${{ github.repository }} - expect_release: true - secrets: - gh_token: ${{ secrets.token }} +# verify-latest-release: +# uses: ynput/ops-repo-automation/.github/workflows/verify_latest_release.yml@main +# with: +# repo: ${{ github.repository }} +# expect_release: true +# secrets: +# gh_token: ${{ secrets.token }} verify-repo-secrets: uses: ynput/ops-repo-automation/.github/workflows/verify_secrets.yml@main @@ -47,13 +47,13 @@ jobs: gh_user: ${{ secrets.user }} gh_email: ${{ secrets.email }} - verify-repo-vars: - uses: ynput/ops-repo-automation/.github/workflows/verify_variables.yml@main - with: - variables: "MAIN_BRANCH,MINOR_BUMP_LABEL,PATCH_BUMP_LABEL,PROJECT_NAME" - repo: ${{ github.repository }} - secrets: - gh_token: ${{ secrets.token }} +# verify-repo-vars: +# uses: ynput/ops-repo-automation/.github/workflows/verify_variables.yml@main +# with: +# variables: "MAIN_BRANCH,MINOR_BUMP_LABEL,PATCH_BUMP_LABEL,PROJECT_NAME" +# repo: ${{ github.repository }} +# secrets: +# gh_token: ${{ secrets.token }} deploy: runs-on: ubuntu-latest From dc9d65d9b85d6a4afe5eda2e3638facf2d7d2c6a Mon Sep 17 00:00:00 2001 From: Philippe Leprince Date: Fri, 9 May 2025 17:01:09 +0200 Subject: [PATCH 04/26] fix authentification error Signed-off-by: Philippe Leprince --- .github/workflows/deploy_mkdocs.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/deploy_mkdocs.yml b/.github/workflows/deploy_mkdocs.yml index 80e69b8fa1..98866eb43e 100644 --- a/.github/workflows/deploy_mkdocs.yml +++ b/.github/workflows/deploy_mkdocs.yml @@ -66,6 +66,11 @@ jobs: fetch-depth: 0 submodules: true + - name: 🔑 Set Authentication + run: | + git config --global user.name "${{ secrets.user }}" + git config --global user.email "${{ secrets.email }}" + - name: Set up Python uses: actions/setup-python@v4 with: From 5899f484a5d242904b5ee21808b2c53249deadc7 Mon Sep 17 00:00:00 2001 From: Philippe Leprince Date: Fri, 9 May 2025 17:10:26 +0200 Subject: [PATCH 05/26] better authentification fix Signed-off-by: Philippe Leprince --- .github/workflows/deploy_mkdocs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy_mkdocs.yml b/.github/workflows/deploy_mkdocs.yml index 98866eb43e..8c05985613 100644 --- a/.github/workflows/deploy_mkdocs.yml +++ b/.github/workflows/deploy_mkdocs.yml @@ -68,8 +68,8 @@ jobs: - name: 🔑 Set Authentication run: | - git config --global user.name "${{ secrets.user }}" - git config --global user.email "${{ secrets.email }}" + git config --global user.name "${{ secrets.user || secrets.CI_USER }}" + git config --global user.email "${{ secrets.email || secrets.CI_EMAIL }}" - name: Set up Python uses: actions/setup-python@v4 From b5006abc8b51bf18f7ba4fa083925df364877692 Mon Sep 17 00:00:00 2001 From: Philippe Leprince Date: Fri, 9 May 2025 17:15:09 +0200 Subject: [PATCH 06/26] display tag Signed-off-by: Philippe Leprince --- .github/workflows/deploy_mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy_mkdocs.yml b/.github/workflows/deploy_mkdocs.yml index 8c05985613..49f6cfafba 100644 --- a/.github/workflows/deploy_mkdocs.yml +++ b/.github/workflows/deploy_mkdocs.yml @@ -80,5 +80,5 @@ jobs: run: | python3 -m pip install -r ./docs/mkdocs_requirements.txt - - name: Mike deploy + - name: Mike deploy ${{ inputs.tag }} run: mike deploy --update-aliases ${{ inputs.tag }} latest From 71a5a4603020905dbd2a33751b4f24030bbabadb Mon Sep 17 00:00:00 2001 From: Philippe Leprince Date: Fri, 9 May 2025 17:38:41 +0200 Subject: [PATCH 07/26] Get the current tag with fallback to 1.0.0 Signed-off-by: Philippe Leprince --- .github/workflows/deploy_mkdocs.yml | 30 ++++++++--------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/.github/workflows/deploy_mkdocs.yml b/.github/workflows/deploy_mkdocs.yml index 49f6cfafba..2e6329f924 100644 --- a/.github/workflows/deploy_mkdocs.yml +++ b/.github/workflows/deploy_mkdocs.yml @@ -12,9 +12,6 @@ on: type: string required: true default: "main" - tag: - type: string - required: true secrets: token: required: true @@ -29,15 +26,6 @@ env: GH_EMAIL: ${{ secrets.email || secrets.CI_EMAIL }} jobs: - -# verify-latest-release: -# uses: ynput/ops-repo-automation/.github/workflows/verify_latest_release.yml@main -# with: -# repo: ${{ github.repository }} -# expect_release: true -# secrets: -# gh_token: ${{ secrets.token }} - verify-repo-secrets: uses: ynput/ops-repo-automation/.github/workflows/verify_secrets.yml@main with: @@ -47,14 +35,6 @@ jobs: gh_user: ${{ secrets.user }} gh_email: ${{ secrets.email }} -# verify-repo-vars: -# uses: ynput/ops-repo-automation/.github/workflows/verify_variables.yml@main -# with: -# variables: "MAIN_BRANCH,MINOR_BUMP_LABEL,PATCH_BUMP_LABEL,PROJECT_NAME" -# repo: ${{ github.repository }} -# secrets: -# gh_token: ${{ secrets.token }} - deploy: runs-on: ubuntu-latest @@ -71,6 +51,12 @@ jobs: git config --global user.name "${{ secrets.user || secrets.CI_USER }}" git config --global user.email "${{ secrets.email || secrets.CI_EMAIL }}" + - name: Get current tag + id: git_tag + uses: "devops-actions/action-get-tag@v1.0.3" + with: + default: 1.0.0 + - name: Set up Python uses: actions/setup-python@v4 with: @@ -80,5 +66,5 @@ jobs: run: | python3 -m pip install -r ./docs/mkdocs_requirements.txt - - name: Mike deploy ${{ inputs.tag }} - run: mike deploy --update-aliases ${{ inputs.tag }} latest + - name: Mike deploy ${{ steps.git_tag.outputs.tag }} + run: mike deploy --update-aliases ${{ steps.git_tag.outputs.tag }} latest From 17fbd5228cad7a867137751f2a0ceb125f10f0a0 Mon Sep 17 00:00:00 2001 From: Philippe Leprince Date: Fri, 9 May 2025 17:43:42 +0200 Subject: [PATCH 08/26] remove quotes Signed-off-by: Philippe Leprince --- .github/workflows/deploy_mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy_mkdocs.yml b/.github/workflows/deploy_mkdocs.yml index 2e6329f924..a8281a1de0 100644 --- a/.github/workflows/deploy_mkdocs.yml +++ b/.github/workflows/deploy_mkdocs.yml @@ -53,7 +53,7 @@ jobs: - name: Get current tag id: git_tag - uses: "devops-actions/action-get-tag@v1.0.3" + uses: devops-actions/action-get-tag@v1.0.3 with: default: 1.0.0 From 18c0299bfb9a1812a485d8c454f68f5d165f5271 Mon Sep 17 00:00:00 2001 From: Philippe Leprince Date: Fri, 9 May 2025 17:47:57 +0200 Subject: [PATCH 09/26] fix missing branch name Signed-off-by: Philippe Leprince --- .github/workflows/deploy_mkdocs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy_mkdocs.yml b/.github/workflows/deploy_mkdocs.yml index a8281a1de0..4d6fd9104e 100644 --- a/.github/workflows/deploy_mkdocs.yml +++ b/.github/workflows/deploy_mkdocs.yml @@ -39,10 +39,10 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout ${{ inputs.branch_name }} + - name: Checkout ${{ inputs.branch_name || 'main' }} uses: actions/checkout@v4 with: - ref: ${{ inputs.branch_name }} + ref: ${{ inputs.branch_name || 'main' }} fetch-depth: 0 submodules: true From af9fd17ee8108f4a13b9777fa9daf68e3a09fa74 Mon Sep 17 00:00:00 2001 From: Philippe Leprince Date: Fri, 9 May 2025 17:55:49 +0200 Subject: [PATCH 10/26] format Signed-off-by: Philippe Leprince --- .github/workflows/deploy_mkdocs.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/deploy_mkdocs.yml b/.github/workflows/deploy_mkdocs.yml index 4d6fd9104e..2f46bdc6ad 100644 --- a/.github/workflows/deploy_mkdocs.yml +++ b/.github/workflows/deploy_mkdocs.yml @@ -21,28 +21,28 @@ on: required: true env: - GH_TOKEN: ${{ secrets.token || secrets.YNPUT_BOT_TOKEN }} - GH_USER: ${{ secrets.user || secrets.CI_USER }} - GH_EMAIL: ${{ secrets.email || secrets.CI_EMAIL }} + GH_TOKEN: ${{ secrets.token || secrets.YNPUT_BOT_TOKEN }} + GH_USER: ${{ secrets.user || secrets.CI_USER }} + GH_EMAIL: ${{ secrets.email || secrets.CI_EMAIL }} jobs: verify-repo-secrets: - uses: ynput/ops-repo-automation/.github/workflows/verify_secrets.yml@main - with: - repo: ${{ github.repository }} - secrets: - gh_token: ${{ secrets.token }} - gh_user: ${{ secrets.user }} - gh_email: ${{ secrets.email }} + uses: ynput/ops-repo-automation/.github/workflows/verify_secrets.yml@main + with: + repo: ${{ github.repository }} + secrets: + gh_token: ${{ secrets.token }} + gh_user: ${{ secrets.user }} + gh_email: ${{ secrets.email }} deploy: runs-on: ubuntu-latest steps: - - name: Checkout ${{ inputs.branch_name || 'main' }} + - name: Checkout ${{ inputs.branch_name}} uses: actions/checkout@v4 with: - ref: ${{ inputs.branch_name || 'main' }} + ref: ${{ inputs.branch_name}} fetch-depth: 0 submodules: true From 63c81970316e66153cb2e2d05f1899c62fac89fd Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 2 Jul 2025 14:47:05 +0200 Subject: [PATCH 11/26] try to fix flickering issue in publisher --- client/ayon_core/tools/utils/widgets.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/tools/utils/widgets.py b/client/ayon_core/tools/utils/widgets.py index de2c42c91f..941aa692f9 100644 --- a/client/ayon_core/tools/utils/widgets.py +++ b/client/ayon_core/tools/utils/widgets.py @@ -441,14 +441,18 @@ class ExpandingTextEdit(QtWidgets.QTextEdit): margins = self.contentsMargins() document_width = 0 - if width >= margins.left() + margins.right(): - document_width = width - margins.left() - margins.right() + margins_size = margins.left() + margins.right() + if width >= margins_size: + document_width = width - margins_size document = self.document().clone() document.setTextWidth(document_width) return math.ceil( - margins.top() + document.size().height() + margins.bottom() + margins.top() + + document.size().height() + + margins.bottom() + + 2 ) def sizeHint(self): From 6419e3ed749fe33c80100a5fe5716166d4f404e2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 11 Sep 2025 13:35:19 +0200 Subject: [PATCH 12/26] use python 3 super --- client/ayon_core/tools/utils/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/utils/widgets.py b/client/ayon_core/tools/utils/widgets.py index 941aa692f9..4b787ff830 100644 --- a/client/ayon_core/tools/utils/widgets.py +++ b/client/ayon_core/tools/utils/widgets.py @@ -418,7 +418,7 @@ class ExpandingTextEdit(QtWidgets.QTextEdit): """QTextEdit which does not have sroll area but expands height.""" def __init__(self, parent=None): - super(ExpandingTextEdit, self).__init__(parent) + super().__init__(parent) size_policy = self.sizePolicy() size_policy.setHeightForWidth(True) From 6b058fccf092717111444b89e168e4f678d9ca51 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 11 Sep 2025 13:35:50 +0200 Subject: [PATCH 13/26] disable scroll bards on message input --- client/ayon_core/tools/publisher/widgets/report_page.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/ayon_core/tools/publisher/widgets/report_page.py b/client/ayon_core/tools/publisher/widgets/report_page.py index 1e46e7e52c..033ddab0ef 100644 --- a/client/ayon_core/tools/publisher/widgets/report_page.py +++ b/client/ayon_core/tools/publisher/widgets/report_page.py @@ -1147,6 +1147,8 @@ class LogItemMessage(QtWidgets.QTextEdit): QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum ) + self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) document = self.document() document.documentLayout().documentSizeChanged.connect( self._adjust_minimum_size From 8f2f10bce1efca04f6f0a22f663d98a99cce95c6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 12 Sep 2025 18:20:43 +0200 Subject: [PATCH 14/26] added 'get_project_entity' to controller --- client/ayon_core/tools/loader/abstract.py | 6 ++++++ client/ayon_core/tools/loader/control.py | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/client/ayon_core/tools/loader/abstract.py b/client/ayon_core/tools/loader/abstract.py index 5ab7e78212..42e88c15d7 100644 --- a/client/ayon_core/tools/loader/abstract.py +++ b/client/ayon_core/tools/loader/abstract.py @@ -513,6 +513,12 @@ class BackendLoaderController(_BaseLoaderController): pass + @abstractmethod + def get_project_entity( + self, project_name: Optional[str] + ) -> Optional[dict[str, Any]]: + pass + class FrontendLoaderController(_BaseLoaderController): @abstractmethod diff --git a/client/ayon_core/tools/loader/control.py b/client/ayon_core/tools/loader/control.py index 7ba42a0981..492a05e82f 100644 --- a/client/ayon_core/tools/loader/control.py +++ b/client/ayon_core/tools/loader/control.py @@ -2,6 +2,7 @@ from __future__ import annotations import logging import uuid +from typing import Optional, Any import ayon_api @@ -188,6 +189,11 @@ class LoaderController(BackendLoaderController, FrontendLoaderController): def get_project_items(self, sender=None): return self._projects_model.get_project_items(sender) + def get_project_entity( + self, project_name: Optional[str] + ) -> Optional[dict[str, Any]]: + return self._projects_model.get_project_entity(project_name) + def get_folder_type_items(self, project_name, sender=None): return self._projects_model.get_folder_type_items( project_name, sender From 7f8e62e8efc2efa016fe66cabc305d1a215fd5bf Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 12 Sep 2025 18:29:55 +0200 Subject: [PATCH 15/26] add icons mapping to products --- client/ayon_core/tools/loader/abstract.py | 7 - .../ayon_core/tools/loader/models/products.py | 205 +++++++++--------- .../tools/loader/ui/products_model.py | 8 +- 3 files changed, 103 insertions(+), 117 deletions(-) diff --git a/client/ayon_core/tools/loader/abstract.py b/client/ayon_core/tools/loader/abstract.py index 42e88c15d7..87cfb1f3f2 100644 --- a/client/ayon_core/tools/loader/abstract.py +++ b/client/ayon_core/tools/loader/abstract.py @@ -78,7 +78,6 @@ class ProductItem: product_type (str): Product type. product_name (str): Product name. product_icon (dict[str, Any]): Product icon definition. - product_type_icon (dict[str, Any]): Product type icon definition. product_in_scene (bool): Is product in scene (only when used in DCC). group_name (str): Group name. folder_id (str): Folder id. @@ -93,8 +92,6 @@ class ProductItem: product_base_type: str, product_name: str, product_icon: dict[str, Any], - product_type_icon: dict[str, Any], - product_base_type_icon: dict[str, Any], group_name: str, folder_id: str, folder_label: str, @@ -106,8 +103,6 @@ class ProductItem: self.product_base_type = product_base_type self.product_name = product_name self.product_icon = product_icon - self.product_type_icon = product_type_icon - self.product_base_type_icon = product_base_type_icon self.product_in_scene = product_in_scene self.group_name = group_name self.folder_id = folder_id @@ -121,8 +116,6 @@ class ProductItem: "product_base_type": self.product_base_type, "product_name": self.product_name, "product_icon": self.product_icon, - "product_type_icon": self.product_type_icon, - "product_base_type_icon": self.product_base_type_icon, "product_in_scene": self.product_in_scene, "group_name": self.group_name, "folder_id": self.folder_id, diff --git a/client/ayon_core/tools/loader/models/products.py b/client/ayon_core/tools/loader/models/products.py index 87e2406c81..f5e0b2fa80 100644 --- a/client/ayon_core/tools/loader/models/products.py +++ b/client/ayon_core/tools/loader/models/products.py @@ -9,7 +9,6 @@ import arrow import ayon_api from ayon_api.operations import OperationsSession - from ayon_core.lib import NestedCacheItem from ayon_core.style import get_default_entity_icon_color from ayon_core.tools.loader.abstract import ( @@ -21,12 +20,63 @@ from ayon_core.tools.loader.abstract import ( ) if TYPE_CHECKING: - from ayon_api.typing import ProductBaseTypeDict, ProductDict, VersionDict - + from ayon_api.typing import ( + ProductBaseTypeDict, + ProductDict, + VersionDict, + ) PRODUCTS_MODEL_SENDER = "products.model" +class ProductBaseTypeIconMapping: + def __init__( + self, + default: Optional[dict[str, str]] = None, + definitions: Optional[list[dict[str, str]]] = None, + ): + self._default = default or {} + self._definitions = definitions or [] + + self._default_def = None + self._definitions_by_name = None + + def get_icon( + self, + product_base_type: Optional[str] = None, + product_type: Optional[str] = None, + ) -> dict[str, str]: + defs = self._get_defs_by_name() + icon = defs.get(product_type) + if icon is None: + icon = defs.get(product_base_type) + if icon is None: + icon = self._get_default_def() + return icon.copy() + + def _get_default_def(self) -> dict[str, str]: + if self._default_def is None: + self._default_def = { + "type": "material-symbols", + "name": self._default.get("icon", "inventory_2"), + "color": self._default.get("color", "#cccccc"), + } + + return self._default_def + + def _get_defs_by_name(self) -> dict[str, dict[str, str]]: + if self._definitions_by_name is None: + self._definitions_by_name = { + product_base_type_def["name"]: { + "type": "material-symbols", + "name": product_base_type_def.get("icon", "inventory_2"), + "color": product_base_type_def.get("color", "#cccccc"), + } + for product_base_type_def in self._definitions + } + return self._definitions_by_name + + def version_item_from_entity(version): version_attribs = version["attrib"] tags = version["tags"] @@ -84,42 +134,18 @@ def version_item_from_entity(version): def product_item_from_entity( product_entity: ProductDict, version_entities, - product_type_items_by_name: dict[str, ProductTypeItem], - product_base_type_items_by_name: dict[str, ProductBaseTypeItem], folder_label, + icons_mapping, product_in_scene, ): product_attribs = product_entity["attrib"] group = product_attribs.get("productGroup") product_type = product_entity["productType"] - product_type_item = product_type_items_by_name.get(product_type) - # NOTE This is needed for cases when products were not created on server - # using api functions. In that case product type item may not be - # available and we need to create a default. - if product_type_item is None: - product_type_item = create_default_product_type_item(product_type) - # Cache the item for future use - product_type_items_by_name[product_type] = product_type_item - product_base_type = product_entity.get("productBaseType") - product_base_type_item = product_base_type_items_by_name.get( - product_base_type) - # Same as for product type item above. Not sure if this is still needed - # though. - if product_base_type_item is None: - product_base_type_item = create_default_product_base_type_item( - product_base_type) - # Cache the item for future use - product_base_type_items_by_name[product_base_type] = ( - product_base_type_item) - product_type_icon = product_type_item.icon - product_base_type_icon = product_base_type_item.icon - product_icon = { - "type": "awesome-font", - "name": "fa.file-o", - "color": get_default_entity_icon_color(), - } + product_icon = icons_mapping.get_icon( + product_base_type, product_type + ) version_items = { version_entity["id"]: version_item_from_entity(version_entity) for version_entity in version_entities @@ -131,8 +157,6 @@ def product_item_from_entity( product_base_type=product_base_type, product_name=product_entity["name"], product_icon=product_icon, - product_type_icon=product_type_icon, - product_base_type_icon=product_base_type_icon, product_in_scene=product_in_scene, group_name=group, folder_id=product_entity["folderId"], @@ -141,22 +165,8 @@ def product_item_from_entity( ) -def product_type_item_from_data( - product_type_data: ProductDict) -> ProductTypeItem: - # TODO implement icon implementation - # icon = product_type_data["icon"] - # color = product_type_data["color"] - icon = { - "type": "awesome-font", - "name": "fa.folder", - "color": "#0091B2", - } - # TODO implement checked logic - return ProductTypeItem(product_type_data["name"], icon) - - def product_base_type_item_from_data( - product_base_type_data: ProductBaseTypeDict + product_base_type_data: ProductBaseTypeDict ) -> ProductBaseTypeItem: """Create product base type item from data. @@ -174,34 +184,8 @@ def product_base_type_item_from_data( } return ProductBaseTypeItem( name=product_base_type_data["name"], - icon=icon) - - -def create_default_product_type_item(product_type: str) -> ProductTypeItem: - icon = { - "type": "awesome-font", - "name": "fa.folder", - "color": "#0091B2", - } - return ProductTypeItem(product_type, icon) - - -def create_default_product_base_type_item( - product_base_type: str) -> ProductBaseTypeItem: - """Create default product base type item. - - Args: - product_base_type (str): Product base type name. - - Returns: - ProductBaseTypeItem: Default product base type item. - """ - icon = { - "type": "awesome-font", - "name": "fa.folder", - "color": "#0091B2", - } - return ProductBaseTypeItem(product_base_type, icon) + icon=icon + ) class ProductsModel: @@ -227,6 +211,8 @@ class ProductsModel: self._product_folder_ids_mapping = collections.defaultdict(dict) # Cache helpers + self._product_type_icons_mapping = NestedCacheItem( + levels=1, default_factory=list, lifetime=self.lifetime) self._product_type_items_cache = NestedCacheItem( levels=1, default_factory=list, lifetime=self.lifetime) self._product_base_type_items_cache = NestedCacheItem( @@ -243,11 +229,14 @@ class ProductsModel: self._version_item_by_id.clear() self._product_folder_ids_mapping.clear() + self._product_type_icons_mapping.reset() self._product_type_items_cache.reset() self._product_items_cache.reset() self._repre_items_cache.reset() - def get_product_type_items(self, project_name): + def get_product_type_items( + self, project_name: Optional[str] + ) -> list[ProductTypeItem]: """Product type items for project. Args: @@ -255,23 +244,27 @@ class ProductsModel: Returns: list[ProductTypeItem]: Product type items. - """ + """ if not project_name: return [] cache = self._product_type_items_cache[project_name] if not cache.is_valid: + icons_mapping = self._get_product_type_icons(project_name) product_types = ayon_api.get_project_product_types(project_name) cache.update_data([ - product_type_item_from_data(product_type) + ProductTypeItem( + product_type["name"], + icons_mapping.get_icon(product_type=product_type["name"]), + ) for product_type in product_types ]) return cache.get_data() def get_product_base_type_items( - self, - project_name: Optional[str]) -> list[ProductBaseTypeItem]: + self, project_name: Optional[str] + ) -> list[ProductBaseTypeItem]: """Product base type items for the project. Args: @@ -286,6 +279,7 @@ class ProductsModel: cache = self._product_base_type_items_cache[project_name] if not cache.is_valid: + icons_mapping = self._get_product_type_icons(project_name) product_base_types = [] # TODO add temp implementation here when it is actually # implemented and available on server. @@ -294,7 +288,10 @@ class ProductsModel: project_name ) cache.update_data([ - product_base_type_item_from_data(product_base_type) + ProductBaseTypeItem( + product_base_type["name"], + icons_mapping.get_icon(product_base_type["name"]), + ) for product_base_type in product_base_types ]) return cache.get_data() @@ -511,6 +508,27 @@ class ProductsModel: PRODUCTS_MODEL_SENDER ) + def _get_product_type_icons( + self, project_name: Optional[str] + ) -> ProductBaseTypeIconMapping: + cache = self._product_type_icons_mapping[project_name] + if cache.is_valid: + return cache.get_data() + + project_entity = self._controller.get_project_entity(project_name) + icons_mapping = ProductBaseTypeIconMapping() + if project_entity: + product_base_types = ( + project_entity["config"].get("productBaseTypes", {}) + ) + icons_mapping = ProductBaseTypeIconMapping( + product_base_types.get("default"), + product_base_types.get("definitions") + ) + + cache.update_data(icons_mapping) + return icons_mapping + def _get_product_items_by_id(self, project_name, product_ids): product_item_by_id = self._product_item_by_id[project_name] missing_product_ids = set() @@ -553,36 +571,18 @@ class ProductsModel: products: Iterable[ProductDict], versions: Iterable[VersionDict], folder_items=None, - product_type_items=None, - product_base_type_items: Optional[Iterable[ProductBaseTypeItem]] = None ): if folder_items is None: folder_items = self._controller.get_folder_items(project_name) - if product_type_items is None: - product_type_items = self.get_product_type_items(project_name) - - if product_base_type_items is None: - product_base_type_items = self.get_product_base_type_items( - project_name - ) - loaded_product_ids = self._controller.get_loaded_product_ids() versions_by_product_id = collections.defaultdict(list) for version in versions: versions_by_product_id[version["productId"]].append(version) - product_type_items_by_name = { - product_type_item.name: product_type_item - for product_type_item in product_type_items - } - - product_base_type_items_by_name: dict[str, ProductBaseTypeItem] = { - product_base_type_item.name: product_base_type_item - for product_base_type_item in product_base_type_items - } output: dict[str, ProductItem] = {} + icons_mapping = self._get_product_type_icons(project_name) for product in products: product_id = product["id"] folder_id = product["folderId"] @@ -595,9 +595,8 @@ class ProductsModel: product_item = product_item_from_entity( product, versions, - product_type_items_by_name, - product_base_type_items_by_name, folder_item.label, + icons_mapping, product_id in loaded_product_ids, ) output[product_id] = product_item diff --git a/client/ayon_core/tools/loader/ui/products_model.py b/client/ayon_core/tools/loader/ui/products_model.py index f3e5271f51..79ed197d83 100644 --- a/client/ayon_core/tools/loader/ui/products_model.py +++ b/client/ayon_core/tools/loader/ui/products_model.py @@ -17,7 +17,6 @@ PRODUCT_ID_ROLE = QtCore.Qt.UserRole + 6 PRODUCT_NAME_ROLE = QtCore.Qt.UserRole + 7 PRODUCT_TYPE_ROLE = QtCore.Qt.UserRole + 8 PRODUCT_BASE_TYPE_ROLE = QtCore.Qt.UserRole + 9 -PRODUCT_TYPE_ICON_ROLE = QtCore.Qt.UserRole + 10 PRODUCT_IN_SCENE_ROLE = QtCore.Qt.UserRole + 11 VERSION_ID_ROLE = QtCore.Qt.UserRole + 12 VERSION_HERO_ROLE = QtCore.Qt.UserRole + 13 @@ -228,10 +227,7 @@ class ProductsModel(QtGui.QStandardItemModel): return super().data(index, role) if role == QtCore.Qt.DecorationRole: - if col == 1: - role = PRODUCT_TYPE_ICON_ROLE - else: - return None + return None if ( role == VERSION_NAME_EDIT_ROLE @@ -455,7 +451,6 @@ class ProductsModel(QtGui.QStandardItemModel): model_item = QtGui.QStandardItem(product_item.product_name) model_item.setEditable(False) icon = get_qt_icon(product_item.product_icon) - product_type_icon = get_qt_icon(product_item.product_type_icon) model_item.setColumnCount(self.columnCount()) model_item.setData(icon, QtCore.Qt.DecorationRole) model_item.setData(product_id, PRODUCT_ID_ROLE) @@ -464,7 +459,6 @@ class ProductsModel(QtGui.QStandardItemModel): product_item.product_base_type, PRODUCT_BASE_TYPE_ROLE ) model_item.setData(product_item.product_type, PRODUCT_TYPE_ROLE) - model_item.setData(product_type_icon, PRODUCT_TYPE_ICON_ROLE) model_item.setData(product_item.folder_id, FOLDER_ID_ROLE) self._product_items_by_id[product_id] = product_item From d8045df9a7f6d82941f280d4ae9783b58557a7f7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 12 Sep 2025 18:33:03 +0200 Subject: [PATCH 16/26] use same deafult as backend has --- client/ayon_core/tools/loader/models/products.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/loader/models/products.py b/client/ayon_core/tools/loader/models/products.py index f5e0b2fa80..11468e4db0 100644 --- a/client/ayon_core/tools/loader/models/products.py +++ b/client/ayon_core/tools/loader/models/products.py @@ -58,7 +58,7 @@ class ProductBaseTypeIconMapping: if self._default_def is None: self._default_def = { "type": "material-symbols", - "name": self._default.get("icon", "inventory_2"), + "name": self._default.get("icon", "deployed_code"), "color": self._default.get("color", "#cccccc"), } @@ -69,7 +69,7 @@ class ProductBaseTypeIconMapping: self._definitions_by_name = { product_base_type_def["name"]: { "type": "material-symbols", - "name": product_base_type_def.get("icon", "inventory_2"), + "name": product_base_type_def.get("icon", "deployed_code"), "color": product_base_type_def.get("color", "#cccccc"), } for product_base_type_def in self._definitions From 7b92047a6bea0e10e3e3e8cb34d97129a75d72c7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 15 Sep 2025 11:38:58 +0200 Subject: [PATCH 17/26] move 'ProductTypeIconMapping' to common project model --- .../ayon_core/tools/common_models/__init__.py | 2 + .../ayon_core/tools/common_models/projects.py | 77 +++++++++++++++++- client/ayon_core/tools/loader/abstract.py | 20 +++-- client/ayon_core/tools/loader/control.py | 13 ++-- .../ayon_core/tools/loader/models/products.py | 78 ++----------------- 5 files changed, 108 insertions(+), 82 deletions(-) diff --git a/client/ayon_core/tools/common_models/__init__.py b/client/ayon_core/tools/common_models/__init__.py index ec69e20b64..77cc2dfb0f 100644 --- a/client/ayon_core/tools/common_models/__init__.py +++ b/client/ayon_core/tools/common_models/__init__.py @@ -10,6 +10,7 @@ from .projects import ( PROJECTS_MODEL_SENDER, FolderTypeItem, TaskTypeItem, + ProductTypeIconMapping, ) from .hierarchy import ( FolderItem, @@ -34,6 +35,7 @@ __all__ = ( "PROJECTS_MODEL_SENDER", "FolderTypeItem", "TaskTypeItem", + "ProductTypeIconMapping", "FolderItem", "TaskItem", diff --git a/client/ayon_core/tools/common_models/projects.py b/client/ayon_core/tools/common_models/projects.py index 034947de3a..710482a871 100644 --- a/client/ayon_core/tools/common_models/projects.py +++ b/client/ayon_core/tools/common_models/projects.py @@ -2,7 +2,7 @@ from __future__ import annotations import contextlib from abc import ABC, abstractmethod -from typing import Dict, Any +from typing import Any, Optional from dataclasses import dataclass import ayon_api @@ -51,7 +51,7 @@ class StatusItem: self.icon: str = icon self.state: str = state - def to_data(self) -> Dict[str, Any]: + def to_data(self) -> dict[str, Any]: return { "name": self.name, "color": self.color, @@ -218,6 +218,54 @@ class ProjectItem: return cls(**data) +class ProductTypeIconMapping: + def __init__( + self, + default: Optional[dict[str, str]] = None, + definitions: Optional[list[dict[str, str]]] = None, + ): + self._default = default or {} + self._definitions = definitions or [] + + self._default_def = None + self._definitions_by_name = None + + def get_icon( + self, + product_base_type: Optional[str] = None, + product_type: Optional[str] = None, + ) -> dict[str, str]: + defs = self._get_defs_by_name() + icon = defs.get(product_type) + if icon is None: + icon = defs.get(product_base_type) + if icon is None: + icon = self._get_default_def() + return icon.copy() + + def _get_default_def(self) -> dict[str, str]: + if self._default_def is None: + self._default_def = { + "type": "material-symbols", + "name": self._default.get("icon", "deployed_code"), + "color": self._default.get("color", "#cccccc"), + } + + return self._default_def + + def _get_defs_by_name(self) -> dict[str, dict[str, str]]: + if self._definitions_by_name is None: + self._definitions_by_name = { + product_base_type_def["name"]: { + "type": "material-symbols", + "name": product_base_type_def.get("icon", "deployed_code"), + "color": product_base_type_def.get("color", "#cccccc"), + } + for product_base_type_def in self._definitions + } + return self._definitions_by_name + + def _get_project_items_from_entitiy( projects: list[dict[str, Any]] ) -> list[ProjectItem]: @@ -242,6 +290,9 @@ class ProjectsModel(object): self._projects_by_name = NestedCacheItem( levels=1, default_factory=list ) + self._product_type_icons_mapping = NestedCacheItem( + levels=1, default_factory=ProductTypeIconMapping + ) self._project_statuses_cache = {} self._folder_types_cache = {} self._task_types_cache = {} @@ -255,6 +306,7 @@ class ProjectsModel(object): self._task_types_cache = {} self._projects_cache.reset() self._projects_by_name.reset() + self._product_type_icons_mapping.reset() def refresh(self): """Refresh project items. @@ -390,6 +442,27 @@ class ProjectsModel(object): self._task_type_items_getter, ) + def get_product_type_icons_mapping( + self, project_name: Optional[str] + ) -> ProductTypeIconMapping: + cache = self._product_type_icons_mapping[project_name] + if cache.is_valid: + return cache.get_data() + + project_entity = self.get_project_entity(project_name) + icons_mapping = ProductTypeIconMapping() + if project_entity: + product_base_types = ( + project_entity["config"].get("productBaseTypes", {}) + ) + icons_mapping = ProductTypeIconMapping( + product_base_types.get("default"), + product_base_types.get("definitions") + ) + + cache.update_data(icons_mapping) + return icons_mapping + def _get_project_items( self, project_name, sender, item_type, cache_obj, getter ): diff --git a/client/ayon_core/tools/loader/abstract.py b/client/ayon_core/tools/loader/abstract.py index 87cfb1f3f2..9c7934d2db 100644 --- a/client/ayon_core/tools/loader/abstract.py +++ b/client/ayon_core/tools/loader/abstract.py @@ -9,7 +9,11 @@ from ayon_core.lib.attribute_definitions import ( deserialize_attr_defs, serialize_attr_defs, ) -from ayon_core.tools.common_models import TaskItem, TagItem +from ayon_core.tools.common_models import ( + TaskItem, + TagItem, + ProductTypeIconMapping, +) class ProductTypeItem: @@ -492,8 +496,8 @@ class BackendLoaderController(_BaseLoaderController): topic (str): Event topic name. data (Optional[dict[str, Any]]): Event data. source (Optional[str]): Event source. - """ + """ pass @abstractmethod @@ -502,14 +506,20 @@ class BackendLoaderController(_BaseLoaderController): Returns: set[str]: Set of loaded product ids. - """ + """ pass @abstractmethod - def get_project_entity( + def get_product_type_icons_mapping( self, project_name: Optional[str] - ) -> Optional[dict[str, Any]]: + ) -> ProductTypeIconMapping: + """Product type icons mapping. + + Returns: + ProductTypeIconMapping: Product type icons mapping. + + """ pass diff --git a/client/ayon_core/tools/loader/control.py b/client/ayon_core/tools/loader/control.py index 492a05e82f..c7c182b192 100644 --- a/client/ayon_core/tools/loader/control.py +++ b/client/ayon_core/tools/loader/control.py @@ -17,6 +17,7 @@ from ayon_core.tools.common_models import ( HierarchyModel, ThumbnailsModel, TagItem, + ProductTypeIconMapping, ) from .abstract import ( @@ -189,11 +190,6 @@ class LoaderController(BackendLoaderController, FrontendLoaderController): def get_project_items(self, sender=None): return self._projects_model.get_project_items(sender) - def get_project_entity( - self, project_name: Optional[str] - ) -> Optional[dict[str, Any]]: - return self._projects_model.get_project_entity(project_name) - def get_folder_type_items(self, project_name, sender=None): return self._projects_model.get_folder_type_items( project_name, sender @@ -204,6 +200,13 @@ class LoaderController(BackendLoaderController, FrontendLoaderController): project_name, sender ) + def get_product_type_icons_mapping( + self, project_name: Optional[str] + ) -> ProductTypeIconMapping: + return self._projects_model.get_product_type_icons_mapping( + project_name + ) + def get_folder_items(self, project_name, sender=None): return self._hierarchy_model.get_folder_items(project_name, sender) diff --git a/client/ayon_core/tools/loader/models/products.py b/client/ayon_core/tools/loader/models/products.py index 11468e4db0..7915a75bcf 100644 --- a/client/ayon_core/tools/loader/models/products.py +++ b/client/ayon_core/tools/loader/models/products.py @@ -11,6 +11,7 @@ from ayon_api.operations import OperationsSession from ayon_core.lib import NestedCacheItem from ayon_core.style import get_default_entity_icon_color +from ayon_core.tools.common_models import ProductTypeIconMapping from ayon_core.tools.loader.abstract import ( ProductTypeItem, ProductBaseTypeItem, @@ -29,54 +30,6 @@ if TYPE_CHECKING: PRODUCTS_MODEL_SENDER = "products.model" -class ProductBaseTypeIconMapping: - def __init__( - self, - default: Optional[dict[str, str]] = None, - definitions: Optional[list[dict[str, str]]] = None, - ): - self._default = default or {} - self._definitions = definitions or [] - - self._default_def = None - self._definitions_by_name = None - - def get_icon( - self, - product_base_type: Optional[str] = None, - product_type: Optional[str] = None, - ) -> dict[str, str]: - defs = self._get_defs_by_name() - icon = defs.get(product_type) - if icon is None: - icon = defs.get(product_base_type) - if icon is None: - icon = self._get_default_def() - return icon.copy() - - def _get_default_def(self) -> dict[str, str]: - if self._default_def is None: - self._default_def = { - "type": "material-symbols", - "name": self._default.get("icon", "deployed_code"), - "color": self._default.get("color", "#cccccc"), - } - - return self._default_def - - def _get_defs_by_name(self) -> dict[str, dict[str, str]]: - if self._definitions_by_name is None: - self._definitions_by_name = { - product_base_type_def["name"]: { - "type": "material-symbols", - "name": product_base_type_def.get("icon", "deployed_code"), - "color": product_base_type_def.get("color", "#cccccc"), - } - for product_base_type_def in self._definitions - } - return self._definitions_by_name - - def version_item_from_entity(version): version_attribs = version["attrib"] tags = version["tags"] @@ -211,8 +164,6 @@ class ProductsModel: self._product_folder_ids_mapping = collections.defaultdict(dict) # Cache helpers - self._product_type_icons_mapping = NestedCacheItem( - levels=1, default_factory=list, lifetime=self.lifetime) self._product_type_items_cache = NestedCacheItem( levels=1, default_factory=list, lifetime=self.lifetime) self._product_base_type_items_cache = NestedCacheItem( @@ -229,7 +180,6 @@ class ProductsModel: self._version_item_by_id.clear() self._product_folder_ids_mapping.clear() - self._product_type_icons_mapping.reset() self._product_type_items_cache.reset() self._product_items_cache.reset() self._repre_items_cache.reset() @@ -267,6 +217,10 @@ class ProductsModel: ) -> list[ProductBaseTypeItem]: """Product base type items for the project. + Notes: + This will be used for filtering product types in UI when + product base types are fully implemented. + Args: project_name (optional, str): Project name. @@ -510,24 +464,8 @@ class ProductsModel: def _get_product_type_icons( self, project_name: Optional[str] - ) -> ProductBaseTypeIconMapping: - cache = self._product_type_icons_mapping[project_name] - if cache.is_valid: - return cache.get_data() - - project_entity = self._controller.get_project_entity(project_name) - icons_mapping = ProductBaseTypeIconMapping() - if project_entity: - product_base_types = ( - project_entity["config"].get("productBaseTypes", {}) - ) - icons_mapping = ProductBaseTypeIconMapping( - product_base_types.get("default"), - product_base_types.get("definitions") - ) - - cache.update_data(icons_mapping) - return icons_mapping + ) -> ProductTypeIconMapping: + return self._controller.get_product_type_icons_mapping(project_name) def _get_product_items_by_id(self, project_name, product_ids): product_item_by_id = self._product_item_by_id[project_name] @@ -542,7 +480,7 @@ class ProductsModel: output.update( self._query_product_items_by_ids( - project_name, missing_product_ids + project_name, product_ids=missing_product_ids ) ) return output From cf874e8f0a274b8a38ff8221d7f3d1a567095286 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 15 Sep 2025 11:46:28 +0200 Subject: [PATCH 18/26] use product type icon in scene inventory --- .../ayon_core/tools/sceneinventory/control.py | 15 ++++++++++- .../ayon_core/tools/sceneinventory/model.py | 5 +--- .../tools/sceneinventory/models/containers.py | 25 ++++++++++++++++++- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/control.py b/client/ayon_core/tools/sceneinventory/control.py index 45f76a54ac..606c9e7298 100644 --- a/client/ayon_core/tools/sceneinventory/control.py +++ b/client/ayon_core/tools/sceneinventory/control.py @@ -1,3 +1,5 @@ +from typing import Optional + import ayon_api from ayon_core.lib.events import QueuedEventSystem @@ -6,7 +8,11 @@ from ayon_core.pipeline import ( registered_host, get_current_context, ) -from ayon_core.tools.common_models import HierarchyModel, ProjectsModel +from ayon_core.tools.common_models import ( + HierarchyModel, + ProjectsModel, + ProductTypeIconMapping, +) from .models import SiteSyncModel, ContainersModel @@ -93,6 +99,13 @@ class SceneInventoryController: project_name, None ) + def get_product_type_icons_mapping( + self, project_name: Optional[str] + ) -> ProductTypeIconMapping: + return self._projects_model.get_product_type_icons_mapping( + project_name + ) + # Containers methods def get_containers(self): return self._containers_model.get_containers() diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index 885553acaf..9977acea21 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -214,9 +214,6 @@ class InventoryModel(QtGui.QStandardItemModel): group_icon = qtawesome.icon( "fa.object-group", color=self._default_icon_color ) - product_type_icon = qtawesome.icon( - "fa.folder", color="#0091B2" - ) group_item_font = QtGui.QFont() group_item_font.setBold(True) @@ -303,7 +300,7 @@ class InventoryModel(QtGui.QStandardItemModel): remote_site_progress = "{}%".format( max(progress["remote_site"], 0) * 100 ) - + product_type_icon = get_qt_icon(repre_info.product_type_icon) group_item = QtGui.QStandardItem() group_item.setColumnCount(root_item.columnCount()) group_item.setData(group_name, QtCore.Qt.DisplayRole) diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index f841f87c8e..47f74476de 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -126,6 +126,7 @@ class RepresentationInfo: product_id, product_name, product_type, + product_type_icon, product_group, version_id, representation_name, @@ -135,6 +136,7 @@ class RepresentationInfo: self.product_id = product_id self.product_name = product_name self.product_type = product_type + self.product_type_icon = product_type_icon self.product_group = product_group self.version_id = version_id self.representation_name = representation_name @@ -153,7 +155,17 @@ class RepresentationInfo: @classmethod def new_invalid(cls): - return cls(None, None, None, None, None, None, None, None) + return cls( + None, + None, + None, + None, + None, + None, + None, + None, + None, + ) class VersionItem: @@ -229,6 +241,9 @@ class ContainersModel: def get_representation_info_items(self, project_name, representation_ids): output = {} missing_repre_ids = set() + icons_mapping = self._controller.get_product_type_icons_mapping( + project_name + ) for repre_id in representation_ids: try: uuid.UUID(repre_id) @@ -253,6 +268,7 @@ class ContainersModel: "product_id": None, "product_name": None, "product_type": None, + "product_type_icon": None, "product_group": None, "version_id": None, "representation_name": None, @@ -265,10 +281,17 @@ class ContainersModel: kwargs["folder_id"] = folder["id"] kwargs["folder_path"] = folder["path"] if product: + product_type = product["productType"] + product_base_type = product.get("productBaseType") + icon = icons_mapping.get_icon( + product_base_type=product_base_type, + product_type=product_type, + ) group = product["attrib"]["productGroup"] kwargs["product_id"] = product["id"] kwargs["product_name"] = product["name"] kwargs["product_type"] = product["productType"] + kwargs["product_type_icon"] = icon kwargs["product_group"] = group if version: kwargs["version_id"] = version["id"] From 31469bfd9a0b7d29b88ce9162cfb297fc1c4aef5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 15 Sep 2025 11:52:37 +0200 Subject: [PATCH 19/26] remove unused import --- client/ayon_core/tools/loader/control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/loader/control.py b/client/ayon_core/tools/loader/control.py index c7c182b192..9f159bfb21 100644 --- a/client/ayon_core/tools/loader/control.py +++ b/client/ayon_core/tools/loader/control.py @@ -2,7 +2,7 @@ from __future__ import annotations import logging import uuid -from typing import Optional, Any +from typing import Optional import ayon_api From 7082905e5e25524c16d0bd5a7376eb010d025975 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 15 Sep 2025 12:04:45 +0200 Subject: [PATCH 20/26] use icon color from project anatomy for tasks --- client/ayon_core/tools/common_models/projects.py | 15 ++++++++++++--- client/ayon_core/tools/utils/tasks_widget.py | 3 ++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/tools/common_models/projects.py b/client/ayon_core/tools/common_models/projects.py index 034947de3a..7a5620b033 100644 --- a/client/ayon_core/tools/common_models/projects.py +++ b/client/ayon_core/tools/common_models/projects.py @@ -2,7 +2,7 @@ from __future__ import annotations import contextlib from abc import ABC, abstractmethod -from typing import Dict, Any +from typing import Any, Optional from dataclasses import dataclass import ayon_api @@ -51,7 +51,7 @@ class StatusItem: self.icon: str = icon self.state: str = state - def to_data(self) -> Dict[str, Any]: + def to_data(self) -> dict[str, Any]: return { "name": self.name, "color": self.color, @@ -125,16 +125,24 @@ class TaskTypeItem: icon (str): Icon name in MaterialIcons ("fiber_new"). """ - def __init__(self, name, short, icon): + def __init__( + self, + name: str, + short: str, + icon: str, + color: Optional[str], + ): self.name = name self.short = short self.icon = icon + self.color = color def to_data(self): return { "name": self.name, "short": self.short, "icon": self.icon, + "color": self.color, } @classmethod @@ -147,6 +155,7 @@ class TaskTypeItem: name=task_type_data["name"], short=task_type_data["shortName"], icon=task_type_data["icon"], + color=task_type_data.get("color"), ) diff --git a/client/ayon_core/tools/utils/tasks_widget.py b/client/ayon_core/tools/utils/tasks_widget.py index 744eb6060a..d77ce1e1f4 100644 --- a/client/ayon_core/tools/utils/tasks_widget.py +++ b/client/ayon_core/tools/utils/tasks_widget.py @@ -234,10 +234,11 @@ class TasksQtModel(QtGui.QStandardItemModel): ) icon = None if task_type_item is not None: + color = task_type_item.color or get_default_entity_icon_color() icon = get_qt_icon({ "type": "material-symbols", "name": task_type_item.icon, - "color": get_default_entity_icon_color() + "color": color, }) if icon is None: From 8979fc7608b81db11740738c17c6f581ae1dca1e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 15 Sep 2025 13:54:22 +0200 Subject: [PATCH 21/26] elite status name --- client/ayon_core/tools/utils/delegates.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/utils/delegates.py b/client/ayon_core/tools/utils/delegates.py index 1cc18b5722..059fc1da0e 100644 --- a/client/ayon_core/tools/utils/delegates.py +++ b/client/ayon_core/tools/utils/delegates.py @@ -186,8 +186,15 @@ class StatusDelegate(QtWidgets.QStyledItemDelegate): ) fm = QtGui.QFontMetrics(option.font) if text_rect.width() < fm.width(text): - text = self._get_status_short_name(index) - if text_rect.width() < fm.width(text): + short_text = self._get_status_short_name(index) + if short_text: + text = short_text + + text = fm.elidedText( + text, QtCore.Qt.ElideRight, text_rect.width() + ) + # Allow at least one character + if len(text) < 2: text = "" fg_color = self._get_status_color(index) From 00603d05a4014c1871e742d8dee5c12dae1f7166 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:17:27 +0200 Subject: [PATCH 22/26] fill task type color in publisher's tasks enum --- .../tools/publisher/widgets/tasks_model.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/tasks_model.py b/client/ayon_core/tools/publisher/widgets/tasks_model.py index 8bfa81116a..1803e46c5f 100644 --- a/client/ayon_core/tools/publisher/widgets/tasks_model.py +++ b/client/ayon_core/tools/publisher/widgets/tasks_model.py @@ -146,19 +146,19 @@ class TasksModel(QtGui.QStandardItemModel): self._controller.get_current_project_name() ) } - icon_name_by_task_name = {} + type_item_by_task_name = {} for task_items in task_items_by_folder_path.values(): for task_item in task_items: task_name = task_item.name if ( task_name not in new_task_names - or task_name in icon_name_by_task_name + or task_name in type_item_by_task_name ): continue task_type_name = task_item.task_type task_type_item = task_type_items.get(task_type_name) if task_type_item: - icon_name_by_task_name[task_name] = task_type_item.icon + type_item_by_task_name[task_name] = task_type_item for task_name in new_task_names: item = self._items_by_name.get(task_name) @@ -171,13 +171,18 @@ class TasksModel(QtGui.QStandardItemModel): if not task_name: continue - icon_name = icon_name_by_task_name.get(task_name) - icon = None + icon = icon_name = icon_color = None + task_type_item = type_item_by_task_name.get(task_name) + if task_type_item is not None: + icon_name = task_type_item.icon + icon_color = task_type_item.color if icon_name: + if not icon_color: + icon_color = get_default_entity_icon_color() icon = get_qt_icon({ "type": "material-symbols", "name": icon_name, - "color": get_default_entity_icon_color(), + "color": icon_color, }) if icon is None: icon = default_icon From 216c06437fe2ea50d58a56f5d917f6d962818f28 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Mon, 15 Sep 2025 19:20:10 +0300 Subject: [PATCH 23/26] MK Docs: Update light/dark modes icons --- mkdocs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 8e4c2663bc..a3b89b5455 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -11,12 +11,12 @@ theme: - media: "(prefers-color-scheme: dark)" scheme: slate toggle: - icon: material/toggle-switch-off-outline + icon: material/weather-sunny name: Switch to light mode - media: "(prefers-color-scheme: light)" scheme: default toggle: - icon: material/toggle-switch + icon: material/weather-night name: Switch to dark mode logo: img/ay-symbol-blackw-full.png favicon: img/favicon.ico From 99c50c3a14a96a5b227a1f27c17191ce8ade0d73 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 16 Sep 2025 11:54:01 +0300 Subject: [PATCH 24/26] move mkdocs_requirements to root location. --- docs/mkdocs_requirements.txt => mkdocs_requirements.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/mkdocs_requirements.txt => mkdocs_requirements.txt (100%) diff --git a/docs/mkdocs_requirements.txt b/mkdocs_requirements.txt similarity index 100% rename from docs/mkdocs_requirements.txt rename to mkdocs_requirements.txt From eaa6727a9d0ae5289961b5b46d9fdf9245e6c93a Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 16 Sep 2025 11:56:00 +0300 Subject: [PATCH 25/26] CI: Re-use `deploy_mkdocs` from ynput/ops-repo-automation --- .github/workflows/deploy_mkdocs.yml | 69 ++++------------------------- 1 file changed, 8 insertions(+), 61 deletions(-) diff --git a/.github/workflows/deploy_mkdocs.yml b/.github/workflows/deploy_mkdocs.yml index 2f46bdc6ad..1e02643a3d 100644 --- a/.github/workflows/deploy_mkdocs.yml +++ b/.github/workflows/deploy_mkdocs.yml @@ -1,70 +1,17 @@ name: Deploy MkDocs on: - pull_request: + push: + tags: + - "*" workflow_dispatch: - workflow_call: - inputs: - repo: - type: string - required: true - branch_name: - type: string - required: true - default: "main" - secrets: - token: - required: true - user: - required: true - email: - required: true - -env: - GH_TOKEN: ${{ secrets.token || secrets.YNPUT_BOT_TOKEN }} - GH_USER: ${{ secrets.user || secrets.CI_USER }} - GH_EMAIL: ${{ secrets.email || secrets.CI_EMAIL }} jobs: - verify-repo-secrets: - uses: ynput/ops-repo-automation/.github/workflows/verify_secrets.yml@main + build-mk-docs: + uses: ynput/ops-repo-automation/.github/workflows/deploy_mkdocs.yml@feature/34-add-mk-docs-reusable-workflow with: repo: ${{ github.repository }} secrets: - gh_token: ${{ secrets.token }} - gh_user: ${{ secrets.user }} - gh_email: ${{ secrets.email }} - - deploy: - runs-on: ubuntu-latest - - steps: - - name: Checkout ${{ inputs.branch_name}} - uses: actions/checkout@v4 - with: - ref: ${{ inputs.branch_name}} - fetch-depth: 0 - submodules: true - - - name: 🔑 Set Authentication - run: | - git config --global user.name "${{ secrets.user || secrets.CI_USER }}" - git config --global user.email "${{ secrets.email || secrets.CI_EMAIL }}" - - - name: Get current tag - id: git_tag - uses: devops-actions/action-get-tag@v1.0.3 - with: - default: 1.0.0 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: "3.x" - - - name: Install dependencies - run: | - python3 -m pip install -r ./docs/mkdocs_requirements.txt - - - name: Mike deploy ${{ steps.git_tag.outputs.tag }} - run: mike deploy --update-aliases ${{ steps.git_tag.outputs.tag }} latest + YNPUT_BOT_TOKEN: ${{ secrets.YNPUT_BOT_TOKEN }} + CI_USER: ${{ secrets.CI_USER }} + CI_EMAIL: ${{ secrets.CI_EMAIL }} From a46a9e3143998c93d04824d3aa795e8f53b580d8 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 16 Sep 2025 13:04:54 +0300 Subject: [PATCH 26/26] CI: Update Deploy MkDocs to use action from develop branch from ynput/ops-repo-automation --- .github/workflows/deploy_mkdocs.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy_mkdocs.yml b/.github/workflows/deploy_mkdocs.yml index 1e02643a3d..deafc7b850 100644 --- a/.github/workflows/deploy_mkdocs.yml +++ b/.github/workflows/deploy_mkdocs.yml @@ -8,7 +8,8 @@ on: jobs: build-mk-docs: - uses: ynput/ops-repo-automation/.github/workflows/deploy_mkdocs.yml@feature/34-add-mk-docs-reusable-workflow + # FIXME: Update @develop to @main after `ops-repo-automation` release. + uses: ynput/ops-repo-automation/.github/workflows/deploy_mkdocs.yml@develop with: repo: ${{ github.repository }} secrets: