Merge branch 'develop' of https://github.com/ynput/ayon-core into enhancement/AY-5539_define-creators-per-task

This commit is contained in:
Petr Kalis 2024-05-30 11:33:41 +02:00
commit 62f43bfb6d
14 changed files with 450 additions and 117 deletions

View file

@ -1,6 +1,10 @@
import pyblish.api
from ayon_core.lib import filter_profiles
from ayon_core.pipeline.publish import (
PublishValidationError, OptionalPyblishPluginMixin
PublishValidationError,
OptionalPyblishPluginMixin,
get_current_host_name,
)
@ -13,12 +17,35 @@ class ValidateVersion(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin):
order = pyblish.api.ValidatorOrder
label = "Validate Version"
hosts = ["nuke", "maya", "houdini", "blender",
"photoshop", "aftereffects"]
optional = False
active = True
@classmethod
def apply_settings(cls, settings):
# Disable if no profile is found for the current host
profiles = (
settings
["core"]
["publish"]
["ValidateVersion"]
["plugin_state_profiles"]
)
profile = filter_profiles(
profiles, {"host_names": get_current_host_name()}
)
if not profile:
cls.enabled = False
return
# Apply settings from profile
for attr_name in {
"enabled",
"optional",
"active",
}:
setattr(cls, attr_name, profile[attr_name])
def process(self, instance):
if not self.is_active(instance.data):
return

View file

@ -14,6 +14,7 @@ from .hierarchy import (
)
from .thumbnails import ThumbnailsModel
from .selection import HierarchyExpectedSelection
from .users import UsersModel
__all__ = (
@ -32,4 +33,6 @@ __all__ = (
"ThumbnailsModel",
"HierarchyExpectedSelection",
"UsersModel",
)

View file

@ -0,0 +1,84 @@
import ayon_api
from ayon_core.lib import CacheItem
class UserItem:
def __init__(
self,
username,
full_name,
email,
avatar_url,
active,
):
self.username = username
self.full_name = full_name
self.email = email
self.avatar_url = avatar_url
self.active = active
@classmethod
def from_entity_data(cls, user_data):
return cls(
user_data["name"],
user_data["attrib"]["fullName"],
user_data["attrib"]["email"],
user_data["attrib"]["avatarUrl"],
user_data["active"],
)
class UsersModel:
def __init__(self, controller):
self._controller = controller
self._users_cache = CacheItem(default_factory=list)
def get_user_items(self):
"""Get user items.
Returns:
List[UserItem]: List of user items.
"""
self._invalidate_cache()
return self._users_cache.get_data()
def get_user_items_by_name(self):
"""Get user items by name.
Implemented as most of cases using this model will need to find
user information by username.
Returns:
Dict[str, UserItem]: Dictionary of user items by name.
"""
return {
user_item.username: user_item
for user_item in self.get_user_items()
}
def get_user_item_by_username(self, username):
"""Get user item by username.
Args:
username (str): Username.
Returns:
Union[UserItem, None]: User item or None if not found.
"""
self._invalidate_cache()
for user_item in self.get_user_items():
if user_item.username == username:
return user_item
return None
def _invalidate_cache(self):
if self._users_cache.is_valid:
return
self._users_cache.update_data([
UserItem.from_entity_data(user)
for user in ayon_api.get_users()
])

View file

@ -13,8 +13,10 @@ class WorkfileInfo:
task_id (str): Task id.
filepath (str): Filepath.
filesize (int): File size.
creation_time (int): Creation time (timestamp).
modification_time (int): Modification time (timestamp).
creation_time (float): Creation time (timestamp).
modification_time (float): Modification time (timestamp).
created_by (Union[str, none]): User who created the file.
updated_by (Union[str, none]): User who last updated the file.
note (str): Note.
"""
@ -26,6 +28,8 @@ class WorkfileInfo:
filesize,
creation_time,
modification_time,
created_by,
updated_by,
note,
):
self.folder_id = folder_id
@ -34,6 +38,8 @@ class WorkfileInfo:
self.filesize = filesize
self.creation_time = creation_time
self.modification_time = modification_time
self.created_by = created_by
self.updated_by = updated_by
self.note = note
def to_data(self):
@ -50,6 +56,8 @@ class WorkfileInfo:
"filesize": self.filesize,
"creation_time": self.creation_time,
"modification_time": self.modification_time,
"created_by": self.created_by,
"updated_by": self.updated_by,
"note": self.note,
}
@ -212,6 +220,7 @@ class FileItem:
dirpath (str): Directory path of file.
filename (str): Filename.
modified (float): Modified timestamp.
created_by (Optional[str]): Username.
representation_id (Optional[str]): Representation id of published
workfile.
filepath (Optional[str]): Prepared filepath.
@ -223,6 +232,8 @@ class FileItem:
dirpath,
filename,
modified,
created_by=None,
updated_by=None,
representation_id=None,
filepath=None,
exists=None
@ -230,6 +241,8 @@ class FileItem:
self.filename = filename
self.dirpath = dirpath
self.modified = modified
self.created_by = created_by
self.updated_by = updated_by
self.representation_id = representation_id
self._filepath = filepath
self._exists = exists
@ -269,6 +282,7 @@ class FileItem:
"filename": self.filename,
"dirpath": self.dirpath,
"modified": self.modified,
"created_by": self.created_by,
"representation_id": self.representation_id,
"filepath": self.filepath,
"exists": self.exists,
@ -522,6 +536,16 @@ class AbstractWorkfilesFrontend(AbstractWorkfilesCommon):
pass
@abstractmethod
def get_user_items_by_name(self):
"""Get user items available on AYON server.
Returns:
Dict[str, UserItem]: User items by username.
"""
pass
# Host information
@abstractmethod
def get_workfile_extensions(self):

View file

@ -19,6 +19,7 @@ from ayon_core.tools.common_models import (
HierarchyModel,
HierarchyExpectedSelection,
ProjectsModel,
UsersModel,
)
from .abstract import (
@ -161,6 +162,7 @@ class BaseWorkfileController(
self._save_is_enabled = True
# Expected selected folder and task
self._users_model = self._create_users_model()
self._expected_selection = self._create_expected_selection_obj()
self._selection_model = self._create_selection_model()
self._projects_model = self._create_projects_model()
@ -176,6 +178,12 @@ class BaseWorkfileController(
def is_host_valid(self):
return self._host_is_valid
def _create_users_model(self):
return UsersModel(self)
def _create_workfiles_model(self):
return WorkfilesModel(self)
def _create_expected_selection_obj(self):
return WorkfilesToolExpectedSelection(self)
@ -188,9 +196,6 @@ class BaseWorkfileController(
def _create_hierarchy_model(self):
return HierarchyModel(self)
def _create_workfiles_model(self):
return WorkfilesModel(self)
@property
def event_system(self):
"""Inner event system for workfiles tool controller.
@ -272,6 +277,9 @@ class BaseWorkfileController(
{"enabled": enabled}
)
def get_user_items_by_name(self):
return self._users_model.get_user_items_by_name()
# Host information
def get_workfile_extensions(self):
host = self._host

View file

@ -6,6 +6,7 @@ import arrow
import ayon_api
from ayon_api.operations import OperationsSession
from ayon_core.lib import get_ayon_username
from ayon_core.pipeline.template_data import (
get_template_data,
get_task_template_data,
@ -23,6 +24,8 @@ from ayon_core.tools.workfiles.abstract import (
WorkfileInfo,
)
_NOT_SET = object()
class CommentMatcher(object):
"""Use anatomy and work file data to parse comments from filenames.
@ -188,10 +191,17 @@ class WorkareaModel:
if ext not in self._extensions:
continue
modified = os.path.getmtime(filepath)
items.append(
FileItem(workdir, filename, modified)
workfile_info = self._controller.get_workfile_info(
folder_id, task_id, filepath
)
modified = os.path.getmtime(filepath)
items.append(FileItem(
workdir,
filename,
modified,
workfile_info.created_by,
workfile_info.updated_by,
))
return items
def _get_template_key(self, fill_data):
@ -439,6 +449,7 @@ class WorkfileEntitiesModel:
self._controller = controller
self._cache = {}
self._items = {}
self._current_username = _NOT_SET
def _get_workfile_info_identifier(
self, folder_id, task_id, rootless_path
@ -459,8 +470,12 @@ class WorkfileEntitiesModel:
self, folder_id, task_id, workfile_info, filepath
):
note = ""
created_by = None
updated_by = None
if workfile_info:
note = workfile_info["attrib"].get("description") or ""
created_by = workfile_info.get("createdBy")
updated_by = workfile_info.get("updatedBy")
filestat = os.stat(filepath)
return WorkfileInfo(
@ -470,6 +485,8 @@ class WorkfileEntitiesModel:
filesize=filestat.st_size,
creation_time=filestat.st_ctime,
modification_time=filestat.st_mtime,
created_by=created_by,
updated_by=updated_by,
note=note
)
@ -481,7 +498,7 @@ class WorkfileEntitiesModel:
for workfile_info in ayon_api.get_workfiles_info(
self._controller.get_current_project_name(),
task_ids=[task_id],
fields=["id", "path", "attrib"],
fields=["id", "path", "attrib", "createdBy", "updatedBy"],
):
workfile_identifier = self._get_workfile_info_identifier(
folder_id, task_id, workfile_info["path"]
@ -525,18 +542,32 @@ class WorkfileEntitiesModel:
self._items.pop(identifier, None)
return
if note is None:
return
old_note = workfile_info.get("attrib", {}).get("note")
new_workfile_info = copy.deepcopy(workfile_info)
attrib = new_workfile_info.setdefault("attrib", {})
attrib["description"] = note
update_data = {}
if note is not None and old_note != note:
update_data["attrib"] = {"description": note}
attrib = new_workfile_info.setdefault("attrib", {})
attrib["description"] = note
username = self._get_current_username()
# Automatically fix 'createdBy' and 'updatedBy' fields
# NOTE both fields were not automatically filled by server
# until 1.1.3 release.
if workfile_info.get("createdBy") is None:
update_data["createdBy"] = username
new_workfile_info["createdBy"] = username
if workfile_info.get("updatedBy") != username:
update_data["updatedBy"] = username
new_workfile_info["updatedBy"] = username
if not update_data:
return
self._cache[identifier] = new_workfile_info
self._items.pop(identifier, None)
if old_note == note:
return
project_name = self._controller.get_current_project_name()
@ -545,7 +576,7 @@ class WorkfileEntitiesModel:
project_name,
"workfile",
workfile_info["id"],
{"attrib": {"description": note}},
update_data,
)
session.commit()
@ -554,13 +585,18 @@ class WorkfileEntitiesModel:
project_name = self._controller.get_current_project_name()
username = self._get_current_username()
workfile_info = {
"path": rootless_path,
"taskId": task_id,
"attrib": {
"extension": extension,
"description": note
}
},
# TODO remove 'createdBy' and 'updatedBy' fields when server is
# or above 1.1.3 .
"createdBy": username,
"updatedBy": username,
}
session = OperationsSession()
@ -568,6 +604,11 @@ class WorkfileEntitiesModel:
session.commit()
return workfile_info
def _get_current_username(self):
if self._current_username is _NOT_SET:
self._current_username = get_ayon_username()
return self._current_username
class PublishWorkfilesModel:
"""Model for handling of published workfiles.
@ -599,7 +640,7 @@ class PublishWorkfilesModel:
return self._cached_repre_extensions
def _file_item_from_representation(
self, repre_entity, project_anatomy, task_name=None
self, repre_entity, project_anatomy, author, task_name=None
):
if task_name is not None:
task_info = repre_entity["context"].get("task")
@ -634,6 +675,8 @@ class PublishWorkfilesModel:
dirpath,
filename,
created_at.float_timestamp,
author,
None,
repre_entity["id"]
)
@ -643,9 +686,9 @@ class PublishWorkfilesModel:
# Get subset docs of folder
product_entities = ayon_api.get_products(
project_name,
folder_ids=[folder_id],
product_types=["workfile"],
fields=["id", "name"]
folder_ids={folder_id},
product_types={"workfile"},
fields={"id", "name"}
)
output = []
@ -657,25 +700,33 @@ class PublishWorkfilesModel:
version_entities = ayon_api.get_versions(
project_name,
product_ids=product_ids,
fields=["id", "productId"]
fields={"id", "author"}
)
version_ids = {version["id"] for version in version_entities}
if not version_ids:
versions_by_id = {
version["id"]: version
for version in version_entities
}
if not versions_by_id:
return output
# Query representations of filtered versions and add filter for
# extension
repre_entities = ayon_api.get_representations(
project_name,
version_ids=version_ids
version_ids=set(versions_by_id)
)
project_anatomy = self._controller.project_anatomy
# Filter queried representations by task name if task is set
file_items = []
for repre_entity in repre_entities:
version_id = repre_entity["versionId"]
version_entity = versions_by_id[version_id]
file_item = self._file_item_from_representation(
repre_entity, project_anatomy, task_name
repre_entity,
project_anatomy,
version_entity["author"],
task_name,
)
if file_item is not None:
file_items.append(file_item)

View file

@ -13,7 +13,8 @@ from .utils import BaseOverlayFrame
REPRE_ID_ROLE = QtCore.Qt.UserRole + 1
FILEPATH_ROLE = QtCore.Qt.UserRole + 2
DATE_MODIFIED_ROLE = QtCore.Qt.UserRole + 3
AUTHOR_ROLE = QtCore.Qt.UserRole + 3
DATE_MODIFIED_ROLE = QtCore.Qt.UserRole + 4
class PublishedFilesModel(QtGui.QStandardItemModel):
@ -23,13 +24,19 @@ class PublishedFilesModel(QtGui.QStandardItemModel):
controller (AbstractWorkfilesFrontend): The control object.
"""
columns = [
"Name",
"Author",
"Date Modified",
]
date_modified_col = columns.index("Date Modified")
def __init__(self, controller):
super(PublishedFilesModel, self).__init__()
self.setColumnCount(2)
self.setHeaderData(0, QtCore.Qt.Horizontal, "Name")
self.setHeaderData(1, QtCore.Qt.Horizontal, "Date Modified")
self.setColumnCount(len(self.columns))
for idx, label in enumerate(self.columns):
self.setHeaderData(idx, QtCore.Qt.Horizontal, label)
controller.register_event_callback(
"selection.task.changed",
@ -185,6 +192,8 @@ class PublishedFilesModel(QtGui.QStandardItemModel):
self._remove_empty_item()
self._remove_missing_context_item()
user_items_by_name = self._controller.get_user_items_by_name()
items_to_remove = set(self._items_by_id.keys())
new_items = []
for file_item in file_items:
@ -205,8 +214,15 @@ class PublishedFilesModel(QtGui.QStandardItemModel):
else:
flags = QtCore.Qt.NoItemFlags
author = file_item.created_by
user_item = user_items_by_name.get(author)
if user_item is not None and user_item.full_name:
author = user_item.full_name
item.setFlags(flags)
item.setData(file_item.filepath, FILEPATH_ROLE)
item.setData(author, AUTHOR_ROLE)
item.setData(file_item.modified, DATE_MODIFIED_ROLE)
self._items_by_id[repre_id] = item
@ -225,22 +241,30 @@ class PublishedFilesModel(QtGui.QStandardItemModel):
# Use flags of first column for all columns
if index.column() != 0:
index = self.index(index.row(), 0, index.parent())
return super(PublishedFilesModel, self).flags(index)
return super().flags(index)
def data(self, index, role=None):
if role is None:
role = QtCore.Qt.DisplayRole
# Handle roles for first column
if index.column() == 1:
if role == QtCore.Qt.DecorationRole:
return None
col = index.column()
if col != 1:
return super().data(index, role)
if role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
if role == QtCore.Qt.DecorationRole:
return None
if role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
if col == 1:
role = AUTHOR_ROLE
elif col == 2:
role = DATE_MODIFIED_ROLE
index = self.index(index.row(), 0, index.parent())
else:
return None
index = self.index(index.row(), 0, index.parent())
return super(PublishedFilesModel, self).data(index, role)
return super().data(index, role)
class SelectContextOverlay(BaseOverlayFrame):
@ -295,7 +319,7 @@ class PublishedFilesWidget(QtWidgets.QWidget):
view.setModel(proxy_model)
time_delegate = PrettyTimeDelegate()
view.setItemDelegateForColumn(1, time_delegate)
view.setItemDelegateForColumn(model.date_modified_col, time_delegate)
# Default to a wider first filename column it is what we mostly care
# about and the date modified is relatively small anyway.

View file

@ -10,7 +10,8 @@ from ayon_core.tools.utils.delegates import PrettyTimeDelegate
FILENAME_ROLE = QtCore.Qt.UserRole + 1
FILEPATH_ROLE = QtCore.Qt.UserRole + 2
DATE_MODIFIED_ROLE = QtCore.Qt.UserRole + 3
AUTHOR_ROLE = QtCore.Qt.UserRole + 3
DATE_MODIFIED_ROLE = QtCore.Qt.UserRole + 4
class WorkAreaFilesModel(QtGui.QStandardItemModel):
@ -21,14 +22,20 @@ class WorkAreaFilesModel(QtGui.QStandardItemModel):
"""
refreshed = QtCore.Signal()
columns = [
"Name",
"Author",
"Date Modified",
]
date_modified_col = columns.index("Date Modified")
def __init__(self, controller):
super(WorkAreaFilesModel, self).__init__()
self.setColumnCount(2)
self.setColumnCount(len(self.columns))
self.setHeaderData(0, QtCore.Qt.Horizontal, "Name")
self.setHeaderData(1, QtCore.Qt.Horizontal, "Date Modified")
for idx, label in enumerate(self.columns):
self.setHeaderData(idx, QtCore.Qt.Horizontal, label)
controller.register_event_callback(
"selection.folder.changed",
@ -186,6 +193,7 @@ class WorkAreaFilesModel(QtGui.QStandardItemModel):
return
self._remove_empty_item()
self._remove_missing_context_item()
user_items_by_name = self._controller.get_user_items_by_name()
items_to_remove = set(self._items_by_filename.keys())
new_items = []
@ -205,7 +213,13 @@ class WorkAreaFilesModel(QtGui.QStandardItemModel):
item.setData(file_item.filename, QtCore.Qt.DisplayRole)
item.setData(file_item.filename, FILENAME_ROLE)
updated_by = file_item.updated_by
user_item = user_items_by_name.get(updated_by)
if user_item is not None and user_item.full_name:
updated_by = user_item.full_name
item.setData(file_item.filepath, FILEPATH_ROLE)
item.setData(updated_by, AUTHOR_ROLE)
item.setData(file_item.modified, DATE_MODIFIED_ROLE)
self._items_by_filename[file_item.filename] = item
@ -224,22 +238,30 @@ class WorkAreaFilesModel(QtGui.QStandardItemModel):
# Use flags of first column for all columns
if index.column() != 0:
index = self.index(index.row(), 0, index.parent())
return super(WorkAreaFilesModel, self).flags(index)
return super().flags(index)
def data(self, index, role=None):
if role is None:
role = QtCore.Qt.DisplayRole
# Handle roles for first column
if index.column() == 1:
if role == QtCore.Qt.DecorationRole:
return None
col = index.column()
if col == 0:
return super().data(index, role)
if role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
if role == QtCore.Qt.DecorationRole:
return None
if role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
if col == 1:
role = AUTHOR_ROLE
elif col == 2:
role = DATE_MODIFIED_ROLE
index = self.index(index.row(), 0, index.parent())
else:
return None
index = self.index(index.row(), 0, index.parent())
return super(WorkAreaFilesModel, self).data(index, role)
return super().data(index, role)
def set_published_mode(self, published_mode):
if self._published_mode == published_mode:
@ -279,7 +301,7 @@ class WorkAreaFilesWidget(QtWidgets.QWidget):
view.setModel(proxy_model)
time_delegate = PrettyTimeDelegate()
view.setItemDelegateForColumn(1, time_delegate)
view.setItemDelegateForColumn(model.date_modified_col, time_delegate)
# Default to a wider first filename column it is what we mostly care
# about and the date modified is relatively small anyway.

View file

@ -147,13 +147,38 @@ class SidePanelWidget(QtWidgets.QWidget):
workfile_info.creation_time)
modification_time = datetime.datetime.fromtimestamp(
workfile_info.modification_time)
user_items_by_name = self._controller.get_user_items_by_name()
def convert_username(username):
user_item = user_items_by_name.get(username)
if user_item is not None and user_item.full_name:
return user_item.full_name
return username
created_lines = [
creation_time.strftime(datetime_format)
]
if workfile_info.created_by:
created_lines.insert(
0, convert_username(workfile_info.created_by)
)
modified_lines = [
modification_time.strftime(datetime_format)
]
if workfile_info.updated_by:
modified_lines.insert(
0, convert_username(workfile_info.updated_by)
)
lines = (
"<b>Size:</b>",
size_value,
"<b>Created:</b>",
creation_time.strftime(datetime_format),
"<br/>".join(created_lines),
"<b>Modified:</b>",
modification_time.strftime(datetime_format)
"<br/>".join(modified_lines),
)
self._orig_note = note
self._note_input.setPlainText(note)

View file

@ -107,7 +107,7 @@ class WorkfilesToolWindow(QtWidgets.QWidget):
split_widget.addWidget(tasks_widget)
split_widget.addWidget(col_3_widget)
split_widget.addWidget(side_panel)
split_widget.setSizes([255, 160, 455, 175])
split_widget.setSizes([255, 175, 550, 190])
body_layout.addWidget(split_widget)
@ -169,7 +169,7 @@ class WorkfilesToolWindow(QtWidgets.QWidget):
# Force focus on the open button by default, required for Houdini.
self._files_widget.setFocus()
self.resize(1200, 600)
self.resize(1260, 600)
def _create_col_1_widget(self, controller, parent):
col_widget = QtWidgets.QWidget(parent)

View file

@ -2,7 +2,11 @@ from typing import Any
from ayon_server.addons import BaseServerAddon
from .settings import CoreSettings, DEFAULT_VALUES
from .settings import (
CoreSettings,
DEFAULT_VALUES,
convert_settings_overrides,
)
class CoreAddon(BaseServerAddon):
@ -17,47 +21,8 @@ class CoreAddon(BaseServerAddon):
source_version: str,
overrides: dict[str, Any],
) -> dict[str, Any]:
self._convert_imagio_configs_0_3_1(overrides)
convert_settings_overrides(source_version, overrides)
# Use super conversion
return await super().convert_settings_overrides(
source_version, overrides
)
def _convert_imagio_configs_0_3_1(self, overrides):
"""Imageio config settings did change to profiles since 0.3.1. ."""
imageio_overrides = overrides.get("imageio") or {}
if (
"ocio_config" not in imageio_overrides
or "filepath" not in imageio_overrides["ocio_config"]
):
return
ocio_config = imageio_overrides.pop("ocio_config")
filepath = ocio_config["filepath"]
if not filepath:
return
first_filepath = filepath[0]
ocio_config_profiles = imageio_overrides.setdefault(
"ocio_config_profiles", []
)
base_value = {
"type": "builtin_path",
"product_name": "",
"host_names": [],
"task_names": [],
"task_types": [],
"custom_path": "",
"builtin_path": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio"
}
if first_filepath in (
"{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio",
"{BUILTIN_OCIO_ROOT}/nuke-default/config.ocio",
):
base_value["type"] = "builtin_path"
base_value["builtin_path"] = first_filepath
else:
base_value["type"] = "custom_path"
base_value["custom_path"] = first_filepath
ocio_config_profiles.append(base_value)

