Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
b8e1050
:sparkles: add support for product base types
antirotor May 30, 2025
1eeae55
:bug: remove the stray character in comment
antirotor May 30, 2025
4d83ab8
:sparkles: add `productBaseType` to PRODUCT_FIELDS
antirotor Jun 6, 2025
ceff641
:recycle: better handling of product base type in entity operations
antirotor Jun 10, 2025
e3bc2c5
:sparkles: add product base type getters to init and linting
antirotor Jun 10, 2025
3a96dd6
fix used function to create query
iLLiCiTiT Jun 26, 2025
cb4a9f9
remove unnecessary 'get_product_base_type_names' function
iLLiCiTiT Jun 26, 2025
07bcdb3
Merge branch 'develop' into enhancement/254-product-base-types-add-su…
antirotor Sep 18, 2025
67c4557
remove functions that are not needed
iLLiCiTiT Sep 19, 2025
6813200
remove 'get_product_base_type_names' again
iLLiCiTiT Sep 19, 2025
44d139a
remove 'get_product_base_types' from '_api'
iLLiCiTiT Sep 19, 2025
819a69f
remove icon and color from product base types fields
iLLiCiTiT Sep 19, 2025
26a3f84
remove unused 'ProductBaseTypeDict'
iLLiCiTiT Sep 19, 2025
937ada3
remove 'productBaseType' fields handling
iLLiCiTiT Sep 19, 2025
7d63720
handle 'productBaseType' field if passed in as is
iLLiCiTiT Sep 19, 2025
0691d8e
remove 'get_product_base_types'
iLLiCiTiT Sep 19, 2025
7d984af
added base of product base type support logic
iLLiCiTiT Sep 19, 2025
818e762
implement product base type handling in entity hub
iLLiCiTiT Sep 19, 2025
a52c1f9
added product base type to products api methods
iLLiCiTiT Sep 19, 2025
3dae224
change order of product_base_type argument
iLLiCiTiT Sep 19, 2025
82291c6
change one more argument order
iLLiCiTiT Sep 19, 2025
7c83f11
remove unused import
iLLiCiTiT Sep 19, 2025
072bef1
:recycle: add check and more support in graphql
antirotor Sep 22, 2025
16551af
Merge branch 'develop' into enhancement/254-product-base-types-add-su…
antirotor Oct 22, 2025
0965474
Merge remote-tracking branch 'origin/develop' into enhancement/254-pr…
antirotor Nov 10, 2025
735de48
Merge remote-tracking branch 'origin/enhancement/254-product-base-typ…
antirotor Nov 10, 2025
10e49d7
:recycle: add server version for product base types
antirotor Nov 10, 2025
5c04bad
Update ayon_api/operations.py
antirotor Nov 13, 2025
daef6b9
rename 'product_base_type_supported' to 'is_product_base_type_supported'
iLLiCiTiT Nov 14, 2025
da9c871
add 'productBaseType' only if is supported
iLLiCiTiT Nov 14, 2025
dae95dd
raise error if product base type is not supported but is passed in
iLLiCiTiT Nov 14, 2025
9d31f0e
Merge branch 'develop' into enhancement/254-product-base-types-add-su…
iLLiCiTiT Nov 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions ayon_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@
get_product_types,
get_project_product_types,
get_product_type_names,
get_product_base_types,
get_project_product_base_types,
get_product_base_type_names,
create_product,
update_product,
delete_product,
Expand Down Expand Up @@ -427,6 +430,9 @@
"get_product_types",
"get_project_product_types",
"get_product_type_names",
"get_product_base_types",
"get_project_product_base_types",
"get_product_base_type_names",
"create_product",
"update_product",
"delete_product",
Expand Down
84 changes: 80 additions & 4 deletions ayon_api/_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
FlatFolderDict,
ProjectHierarchyDict,
ProductTypeDict,
ProductBaseTypeDict,
StreamType,
)

