Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions server/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
from .addon import ApplicationsAddon
from .utils import (
ApplicationItem,
ToolItem,
get_application_items,
get_tool_items,
)


__all__ = (
"ApplicationsAddon",

"ApplicationItem",
"ToolItem",
"get_application_items",
"get_tool_items",
)
88 changes: 14 additions & 74 deletions server/actions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import collections
import os
import copy
import typing
from typing import Any
Expand All @@ -17,7 +16,7 @@
except ImportError:
SimpleForm = None

from .constants import LABELS_BY_GROUP_NAME, ICONS_BY_GROUP_NAME
from .utils import get_application_items, ApplicationItem

IDENTIFIER_PREFIX = "application.launch."
IDENTIFIER_WORKFILE_PREFIX = "application.launch-workfile."
Expand All @@ -33,88 +32,29 @@
from .addon import ApplicationsAddon


def _sort_getter(item):
return item["group_label"], item["variant_label"]


def get_items_for_app_groups(groups):
items = []
for group in groups:
group_name = group["name"]
group_label = group.get(
"label", LABELS_BY_GROUP_NAME.get(group_name)
) or group_name
icon_name = ICONS_BY_GROUP_NAME.get(group_name)
if not icon_name:
icon_name = group.get("icon")

if icon_name:
icon_name = os.path.basename(icon_name)

icon = None
if icon_name:
icon = {
"type": "url",
"url": "{addon_url}/public/icons/" + icon_name,
}

for variant in group["variants"]:
variant_name = variant["name"]
if not variant_name:
continue
variant_group_label = variant["group_label"]
if not variant_group_label:
variant_group_label = group_label
variant_label = variant["label"] or variant_name
full_name = f"{group_name}/{variant_name}"
items.append({
"host_name": group["host_name"],
"value": full_name,
"group_label": variant_group_label,
"variant_label": variant_label,
"icon": icon,
"show_grouped": variant["show_grouped"],
})

items.sort(key=_sort_getter)
return items


def _prepare_label_kwargs(item):
group_label = item["group_label"]
variant_label = item["variant_label"]
if _GROUP_LABEL_AVAILABLE and item["show_grouped"]:
def _prepare_label_kwargs(item: ApplicationItem) -> dict[str, str]:
if _GROUP_LABEL_AVAILABLE and item.show_grouped:
return {
"label": variant_label,
"group_label": group_label,
"label": item.variant_label,
"group_label": item.group_label,
}

return {
"label": f"{group_label} {variant_label}",
"label": item.full_label,
}


def _get_app_items_by_name(
addon_settings: dict[str, Any]
) -> dict[str, dict[str, Any]]:
app_settings = addon_settings["applications"]
app_groups = app_settings.pop("additional_apps")
for group_name, value in app_settings.items():
if not value["enabled"]:
continue
value["name"] = group_name
app_groups.append(value)

# This is very simplified profiles logic
app_items = get_items_for_app_groups(app_groups)
) -> dict[str, ApplicationItem]:
return {
item["value"]: item
for item in app_items
item.full_name: item
for item in get_application_items(addon_settings)
}


