mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
🐛 fix template data
This commit is contained in:
parent
365575604c
commit
1dd3c00eb5
2 changed files with 219 additions and 41 deletions
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue