Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to run the workfile template builder on startup and simplify resolve houdini path #74

Merged
merged 8 commits into from
Sep 10, 2024
15 changes: 15 additions & 0 deletions client/ayon_houdini/api/lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
from ayon_core.pipeline.create import CreateContext
from ayon_core.pipeline.template_data import get_template_data
from ayon_core.pipeline.context_tools import get_current_task_entity
from ayon_core.pipeline.workfile.workfile_template_builder import (
TemplateProfileNotFound
)
from ayon_core.tools.utils import PopupUpdateKeys, SimplePopup
from ayon_core.tools.utils.host_tools import get_tool_by_name

Expand Down Expand Up @@ -1389,3 +1392,15 @@ def prompt_reset_context():
update_content_on_context_change()

dialog.deleteLater()


def start_workfile_template_builder():
from .workfile_template_builder import (
build_workfile_template
)

log.info("Starting workfile template builder...")
BigRoy marked this conversation as resolved.
Show resolved Hide resolved
try:
build_workfile_template(workfile_creation_enabled=True)
except TemplateProfileNotFound:
log.warning("Template profile not found. Skipping...")
11 changes: 5 additions & 6 deletions client/ayon_houdini/api/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,10 @@ def install(self):

self._has_been_setup = True

# Set folder settings for the empty scene directly after launch of
# Houdini so it initializes into the correct scene FPS,
# Frame Range, etc.
# TODO: make sure this doesn't trigger when
# opening with last workfile.
_set_context_settings()
# Manually call on_new callback as it doesn't get called when AYON
# launches for the first time on a context, only when going to
# File -> New
on_new()
BigRoy marked this conversation as resolved.
Show resolved Hide resolved

if not IS_HEADLESS:
import hdefereval # noqa, hdefereval is only available in ui mode
Expand Down Expand Up @@ -414,6 +412,7 @@ def _enforce_start_frame():

if hou.isUIAvailable():
import hdefereval
hdefereval.executeDeferred(lib.start_workfile_template_builder)
hdefereval.executeDeferred(_enforce_start_frame)
else:
# Run without execute deferred when no UI is available because
Expand Down
136 changes: 13 additions & 123 deletions client/ayon_houdini/api/workfile_template_builder.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,9 @@
import os

import hou

from ayon_core.lib import (
StringTemplate,
filter_profiles,
)
from ayon_core.pipeline import registered_host, Anatomy
from ayon_core.pipeline import registered_host
from ayon_core.pipeline.workfile.workfile_template_builder import (
AbstractTemplateBuilder,
PlaceholderPlugin,
TemplateProfileNotFound,
TemplateLoadFailed,
TemplateNotFound
PlaceholderPlugin
)
from ayon_core.tools.workfile_template_build import (
WorkfileBuildPlaceholderDialog,
Expand All @@ -29,121 +20,20 @@

class HoudiniTemplateBuilder(AbstractTemplateBuilder):
"""Concrete implementation of AbstractTemplateBuilder for Houdini"""

def get_template_preset(self):
"""Unified way how template preset is received using settings.

Method is dependent on '_get_build_profiles' which should return filter
profiles to resolve path to a template. Default implementation looks
into host settings:
- 'project_settings/{host name}/templated_workfile_build/profiles'
def resolve_template_path(self, path, fill_data):
"""Allows additional resolving over the template path using custom
integration methods, like Houdini's expand string functionality.

Returns:
dict: Dictionary with `path`, `keep_placeholder` and
`create_first_version` settings from the template preset
for current context.

Raises:
TemplateProfileNotFound: When profiles are not filled.
TemplateLoadFailed: Profile was found but path is not set.
TemplateNotFound: Path was set but file does not exist.
This only works with ayon-core 0.4.5+
"""
# use default template data formatting
path = super().resolve_template_path(path, fill_data)

host_name = self.host_name
project_name = self.project_name
task_name = self.current_task_name
task_type = self.current_task_type

build_profiles = self._get_build_profiles()
profile = filter_profiles(
build_profiles,
{
"task_types": task_type,
"task_names": task_name
}
)

if not profile:
raise TemplateProfileNotFound((
"No matching profile found for task '{}' of type '{}' "
"with host '{}'"
).format(task_name, task_type, host_name))

path = profile["path"]

# switch to remove placeholders after they are used
keep_placeholder = profile.get("keep_placeholder")
create_first_version = profile.get("create_first_version")

# backward compatibility, since default is True
if keep_placeholder is None:
keep_placeholder = True

if not path:
raise TemplateLoadFailed((
"Template path is not set.\n"
"Path need to be set in {}\\Template Workfile Build "
"Settings\\Profiles"
).format(host_name.title()))

# Try fill path with environments and anatomy roots
anatomy = Anatomy(project_name)
fill_data = {
key: value
for key, value in os.environ.items()
}

fill_data["root"] = anatomy.roots
fill_data["project"] = {
"name": project_name,
"code": anatomy.project_code,
}

result = StringTemplate.format_template(path, fill_data)
if result.solved:
path = result.normalized()

# I copied the whole thing because I wanted to add some
# Houdini specific code here
# escape backslashes for `expandString` and expand houdini vars
path = path.replace("\\", "\\\\")
path = hou.text.expandString(path)

if path and os.path.exists(path):
self.log.info("Found template at: '{}'".format(path))
return {
"path": path,
"keep_placeholder": keep_placeholder,
"create_first_version": create_first_version
}

solved_path = None
while True:
try:
solved_path = anatomy.path_remapper(path)
except KeyError as missing_key:
raise KeyError(
"Could not solve key '{}' in template path '{}'".format(
missing_key, path))

if solved_path is None:
solved_path = path
if solved_path == path:
break
path = solved_path

solved_path = os.path.normpath(solved_path)
if not os.path.exists(solved_path):
raise TemplateNotFound(
"Template found in AYON settings for task '{}' with host "
"'{}' does not exists. (Not found : {})".format(
task_name, host_name, solved_path))

self.log.info("Found template at: '{}'".format(solved_path))

return {
"path": solved_path,
"keep_placeholder": keep_placeholder,
"create_first_version": create_first_version
}
return path

def import_template(self, path):
"""Import template into current scene.
Expand Down Expand Up @@ -239,10 +129,10 @@ def delete_placeholder(self, placeholder):
placeholder_node.destroy()


def build_workfile_template(*args):
def build_workfile_template(*args, **kwargs):
# NOTE Should we inform users that they'll lose unsaved changes ?
builder = HoudiniTemplateBuilder(registered_host())
builder.build_template()
builder.build_template(*args, **kwargs)


def update_workfile_template(*args):
Expand Down