View file

@ -1,7 +1,10 @@
from .main import CoreSettings, DEFAULT_VALUES
from .conversion import convert_settings_overrides
__all__ = (
"CoreSettings",
"DEFAULT_VALUES",
"convert_settings_overrides",
)

View file

@ -0,0 +1,86 @@
import copy
from typing import Any
from .publish_plugins import DEFAULT_PUBLISH_VALUES
def _convert_imageio_configs_0_3_1(overrides):
"""Imageio config settings did change to profiles since 0.3.1. ."""
imageio_overrides = overrides.get("imageio") or {}
if (
"ocio_config" not in imageio_overrides
or "filepath" not in imageio_overrides["ocio_config"]
):
return
ocio_config = imageio_overrides.pop("ocio_config")
filepath = ocio_config["filepath"]
if not filepath:
return
first_filepath = filepath[0]
ocio_config_profiles = imageio_overrides.setdefault(
"ocio_config_profiles", []
)
base_value = {
"type": "builtin_path",
"product_name": "",
"host_names": [],
"task_names": [],
"task_types": [],
"custom_path": "",
"builtin_path": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio"
}
if first_filepath in (
"{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio",
"{BUILTIN_OCIO_ROOT}/nuke-default/config.ocio",
):
base_value["type"] = "builtin_path"
base_value["builtin_path"] = first_filepath
else:
base_value["type"] = "custom_path"
base_value["custom_path"] = first_filepath
ocio_config_profiles.append(base_value)
def _convert_validate_version_0_3_3(publish_overrides):
"""ValidateVersion plugin changed in 0.3.3."""
if "ValidateVersion" not in publish_overrides:
return
validate_version = publish_overrides["ValidateVersion"]
# Already new settings
if "plugin_state_profiles" in validate_version:
return
# Use new default profile as base
profile = copy.deepcopy(
DEFAULT_PUBLISH_VALUES["ValidateVersion"]["plugin_state_profiles"][0]
)
# Copy values from old overrides to new overrides
for key in {
"enabled",
"optional",
"active",
}:
if key not in validate_version:
continue
profile[key] = validate_version.pop(key)
validate_version["plugin_state_profiles"] = [profile]
def _conver_publish_plugins(overrides):
if "publish" not in overrides:
return
_convert_validate_version_0_3_3(overrides["publish"])
def convert_settings_overrides(
source_version: str,
overrides: dict[str, Any],
) -> dict[str, Any]:
_convert_imageio_configs_0_3_1(overrides)
_conver_publish_plugins(overrides)
return overrides

View file

@ -59,7 +59,7 @@ class CollectFramesFixDefModel(BaseSettingsModel):
)
class ValidateOutdatedContainersProfile(BaseSettingsModel):
class PluginStateByHostModelProfile(BaseSettingsModel):
_layout = "expanded"
# Filtering
host_names: list[str] = SettingsField(
@ -72,17 +72,12 @@ class ValidateOutdatedContainersProfile(BaseSettingsModel):
active: bool = SettingsField(True, title="Active")
class ValidateOutdatedContainersModel(BaseSettingsModel):
"""Validate if Publishing intent was selected.
It is possible to disable validation for specific publishing context
with profiles.
"""
class PluginStateByHostModel(BaseSettingsModel):
_isGroup = True
plugin_state_profiles: list[ValidateOutdatedContainersProfile] = SettingsField(
plugin_state_profiles: list[PluginStateByHostModelProfile] = SettingsField(
default_factory=list,
title="Plugin enable state profiles",
description="Change plugin state based on host name."
)
@ -793,12 +788,16 @@ class PublishPuginsModel(BaseSettingsModel):
default_factory=ValidateBaseModel,
title="Validate Editorial Asset Name"
)
ValidateVersion: ValidateBaseModel = SettingsField(
default_factory=ValidateBaseModel,
title="Validate Version"
ValidateVersion: PluginStateByHostModel = SettingsField(
default_factory=PluginStateByHostModel,
title="Validate Version",
description=(
"Validate that product version to integrate"
" is newer than latest version in AYON."
)
)
ValidateOutdatedContainers: ValidateOutdatedContainersModel = SettingsField(
default_factory=ValidateOutdatedContainersModel,
ValidateOutdatedContainers: PluginStateByHostModel = SettingsField(
default_factory=PluginStateByHostModel,
title="Validate Containers"
)
ValidateIntent: ValidateIntentModel = SettingsField(
@ -882,9 +881,21 @@ DEFAULT_PUBLISH_VALUES = {
"active": True
},
"ValidateVersion": {
"enabled": True,
"optional": False,
"active": True
"plugin_state_profiles": [
{
"host_names": [
"aftereffects",
"blender",
"houdini",
"maya",
"nuke",
"photoshop",
],
"enabled": True,
"optional": False,
"active": True
}
]
},
"ValidateOutdatedContainers": {
"plugin_state_profiles": [