move 'ProductTypeIconMapping' to common project model

This commit is contained in:
Jakub Trllo 2025-09-15 11:38:58 +02:00
parent d8045df9a7
commit 7b92047a6b
5 changed files with 108 additions and 82 deletions

View file

@ -10,6 +10,7 @@ from .projects import (
PROJECTS_MODEL_SENDER, PROJECTS_MODEL_SENDER,
FolderTypeItem, FolderTypeItem,
TaskTypeItem, TaskTypeItem,
ProductTypeIconMapping,
) )
from .hierarchy import ( from .hierarchy import (
FolderItem, FolderItem,
@ -34,6 +35,7 @@ __all__ = (
"PROJECTS_MODEL_SENDER", "PROJECTS_MODEL_SENDER",
"FolderTypeItem", "FolderTypeItem",
"TaskTypeItem", "TaskTypeItem",
"ProductTypeIconMapping",
"FolderItem", "FolderItem",
"TaskItem", "TaskItem",

View file

@ -2,7 +2,7 @@ from __future__ import annotations
import contextlib import contextlib
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Dict, Any from typing import Any, Optional
from dataclasses import dataclass from dataclasses import dataclass
import ayon_api import ayon_api
@ -51,7 +51,7 @@ class StatusItem:
self.icon: str = icon self.icon: str = icon
self.state: str = state self.state: str = state
def to_data(self) -> Dict[str, Any]: def to_data(self) -> dict[str, Any]:
return { return {
"name": self.name, "name": self.name,
"color": self.color, "color": self.color,
@ -218,6 +218,54 @@ class ProjectItem:
return cls(**data) 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( def _get_project_items_from_entitiy(
projects: list[dict[str, Any]] projects: list[dict[str, Any]]
) -> list[ProjectItem]: ) -> list[ProjectItem]:
@ -242,6 +290,9 @@ class ProjectsModel(object):
self._projects_by_name = NestedCacheItem( self._projects_by_name = NestedCacheItem(
levels=1, default_factory=list levels=1, default_factory=list
) )
self._product_type_icons_mapping = NestedCacheItem(
levels=1, default_factory=ProductTypeIconMapping
)
self._project_statuses_cache = {} self._project_statuses_cache = {}
self._folder_types_cache = {} self._folder_types_cache = {}
self._task_types_cache = {} self._task_types_cache = {}
@ -255,6 +306,7 @@ class ProjectsModel(object):
self._task_types_cache = {} self._task_types_cache = {}
self._projects_cache.reset() self._projects_cache.reset()
self._projects_by_name.reset() self._projects_by_name.reset()
self._product_type_icons_mapping.reset()
def refresh(self): def refresh(self):
"""Refresh project items. """Refresh project items.
@ -390,6 +442,27 @@ class ProjectsModel(object):
self._task_type_items_getter, 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( def _get_project_items(
self, project_name, sender, item_type, cache_obj, getter self, project_name, sender, item_type, cache_obj, getter
): ):

View file

@ -9,7 +9,11 @@ from ayon_core.lib.attribute_definitions import (
deserialize_attr_defs, deserialize_attr_defs,
serialize_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: class ProductTypeItem:
@ -492,8 +496,8 @@ class BackendLoaderController(_BaseLoaderController):
topic (str): Event topic name. topic (str): Event topic name.
data (Optional[dict[str, Any]]): Event data. data (Optional[dict[str, Any]]): Event data.
source (Optional[str]): Event source. source (Optional[str]): Event source.
"""
"""
pass pass
@abstractmethod @abstractmethod
@ -502,14 +506,20 @@ class BackendLoaderController(_BaseLoaderController):
Returns: Returns:
set[str]: Set of loaded product ids. set[str]: Set of loaded product ids.
"""
"""
pass pass
@abstractmethod @abstractmethod
def get_project_entity( def get_product_type_icons_mapping(
self, project_name: Optional[str] self, project_name: Optional[str]
) -> Optional[dict[str, Any]]: ) -> ProductTypeIconMapping:
"""Product type icons mapping.
Returns:
ProductTypeIconMapping: Product type icons mapping.
"""
pass pass

View file

@ -17,6 +17,7 @@ from ayon_core.tools.common_models import (
HierarchyModel, HierarchyModel,
ThumbnailsModel, ThumbnailsModel,
TagItem, TagItem,
ProductTypeIconMapping,
) )
from .abstract import ( from .abstract import (
@ -189,11 +190,6 @@ class LoaderController(BackendLoaderController, FrontendLoaderController):
def get_project_items(self, sender=None): def get_project_items(self, sender=None):
return self._projects_model.get_project_items(sender) 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): def get_folder_type_items(self, project_name, sender=None):
return self._projects_model.get_folder_type_items( return self._projects_model.get_folder_type_items(
project_name, sender project_name, sender
@ -204,6 +200,13 @@ class LoaderController(BackendLoaderController, FrontendLoaderController):
project_name, sender 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): def get_folder_items(self, project_name, sender=None):
return self._hierarchy_model.get_folder_items(project_name, sender) return self._hierarchy_model.get_folder_items(project_name, sender)

View file

@ -11,6 +11,7 @@ from ayon_api.operations import OperationsSession
from ayon_core.lib import NestedCacheItem from ayon_core.lib import NestedCacheItem
from ayon_core.style import get_default_entity_icon_color 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 ( from ayon_core.tools.loader.abstract import (
ProductTypeItem, ProductTypeItem,
ProductBaseTypeItem, ProductBaseTypeItem,
@ -29,54 +30,6 @@ if TYPE_CHECKING:
PRODUCTS_MODEL_SENDER = "products.model" 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): def version_item_from_entity(version):
version_attribs = version["attrib"] version_attribs = version["attrib"]
tags = version["tags"] tags = version["tags"]
@ -211,8 +164,6 @@ class ProductsModel:
self._product_folder_ids_mapping = collections.defaultdict(dict) self._product_folder_ids_mapping = collections.defaultdict(dict)
# Cache helpers # Cache helpers
self._product_type_icons_mapping = NestedCacheItem(
levels=1, default_factory=list, lifetime=self.lifetime)
self._product_type_items_cache = NestedCacheItem( self._product_type_items_cache = NestedCacheItem(
levels=1, default_factory=list, lifetime=self.lifetime) levels=1, default_factory=list, lifetime=self.lifetime)
self._product_base_type_items_cache = NestedCacheItem( self._product_base_type_items_cache = NestedCacheItem(
@ -229,7 +180,6 @@ class ProductsModel:
self._version_item_by_id.clear() self._version_item_by_id.clear()
self._product_folder_ids_mapping.clear() self._product_folder_ids_mapping.clear()
self._product_type_icons_mapping.reset()
self._product_type_items_cache.reset() self._product_type_items_cache.reset()
self._product_items_cache.reset() self._product_items_cache.reset()
self._repre_items_cache.reset() self._repre_items_cache.reset()
@ -267,6 +217,10 @@ class ProductsModel:
) -> list[ProductBaseTypeItem]: ) -> list[ProductBaseTypeItem]:
"""Product base type items for the project. """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: Args:
project_name (optional, str): Project name. project_name (optional, str): Project name.
@ -510,24 +464,8 @@ class ProductsModel:
def _get_product_type_icons( def _get_product_type_icons(
self, project_name: Optional[str] self, project_name: Optional[str]
) -> ProductBaseTypeIconMapping: ) -> ProductTypeIconMapping:
cache = self._product_type_icons_mapping[project_name] return self._controller.get_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): def _get_product_items_by_id(self, project_name, product_ids):
product_item_by_id = self._product_item_by_id[project_name] product_item_by_id = self._product_item_by_id[project_name]
@ -542,7 +480,7 @@ class ProductsModel:
output.update( output.update(
self._query_product_items_by_ids( self._query_product_items_by_ids(
project_name, missing_product_ids project_name, product_ids=missing_product_ids
) )
) )
return output return output