def _get_task_types_by_app_name(
app_items_by_name: dict[str, dict[str, Any]],
app_items_by_name: dict[str, ApplicationItem],
addon_settings: dict[str, Any],
project_entity: ProjectEntity
) -> dict[str, set[str]]:
Expand All @@ -127,7 +67,7 @@ def _get_task_types_by_app_name(
project_apps = project_entity.original_attributes.get(
"applications", []
)
for app_full_name, item in app_items_by_name.items():
for app_full_name in app_items_by_name.keys():
if app_full_name in project_apps:
task_types_by_app_name[app_full_name] |= (
project_task_types.copy()
Expand Down Expand Up @@ -222,7 +162,7 @@ async def get_action_manifests(
identifier=f"{IDENTIFIER_PREFIX}{app_name}",
**_prepare_label_kwargs(app_item),
category="Applications",
icon=app_item["icon"],
icon=app_item.icon,
order=0,
entity_type="task",
entity_subtypes=list(task_types),
Expand Down Expand Up @@ -299,13 +239,13 @@ async def get_dynamic_action_manifests(
collected_apps.add(app_name)

app_item = app_items_by_name[app_name]
if app_item["host_name"] not in host_names:
if app_item.host_name not in host_names:
continue
output.append(DynamicActionManifest(
identifier=f"{IDENTIFIER_WORKFILE_PREFIX}{app_name}",
**_prepare_label_kwargs(app_item),
category="Applications",
icon=app_item["icon"],
icon=app_item.icon,
order=0,
addon_name=addon.name,
addon_version=addon.version,
Expand Down
160 changes: 159 additions & 1 deletion server/addon.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
from ayon_server.entities.core import attribute_library
from ayon_server.entities.user import UserEntity
from ayon_server.helpers.project_list import get_project_list
from ayon_server.bundles.project_bundles import (
has_project_bundle,
get_project_bundle_addons,
)

try:
# Added in ayon-backend 1.8.0
Expand All @@ -58,7 +62,17 @@ def hash_data(data):
)

from .constants import LABELS_BY_GROUP_NAME
from .settings import ApplicationsAddonSettings, DEFAULT_VALUES
from .settings import (
ApplicationsAddonSettings,
DEFAULT_VALUES,
applications_enum,
)
from .utils import (
ApplicationItem,
ToolItem,
get_application_items,
get_tool_items,
)
from .actions import (
get_action_manifests,
get_dynamic_action_manifests,
Expand Down Expand Up @@ -342,6 +356,140 @@ async def convert_settings_overrides(
prj_tools["enabled"] = False
return overrides

async def get_application_items(
self, project_name: str | None, variant: str
) -> list[ApplicationItem]:
if project_name is None:
settings = await self.get_studio_settings(variant=variant)
else:
settings = await self.get_project_settings(
project_name, variant=variant
)
return get_application_items(settings.dict())

async def get_tool_items(
self, project_name: str | None, variant: str
) -> list[ToolItem]:
if project_name is None:
settings = await self.get_studio_settings(variant=variant)
else:
settings = await self.get_project_settings(
project_name, variant=variant
)
return get_tool_items(settings.dict())

async def get_addon_for_context(
self, project_name: str | None, variant: str
) -> BaseServerAddon | None:
"""Find applications addon version for a given context."""
if (
project_name is None
or variant not in ("production", "staging")
or not await has_project_bundle(project_name, variant=variant)
):
return await self._get_studio_bundle_addon(variant)

addons = await get_project_bundle_addons(
project_name, variant=variant
)
version = addons.get(self.name)
if not version or version == "__disable__":
return None

if version == "__inherit__":
return await self._get_studio_bundle_addon(variant)

addon_library = AddonLibrary.getinstance()
if (addon_def := addon_library.data.get(self.name)) is None:
return None
return addon_def.get(version)

async def get_applications_settings_enum(
self,
*,
project_name: str | None = None,
settings_variant: str = None,
):
"""Helper that can be used to get applications enum for settings.

Example:
from ayon_server.addons import AddonLibrary

async def apps_enum(project_name, addon, settings_variant):
addon_library = AddonLibrary.getinstance()
app_addons = addon_library.data.get("applications") or {}
addon = app_addons.latest
if hasattr(addon, "get_applications_settings_enum"):
return await addon.get_applications_settings_enum(
project_name=project_name,
settings_variant=settings_variant,
)
return []

class SomeSettingsModel(BaseModel):
application: str = SettingsField(
default_factory=list,
title="Applications",
enum_resolver=apps_enum,
)
"""
if settings_variant is None:
settings_variant = "production"
addon = await self.get_addon_for_context(
project_name, settings_variant
)
if addon is self:
return await applications_enum(
project_name=project_name,
addon=addon,
settings_variant=settings_variant,
)

if hasattr(addon, "get_applications_settings_enum"):
v_enum_func = addon.get_applications_settings_enum()
return await v_enum_func(
project_name=project_name,
addon=addon,
settings_variant=settings_variant,
)
return []

async def get_applications_for_context(
self, project_name: str | None, variant: str
) -> list[ApplicationItem]:
"""Get applications available for a given context.

This method can be used by other addons to get applciations available
for a given project and variant. It will return applciations based
on variant and project bundle if project has any.

Will work only if the addon version is new enough to have
'get_tool_items' method, otherwise it will return empty list.

"""
addon = await self._get_addon_for_context(project_name, variant)
if hasattr(addon, "get_application_items"):
return await addon.get_application_items(project_name, variant)
return []

async def get_tools_for_context(
self, project_name: str | None, variant: str
) -> list[ToolItem]:
"""Get tools available for a given context.

This method can be used by other addons to get tools available for
a given project and variant. It will return tools based on variant
and project bundle if project has any.

Will work only if the addon version is new enough to have
'get_tool_items' method, otherwise it will return empty list.

"""
addon = await self._get_addon_for_context(project_name, variant)
if hasattr(addon, "get_tool_items"):
return await addon.get_tool_items(project_name, variant)
return []

# --------------------------------------
# Backwards compatibility for attributes
# --------------------------------------
Expand Down Expand Up @@ -591,3 +739,13 @@ async def _autofill_workfile_entities(self):
"project_names": project_names,
}
)

async def _get_studio_bundle_addon(self, variant: str):
addon_library = AddonLibrary.getinstance()
if (addon_def := addon_library.data.get(self.name)) is None:
return None
addon_versions_by_name = (
await addon_library.get_addon_versions_by_variant(variant)
)
version = addon_versions_by_name.get(self.name)
return addon_def.get(version)
Loading
Loading