🐛 fix template data

This commit is contained in:
Ondrej Samohel 2024-12-10 23:02:10 +01:00
parent 365575604c
commit 1dd3c00eb5
No known key found for this signature in database
GPG key ID: 02376E18990A97C6
2 changed files with 219 additions and 41 deletions

View file

@ -3,6 +3,7 @@ from __future__ import annotations
import contextlib
import copy
from copy import deepcopy
from dataclasses import dataclass
from pathlib import Path
from typing import TYPE_CHECKING, Any, List
@ -47,8 +48,8 @@ if TYPE_CHECKING:
from ayon_core.pipeline import Anatomy
from ayon_core.pipeline.anatomy.templates import (
TemplateItem as AnatomyTemplateItem,
)
TemplateItem as AnatomyTemplateItem, AnatomyStringTemplate,
)
@dataclass(frozen=True)
@ -203,23 +204,43 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
"Instance has no persistent representations. Skipping")
return
# 3) get template and template data
template: str = self.get_publish_template(instance)
# 4) initialize OperationsSession()
op_session = OperationsSession()
# 5) Prepare product
product_entity = self.prepare_product(instance, op_session)
# 6) Prepare version
version_entity = self.prepare_version(
instance, op_session, product_entity
)
instance.data["versionEntity"] = version_entity
instance_template_data: dict[str, str] = {}
transfers = self.get_transfers_from_representations(
instance, representations)
def get_transfers_from_representations(
self,
instance: pyblish.api.Instance,
representations: list[Representation]) -> list[TransferItem]:
"""Get transfers from representations.
This method will go through all representations and prepare transfers
based on the traits they contain. First it will validate the
representation, and then it will prepare template data for the
representation. It specifically handles FileLocations, FileLocation,
Bundle, Sequence and UDIM traits.
Args:
instance (pyblish.api.Instance): Instance to process.
representations (list[Representation]): List of representations.
Returns:
list[TransferItem]: List of transfers.
Raises:
PublishError: If representation is invalid.
"""
template: str = self.get_publish_template(instance)
instance_template_data: dict[str, str] = {}
transfers: list[TransferItem] = []
# prepare template and data to format it
for representation in representations:
@ -235,6 +256,7 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
template_data = self.get_template_data_from_representation(
representation, instance)
# add instance based template data
template_data.update(instance_template_data)
# treat Variant as `output` in template data
@ -246,7 +268,7 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
template_item = TemplateItem(
anatomy=instance.context.data["anatomy"],
template=template,
template_data=template_data,
template_data=copy.deepcopy(template_data),
template_object=self.get_publish_template_object(instance),
)
@ -273,7 +295,7 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
self.get_transfers_from_bundle(
representation, template_item, transfers
)
return transfers
def _get_relative_to_root_original_dirname(
self, instance: pyblish.api.Instance) -> str:
@ -668,6 +690,8 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
"""
template_data = copy.deepcopy(instance.data["anatomyData"])
template_data["representation"] = representation.name
template_data["version"] = instance.data["version"]
template_data["hierarchy"] = instance.data["hierarchy"]
# add colorspace data to template data
if representation.contains_trait(ColorManaged):
@ -770,23 +794,26 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
if template_padding > dst_padding:
dst_padding = template_padding
# add template path and the data to resolve it
representation.add_trait(TemplatePath(
template=template_item.template,
data=template_item.template_data
))
# go through all frames in the sequence
# find their corresponding file locations
# format their template and add them to transfers
for frame in frames:
template_item.template_data["frame"] = frame
template_filled = path_template_object.format_strict(
template_item.template_data
)
file_loc: FileLocation = representation.get_trait(
FileLocations).get_file_location_for_frame(
frame, sequence)
template_item.template_data["frame"] = frame
template_item.template_data["ext"] = (
file_loc.file_path.suffix
)
template_filled = path_template_object.format_strict(
template_item.template_data
)
# add used values to the template data
used_values: dict = template_filled.used_values
template_item.template_data.update(used_values)
transfers.append(
TransferItem(
source=file_loc.file_path,
@ -799,6 +826,12 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
)
)
# add template path and the data to resolve it
representation.add_trait(TemplatePath(
template=template_item.template,
data=template_item.template_data
))
@staticmethod
def get_transfers_from_udim(
@ -819,13 +852,23 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
"""
udim: UDIM = representation.get_trait(UDIM)
path_template_object: AnatomyStringTemplate = (
template_item.template_object["path"]
)
for file_loc in representation.get_trait(
FileLocations).file_paths:
template_item.template_data["udim"] = (
udim.get_udim_from_file_location(file_loc)
)
template_filled = template_item.template.format(
**template_item.template_data)
template_filled = path_template_object.format_strict(
template_item.template_data
)
# add used values to the template data
used_values: dict = template_filled.used_values
template_item.template_data.update(used_values)
transfers.append(
TransferItem(
source=file_loc.file_path,
@ -861,7 +904,12 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
template_item (TemplateItem): Template item.
"""
path_template_object = template_item.template_object["path"]
path_template_object: AnatomyStringTemplate = (
template_item.template_object["path"]
)
template_item.template_data["ext"] = (
representation.get_trait(FileLocation).file_path.suffix
)
template_item.template_data.pop("frame", None)
with contextlib.suppress(MissingTraitError):
udim = representation.get_trait(UDIM)
@ -870,6 +918,11 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
template_filled = path_template_object.format_strict(
template_item.template_data
)
# add used values to the template data
used_values: dict = template_filled.used_values
template_item.template_data.update(used_values)
file_loc: FileLocation = representation.get_trait(FileLocation)
transfers.append(
TransferItem(
@ -878,7 +931,7 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
size=file_loc.file_size,
checksum=file_loc.file_hash,
template=template_item.template,
template_data=template_item.template_data.copy(),
template_data=template_item.template_data,
representation=representation,
)
)
@ -928,3 +981,4 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
IntegrateTraits.get_transfers_from_bundle(
sub_representation, template_item, transfers
)

View file

@ -2,10 +2,16 @@
from __future__ import annotations
import base64
import time
from pathlib import Path
import pyblish.api
import pytest
import pytest_ayon
from ayon_api.operations import (
OperationsSession,
)
from ayon_core.pipeline.anatomy import Anatomy
from ayon_core.pipeline.traits import (
FileLocation,
FileLocations,
@ -18,6 +24,7 @@ from ayon_core.pipeline.traits import (
Sequence,
Transient,
)
from ayon_core.pipeline.version_start import get_versioning_start
# Tagged,
# TemplatePath,
@ -26,6 +33,8 @@ from ayon_core.settings import get_project_settings
PNG_FILE_B64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAACklEQVR4AWNgAAAAAgABc3UBGAAAAABJRU5ErkJggg==" # noqa: E501
SEQUENCE_LENGTH = 10
CURRENT_TIME = time.time()
@pytest.fixture(scope="session")
def single_file(tmp_path_factory: pytest.TempPathFactory) -> Path:
@ -48,7 +57,7 @@ def sequence_files(tmp_path_factory: pytest.TempPathFactory) -> list[Path]:
@pytest.fixture()
def mock_context(
project: object,
project: pytest_ayon.ProjectInfo,
single_file: Path,
sequence_files: list[Path]) -> pyblish.api.Context:
"""Return a mock instance.
@ -56,18 +65,6 @@ def mock_context(
This is mocking pyblish context for testing. It is using real AYON project
thanks to the ``project`` fixture.
This returns following data::
project_name: str
project_code: str
project_root_folders: dict[str, str]
folder: IdNamePair
task: IdNamePair
product: IdNamePair
version: IdNamePair
representations: List[IdNamePair]
links: List[str]
Args:
project (object): The project info. It is `ProjectInfo` object
returned by pytest fixture.
@ -75,25 +72,68 @@ def mock_context(
sequence_files (list[Path]): The paths to a sequence of image files.
"""
anatomy = Anatomy(project.project_name)
context = pyblish.api.Context()
context.data["projectName"] = project.project_name
context.data["hostName"] = "test_host"
context.data["project_settings"] = get_project_settings(
project.project_name)
context.data["anatomy"] = anatomy
context.data["time"] = CURRENT_TIME
context.data["user"] = "test_user"
context.data["machine"] = "test_machine"
context.data["fps"] = 25
instance = context.create_instance("mock_instance")
instance.data["source"] = "test_source"
instance.data["families"] = ["render"]
instance.data["anatomyData"] = {
"project": project.project_name,
"project": {
"name": project.project_name,
"code": project.project_code
},
"task": {
"name": project.task.name,
"type": "test" # pytest-ayon doesn't return the task type yet
}
},
"folder": {
"name": project.folder.name,
"type": "test" # pytest-ayon doesn't return the folder type yet
},
"product": {
"name": project.product.name,
"type": "test" # pytest-ayon doesn't return the product type yet
},
}
instance.data["folderEntity"] = project.folder_entity
instance.data["productType"] = "test_product"
instance.data["productName"] = project.product.name
instance.data["anatomy"] = anatomy
instance.data["comment"] = "test_comment"
instance.data["integrate"] = True
instance.data["farm"] = False
parents = project.folder_entity["path"].lstrip("/").split("/")
hierarchy = ""
if parents:
hierarchy = "/".join(parents)
instance.data["hierarchy"] = hierarchy
version_number = get_versioning_start(
context.data["projectName"],
instance.context.data["hostName"],
task_name=project.task.name,
task_type="test",
product_type=instance.data["productType"],
product_name=instance.data["productName"]
)
instance.data["version"] = version_number
_file_size = len(base64.b64decode(PNG_FILE_B64))
file_locations = [
FileLocation(
@ -121,7 +161,7 @@ def mock_context(
),
Sequence(
frame_padding=4,
frame_regex=r"^img\.(\d{4})\.png$",
frame_regex=r"^img\.(?P<frame>\d{4})\.png$",
),
FileLocations(
file_paths=file_locations,
@ -176,3 +216,87 @@ def test_filter_lifecycle() -> None:
assert len(filtered) == 1
assert filtered[0] == persistent_representation
def test_prepare_product(
project: pytest_ayon.ProjectInfo,
mock_context: pyblish.api.Context) -> None:
"""Test prepare_product."""
integrator = IntegrateTraits()
op_session = OperationsSession()
product = integrator.prepare_product(mock_context[0], op_session)
assert product == {
"attrib": {},
"data": {
"families": ["default", "render"],
},
"folderId": project.folder_entity["id"],
"name": "renderMain",
"productType": "test_product",
"id": project.product_entity["id"],
}
def test_prepare_version(
project: pytest_ayon.ProjectInfo,
mock_context: pyblish.api.Context) -> None:
"""Test prepare_version."""
integrator = IntegrateTraits()
op_session = OperationsSession()
product = integrator.prepare_product(mock_context[0], op_session)
version = integrator.prepare_version(
mock_context[0], op_session , product)
assert version == {
"attrib": {
"comment": "test_comment",
"families": ["default", "render"],
"fps": 25,
"machine": "test_machine",
"source": "test_source",
},
"data": {
"author": "test_user",
"time": CURRENT_TIME,
},
"id": project.version_entity["id"],
"productId": project.product_entity["id"],
"version": 1,
}
def test_get_transfers_from_representation(
mock_context: pyblish.api.Context) -> None:
"""Test get_transfers_from_representation."""
integrator = IntegrateTraits()
instance = mock_context[0]
representations: list[Representation] = instance.data[
"representations_with_traits"]
transfers = integrator.get_transfers_from_representations(
instance, representations)
assert transfers == [
{
"file_path": Path("test"),
"file_size": 1234,
"traits": [
"Persistent",
"Image",
"MimeType"
]
},
{
"file_path": Path("test"),
"file_size": 1234,
"traits": [
"Persistent",
"FrameRanged",
"Sequence",
"FileLocations",
"Image",
"PixelBased",
"MimeType"
]
}
]