Expand Down Expand Up @@ -4328,6 +4329,7 @@ def get_products(
product_names: Optional[Iterable[str]] = None,
folder_ids: Optional[Iterable[str]] = None,
product_types: Optional[Iterable[str]] = None,
product_base_types: Optional[Iterable[str]] = None,
product_name_regex: Optional[str] = None,
product_path_regex: Optional[str] = None,
names_by_folder_ids: Optional[Dict[str, Iterable[str]]] = None,
Expand All @@ -4337,21 +4339,22 @@ def get_products(
fields: Optional[Iterable[str]] = None,
own_attributes=_PLACEHOLDER,
) -> Generator["ProductDict", None, None]:
"""Query products from server.
"""Query products from the server.

Todos:
Separate 'name_by_folder_ids' filtering to separated method. It
cannot be combined with some other filters.

Args:
project_name (str): Name of project.
project_name (str): Name of the project.
product_ids (Optional[Iterable[str]]): Task ids to filter.
product_names (Optional[Iterable[str]]): Task names used for
filtering.
folder_ids (Optional[Iterable[str]]): Ids of task parents.
Use 'None' if folder is direct child of project.
Use 'None' if folder is direct child of the project.
product_types (Optional[Iterable[str]]): Product types used for
filtering.
product_base_types (Optional[Iterable[str]]): Product base types
product_name_regex (Optional[str]): Filter products by name regex.
product_path_regex (Optional[str]): Filter products by path regex.
Path starts with folder path and ends with product name.
Expand Down Expand Up @@ -4380,6 +4383,7 @@ def get_products(
product_names=product_names,
folder_ids=folder_ids,
product_types=product_types,
product_base_types=product_base_types,
product_name_regex=product_name_regex,
product_path_regex=product_path_regex,
names_by_folder_ids=names_by_folder_ids,
Expand Down Expand Up @@ -4461,7 +4465,7 @@ def get_product_types(
) -> List["ProductTypeDict"]:
"""Types of products.

This is server wide information. Product types have 'name', 'icon' and
This is the server-wide information. Product types have 'name', 'icon' and
'color'.

Args:
Expand All @@ -4477,6 +4481,27 @@ def get_product_types(
)


def get_product_base_types(
fields: Optional[Iterable[str]] = None,
) -> List["ProductBaseTypeDict"]:
"""Base types of products.

This is the server-wide information. Product base types have 'name', 'icon'
and 'color'.

Args:
fields (Optional[Iterable[str]]): Product base types fields to query.

Returns:
list[ProductBaseTypeDict]: Product base types information.

"""
con = get_server_api_connection()
return con.get_product_base_types(
fields=fields,
)


def get_project_product_types(
project_name: str,
fields: Optional[Iterable[str]] = None,
Expand All @@ -4501,6 +4526,30 @@ def get_project_product_types(
)


def get_project_product_base_types(
project_name: str,
fields: Optional[Iterable[str]] = None,
) -> List["ProductBaseTypeDict"]:
"""Base types of products available in a project.

Filter only product base types available in a project.

Args:
project_name (str): Name of project where to look for
product base types.
fields (Optional[Iterable[str]]): Product base types fields to query.

Returns:
List[ProductBaseTypeDict]: Product base types information.

"""
con = get_server_api_connection()
return con.get_project_product_base_types(
project_name=project_name,
fields=fields,
)


def get_product_type_names(
project_name: Optional[str] = None,
product_ids: Optional[Iterable[str]] = None,
Expand Down Expand Up @@ -4528,6 +4577,33 @@ def get_product_type_names(
)


def get_product_base_type_names(
project_name: Optional[str] = None,
product_ids: Optional[Iterable[str]] = None,
) -> Set[str]:
"""Base product type names.

Warnings:
Similar use case as `get_product_type_names` but for base
product types.

Args:
project_name (Optional[str]): Name of project where to look for
queried entities.
product_ids (Optional[Iterable[str]]): Product ids filter. Can be
used only with 'project_name'.

Returns:
set[str]: Base product type names.

"""
con = get_server_api_connection()
return con.get_product_base_type_names(
project_name=project_name,
product_ids=product_ids,
)


def create_product(
project_name: str,
name: str,
Expand Down
8 changes: 8 additions & 0 deletions ayon_api/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@
"color",
}

# --- Product base type ---
DEFAULT_PRODUCT_BASE_TYPE_FIELDS = {
"name",
"icon",
"color",
}

# --- Project ---
DEFAULT_PROJECT_FIELDS = {
"active",
Expand Down Expand Up @@ -98,6 +105,7 @@
"folderId",
"active",
"productType",
"productBaseType",
"data",
"status",
"tags",
Expand Down
30 changes: 29 additions & 1 deletion ayon_api/entity_hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ def add_new_product(
self,
name: str,
product_type: str,
product_base_type: Optional[str] = None,
folder_id: Optional["Union[str, _CustomNone]"] = UNKNOWN_VALUE,
tags: Optional[Iterable[str]] = None,
attribs: Optional[Dict[str, Any]] = UNKNOWN_VALUE,
Expand All @@ -443,10 +444,11 @@ def add_new_product(
entity_id: Optional[str] = None,
created: Optional[bool] = True,
):
"""Create task object and add it to entity hub.
"""Create a task object and add it to the entity hub.

Args:
name (str): Name of entity.
product_base_type (str): Base type of product.
product_type (str): Type of product.
folder_id (Union[str, None]): Parent folder id.
tags (Optional[Iterable[str]]): Folder tags.
Expand All @@ -458,13 +460,19 @@ def add_new_product(
created (Optional[bool]): Entity is new. When 'None' is passed the
value is defined based on value of 'entity_id'.

Todo:
- Once the product base type is implemented and established,
it should be made mandatory to pass it and product_type
itself should be optional.

Returns:
ProductEntity: Added product entity.

"""
product_entity = ProductEntity(
name=name,
product_type=product_type,
product_base_type=product_base_type,
folder_id=folder_id,
tags=tags,
attribs=attribs,
Expand Down Expand Up @@ -3406,6 +3414,7 @@ def to_create_body_data(self):
class ProductEntity(BaseEntity):
_supports_name = True
_supports_tags = True
_supports_base_type = True

entity_type = "product"
parent_entity_types = ["folder"]
Expand All @@ -3414,6 +3423,7 @@ def __init__(
self,
name: str,
product_type: str,
product_base_type: Optional[str] = None,
folder_id: Optional["Union[str, _CustomNone]"] = UNKNOWN_VALUE,
tags: Optional[Iterable[str]] = None,
attribs: Optional[Dict[str, Any]] = UNKNOWN_VALUE,
Expand All @@ -3435,6 +3445,7 @@ def __init__(
entity_hub=entity_hub,
)
self._product_type = product_type
self._product_base_type = product_base_type

self._orig_product_type = product_type

Expand All @@ -3454,6 +3465,21 @@ def set_product_type(self, product_type):

product_type = property(get_product_type, set_product_type)

def get_product_base_type(self) -> Optional[str]:
"""Get the product base type.

Returns:
Optional[str]: The product base type, or None if not set.

"""
return self._product_base_type

def set_product_base_type(self, product_base_type: str) -> None:
"""Set the product base type."""
self._product_base_type = product_base_type

product_base_type = property(get_product_base_type, set_product_base_type)

def lock(self):
super().lock()
self._orig_product_type = self._product_type
Expand All @@ -3475,6 +3501,7 @@ def from_entity_data(cls, product, entity_hub):
return cls(
name=product["name"],
product_type=product["productType"],
product_base_type=product["productBaseType"],
folder_id=product["folderId"],
tags=product["tags"],
attribs=product["attrib"],
Expand All @@ -3492,6 +3519,7 @@ def to_create_body_data(self):
output = {
"name": self.name,
"productType": self.product_type,
"productBaseType": self.product_base_type,
"folderId": self.parent_id,
}

Expand Down
49 changes: 49 additions & 0 deletions ayon_api/graphql_queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,28 @@ def product_types_query(fields):
return query


def product_base_types_query(fields):
query = GraphQlQuery("ProductBaseTypes")
product_base_types_field = query.add_field("productBaseTypes")

nested_fields = fields_to_dict(fields)

query_queue = collections.deque()
for key, value in nested_fields.items():
query_queue.append((key, value, product_base_types_field))

while query_queue:
item = query_queue.popleft()
key, value, parent = item
field = parent.add_field(key)
if value is FIELD_VALUE:
continue

for k, v in value.items():
query_queue.append((k, v, field))
return query


def project_product_types_query(fields):
query = GraphQlQuery("ProjectProductTypes")
project_query = query.add_field("project")
Expand All @@ -143,6 +165,30 @@ def project_product_types_query(fields):
return query


def project_product_base_types_query(fields):
query = GraphQlQuery("ProjectProductBaseTypes")
project_query = query.add_field("project")
project_name_var = query.add_variable("projectName", "String!")
project_query.set_filter("name", project_name_var)
product_base_types_field = project_query.add_field("productBaseTypes")
nested_fields = fields_to_dict(fields)

query_queue = collections.deque()
for key, value in nested_fields.items():
query_queue.append((key, value, product_base_types_field))

while query_queue:
item = query_queue.popleft()
key, value, parent = item
field = parent.add_field(key)
if value is FIELD_VALUE:
continue

for k, v in value.items():
query_queue.append((k, v, field))
return query


def folders_graphql_query(fields):
query = GraphQlQuery("FoldersQuery")
project_name_var = query.add_variable("projectName", "String!")
Expand Down Expand Up @@ -298,6 +344,8 @@ def products_graphql_query(fields):
product_names_var = query.add_variable("productNames", "[String!]")
folder_ids_var = query.add_variable("folderIds", "[String!]")
product_types_var = query.add_variable("productTypes", "[String!]")
product_base_types_var = query.add_variable(
"productBaseTypes", "[String!]")
product_name_regex_var = query.add_variable("productNameRegex", "String!")
product_path_regex_var = query.add_variable("productPathRegex", "String!")
statuses_var = query.add_variable("productStatuses.", "[String!]")
Expand All @@ -311,6 +359,7 @@ def products_graphql_query(fields):
products_field.set_filter("names", product_names_var)
products_field.set_filter("folderIds", folder_ids_var)
products_field.set_filter("productTypes", product_types_var)
products_field.set_filter("productBaseTypes", product_base_types_var)
products_field.set_filter("statuses", statuses_var)
products_field.set_filter("tags", tags_var)
products_field.set_filter("nameEx", product_name_regex_var)
Expand Down
Loading