mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
🔧 WIP on product base type support in loader tool
This commit is contained in:
parent
fac933c16a
commit
6ea717bc36
2 changed files with 226 additions and 46 deletions
|
|
@ -1,5 +1,6 @@
|
|||
from __future__ import annotations
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import List
|
||||
from typing import List, Optional, TypedDict
|
||||
|
||||
from ayon_core.lib.attribute_definitions import (
|
||||
AbstractAttrDef,
|
||||
|
|
@ -8,15 +9,62 @@ from ayon_core.lib.attribute_definitions import (
|
|||
)
|
||||
|
||||
|
||||
IconData = TypedDict("IconData", {
|
||||
"type": str,
|
||||
"name": str,
|
||||
"color": str
|
||||
})
|
||||
|
||||
ProductBaseTypeItemData = TypedDict("ProductBaseTypeItemData", {
|
||||
"name": str,
|
||||
"icon": IconData
|
||||
})
|
||||
|
||||
|
||||
VersionItemData = TypedDict("VersionItemData", {
|
||||
"version_id": str,
|
||||
"version": int,
|
||||
"is_hero": bool,
|
||||
"product_id": str,
|
||||
"task_id": Optional[str],
|
||||
"thumbnail_id": Optional[str],
|
||||
"published_time": Optional[str],
|
||||
"author": Optional[str],
|
||||
"status": Optional[str],
|
||||
"frame_range": Optional[str],
|
||||
"duration": Optional[int],
|
||||
"handles": Optional[str],
|
||||
"step": Optional[int],
|
||||
"comment": Optional[str],
|
||||
"source": Optional[str]
|
||||
})
|
||||
|
||||
|
||||
ProductItemData = TypedDict("ProductItemData", {
|
||||
"product_id": str,
|
||||
"product_type": str,
|
||||
"product_base_type": str,
|
||||
"product_name": str,
|
||||
"product_icon": IconData,
|
||||
"product_type_icon": IconData,
|
||||
"product_base_type_icon": IconData,
|
||||
"group_name": str,
|
||||
"folder_id": str,
|
||||
"folder_label": str,
|
||||
"version_items": dict[str, VersionItemData],
|
||||
"product_in_scene": bool
|
||||
})
|
||||
|
||||
|
||||
class ProductTypeItem:
|
||||
"""Item representing product type.
|
||||
|
||||
Args:
|
||||
name (str): Product type name.
|
||||
icon (dict[str, Any]): Product type icon definition.
|
||||
icon (IconData): Product type icon definition.
|
||||
"""
|
||||
|
||||
def __init__(self, name, icon):
|
||||
def __init__(self, name: str, icon: IconData):
|
||||
self.name = name
|
||||
self.icon = icon
|
||||
|
||||
|
|
@ -31,6 +79,24 @@ class ProductTypeItem:
|
|||
return cls(**data)
|
||||
|
||||
|
||||
class ProductBaseTypeItem:
|
||||
"""Item representing product base type."""
|
||||
|
||||
def __init__(self, name: str, icon: IconData):
|
||||
self.name = name
|
||||
self.icon = icon
|
||||
|
||||
def to_data(self) -> ProductBaseTypeItemData:
|
||||
return {
|
||||
"name": self.name,
|
||||
"icon": self.icon,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_data(cls, data: ProductBaseTypeItemData):
|
||||
return cls(**data)
|
||||
|
||||
|
||||
class ProductItem:
|
||||
"""Product item with it versions.
|
||||
|
||||
|
|
@ -38,8 +104,8 @@ class ProductItem:
|
|||
product_id (str): Product id.
|
||||
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_icon (IconData): Product icon definition.
|
||||
product_type_icon (IconData): 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.
|
||||
|
|
@ -49,35 +115,42 @@ class ProductItem:
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
product_id,
|
||||
product_type,
|
||||
product_name,
|
||||
product_icon,
|
||||
product_type_icon,
|
||||
product_in_scene,
|
||||
group_name,
|
||||
folder_id,
|
||||
folder_label,
|
||||
version_items,
|
||||
product_id: str,
|
||||
product_type: str,
|
||||
product_base_type: str,
|
||||
product_name: str,
|
||||
product_icon: IconData,
|
||||
product_type_icon: IconData,
|
||||
product_base_type_icon: IconData,
|
||||
group_name: str,
|
||||
folder_id: str,
|
||||
folder_label: str,
|
||||
version_items: dict[str, VersionItem],
|
||||
*,
|
||||
product_in_scene: bool,
|
||||
):
|
||||
self.product_id = product_id
|
||||
self.product_type = product_type
|
||||
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
|
||||
self.folder_label = folder_label
|
||||
self.version_items = version_items
|
||||
|
||||
def to_data(self):
|
||||
def to_data(self) -> ProductItemData:
|
||||
return {
|
||||
"product_id": self.product_id,
|
||||
"product_type": self.product_type,
|
||||
"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,
|
||||
|
|
@ -124,21 +197,22 @@ class VersionItem:
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
version_id,
|
||||
version,
|
||||
is_hero,
|
||||
product_id,
|
||||
task_id,
|
||||
thumbnail_id,
|
||||
published_time,
|
||||
author,
|
||||
status,
|
||||
frame_range,
|
||||
duration,
|
||||
handles,
|
||||
step,
|
||||
comment,
|
||||
source,
|
||||
*,
|
||||
version_id: str,
|
||||
version: int,
|
||||
is_hero: bool,
|
||||
product_id: str,
|
||||
task_id: Optional[str] = None,
|
||||
thumbnail_id: Optional[str] = None,
|
||||
published_time: Optional[str] = None,
|
||||
author: Optional[str] = None,
|
||||
status: Optional[str] = None,
|
||||
frame_range: Optional[str] = None,
|
||||
duration: Optional[int] = None,
|
||||
handles: Optional[str] = None,
|
||||
step: Optional[int] = None,
|
||||
comment: Optional[str] = None,
|
||||
source: Optional[str] = None,
|
||||
):
|
||||
self.version_id = version_id
|
||||
self.product_id = product_id
|
||||
|
|
@ -198,7 +272,7 @@ class VersionItem:
|
|||
def __le__(self, other):
|
||||
return self.__eq__(other) or self.__lt__(other)
|
||||
|
||||
def to_data(self):
|
||||
def to_data(self) -> VersionItemData:
|
||||
return {
|
||||
"version_id": self.version_id,
|
||||
"product_id": self.product_id,
|
||||
|
|
@ -218,7 +292,7 @@ class VersionItem:
|
|||
}
|
||||
|
||||
@classmethod
|
||||
def from_data(cls, data):
|
||||
def from_data(cls, data: VersionItemData):
|
||||
return cls(**data)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,29 @@
|
|||
"""Products model for loader tools."""
|
||||
from __future__ import annotations
|
||||
import collections
|
||||
import contextlib
|
||||
from typing import TYPE_CHECKING, Iterable, Optional
|
||||
|
||||
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 (
|
||||
IconData,
|
||||
ProductTypeItem,
|
||||
ProductBaseTypeItem,
|
||||
ProductItem,
|
||||
VersionItem,
|
||||
RepreItem,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ayon_api.typing import ProductBaseTypeDict, ProductDict, VersionDict
|
||||
|
||||
|
||||
PRODUCTS_MODEL_SENDER = "products.model"
|
||||
|
||||
|
||||
|
|
@ -70,9 +80,10 @@ def version_item_from_entity(version):
|
|||
|
||||
|
||||
def product_item_from_entity(
|
||||
product_entity,
|
||||
product_entity: ProductDict,
|
||||
version_entities,
|
||||
product_type_items_by_name,
|
||||
product_type_items_by_name: dict[str, ProductTypeItem],
|
||||
product_base_type_items_by_name: dict[str, ProductBaseTypeItem],
|
||||
folder_label,
|
||||
product_in_scene,
|
||||
):
|
||||
|
|
@ -88,9 +99,21 @@ def product_item_from_entity(
|
|||
# Cache the item for future use
|
||||
product_type_items_by_name[product_type] = product_type_item
|
||||
|
||||
product_type_icon = product_type_item.icon
|
||||
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_icon = {
|
||||
product_type_icon = product_type_item.icon
|
||||
product_base_type_icon = product_base_type_item.icon
|
||||
product_icon: IconData = {
|
||||
"type": "awesome-font",
|
||||
"name": "fa.file-o",
|
||||
"color": get_default_entity_icon_color(),
|
||||
|
|
@ -103,9 +126,11 @@ def product_item_from_entity(
|
|||
return ProductItem(
|
||||
product_id=product_entity["id"],
|
||||
product_type=product_type,
|
||||
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"],
|
||||
|
|
@ -114,11 +139,12 @@ def product_item_from_entity(
|
|||
)
|
||||
|
||||
|
||||
def product_type_item_from_data(product_type_data):
|
||||
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 = {
|
||||
icon: IconData = {
|
||||
"type": "awesome-font",
|
||||
"name": "fa.folder",
|
||||
"color": "#0091B2",
|
||||
|
|
@ -127,8 +153,30 @@ def product_type_item_from_data(product_type_data):
|
|||
return ProductTypeItem(product_type_data["name"], icon)
|
||||
|
||||
|
||||
def create_default_product_type_item(product_type):
|
||||
icon = {
|
||||
def product_base_type_item_from_data(
|
||||
product_base_type_data: ProductBaseTypeDict
|
||||
) -> ProductBaseTypeItem:
|
||||
"""Create product base type item from data.
|
||||
|
||||
Args:
|
||||
product_base_type_data (ProductBaseTypeDict): Product base type data.
|
||||
|
||||
Returns:
|
||||
ProductBaseTypeDict: Product base type item.
|
||||
|
||||
"""
|
||||
icon: IconData = {
|
||||
"type": "awesome-font",
|
||||
"name": "fa.folder",
|
||||
"color": "#0091B2",
|
||||
}
|
||||
return ProductBaseTypeItem(
|
||||
name=product_base_type_data["name"],
|
||||
icon=icon)
|
||||
|
||||
|
||||
def create_default_product_type_item(product_type: str) -> ProductTypeItem:
|
||||
icon: IconData = {
|
||||
"type": "awesome-font",
|
||||
"name": "fa.folder",
|
||||
"color": "#0091B2",
|
||||
|
|
@ -136,10 +184,28 @@ def create_default_product_type_item(product_type):
|
|||
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: IconData = {
|
||||
"type": "awesome-font",
|
||||
"name": "fa.folder",
|
||||
"color": "#0091B2",
|
||||
}
|
||||
return ProductBaseTypeItem(product_base_type, icon)
|
||||
|
||||
|
||||
class ProductsModel:
|
||||
"""Model for products, version and representation.
|
||||
|
||||
All of the entities are product based. This model prepares data for UI
|
||||
All the entities are product based. This model prepares data for UI
|
||||
and caches it for faster access.
|
||||
|
||||
Note:
|
||||
|
|
@ -161,6 +227,8 @@ class ProductsModel:
|
|||
# Cache helpers
|
||||
self._product_type_items_cache = NestedCacheItem(
|
||||
levels=1, default_factory=list, lifetime=self.lifetime)
|
||||
self._product_base_type_items_cache = NestedCacheItem(
|
||||
levels=1, default_factory=list, lifetime=self.lifetime)
|
||||
self._product_items_cache = NestedCacheItem(
|
||||
levels=2, default_factory=dict, lifetime=self.lifetime)
|
||||
self._repre_items_cache = NestedCacheItem(
|
||||
|
|
@ -199,6 +267,31 @@ class ProductsModel:
|
|||
])
|
||||
return cache.get_data()
|
||||
|
||||
def get_product_base_type_items(
|
||||
self,
|
||||
project_name: Optional[str]) -> list[ProductBaseTypeItem]:
|
||||
"""Product base type items for project.
|
||||
|
||||
Args:
|
||||
project_name (optional, str): Project name.
|
||||
|
||||
Returns:
|
||||
list[ProductBaseTypeDict]: Product base type items.
|
||||
|
||||
"""
|
||||
if not project_name:
|
||||
return []
|
||||
|
||||
cache = self._product_base_type_items_cache[project_name]
|
||||
if not cache.is_valid:
|
||||
product_base_types = ayon_api.get_project_product_base_types(
|
||||
project_name)
|
||||
cache.update_data([
|
||||
product_base_type_item_from_data(product_base_type)
|
||||
for product_base_type in product_base_types
|
||||
])
|
||||
return cache.get_data()
|
||||
|
||||
def get_product_items(self, project_name, folder_ids, sender):
|
||||
"""Product items with versions for project and folder ids.
|
||||
|
||||
|
|
@ -449,11 +542,12 @@ class ProductsModel:
|
|||
|
||||
def _create_product_items(
|
||||
self,
|
||||
project_name,
|
||||
products,
|
||||
versions,
|
||||
project_name: str,
|
||||
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)
|
||||
|
|
@ -461,6 +555,11 @@ class ProductsModel:
|
|||
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)
|
||||
|
|
@ -470,7 +569,13 @@ class ProductsModel:
|
|||
product_type_item.name: product_type_item
|
||||
for product_type_item in product_type_items
|
||||
}
|
||||
output = {}
|
||||
|
||||
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] = {}
|
||||
for product in products:
|
||||
product_id = product["id"]
|
||||
folder_id = product["folderId"]
|
||||
|
|
@ -484,6 +589,7 @@ class ProductsModel:
|
|||
product,
|
||||
versions,
|
||||
product_type_items_by_name,
|
||||
product_base_type_items_by_name,
|
||||
folder_item.label,
|
||||
product_id in loaded_product_ids,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue