diff --git a/client/ayon_core/tools/workfiles/abstract.py b/client/ayon_core/tools/workfiles/abstract.py
index 863d6bb9bc..e7a038575a 100644
--- a/client/ayon_core/tools/workfiles/abstract.py
+++ b/client/ayon_core/tools/workfiles/abstract.py
@@ -787,6 +787,33 @@ class AbstractWorkfilesFrontend(AbstractWorkfilesCommon):
"""
pass
+ @abstractmethod
+ def get_published_workfile_info(self, representation_id: str):
+ """Get published workfile info by representation ID.
+
+ Args:
+ representation_id (str): Representation id.
+
+ Returns:
+ Optional[PublishedWorkfileInfo]: Published workfile info or None
+ if not found.
+
+ """
+ pass
+
+ @abstractmethod
+ def get_published_workfile_version_comment(self, representation_id: str):
+ """Get version comment for published workfile.
+
+ Args:
+ representation_id (str): Representation id.
+
+ Returns:
+ Optional[str]: Version comment or None.
+
+ """
+ pass
+
@abstractmethod
def get_workfile_info(self, folder_id, task_id, rootless_path):
"""Workfile info from database.
diff --git a/client/ayon_core/tools/workfiles/control.py b/client/ayon_core/tools/workfiles/control.py
index f0e0f0e416..13a88e325f 100644
--- a/client/ayon_core/tools/workfiles/control.py
+++ b/client/ayon_core/tools/workfiles/control.py
@@ -432,6 +432,16 @@ class BaseWorkfileController(
folder_id, task_id
)
+ def get_published_workfile_info(self, representation_id):
+ return self._workfiles_model.get_published_workfile_info(
+ representation_id
+ )
+
+ def get_published_workfile_version_comment(self, representation_id):
+ return self._workfiles_model.get_published_workfile_version_comment(
+ representation_id
+ )
+
def get_workfile_info(self, folder_id, task_id, rootless_path):
return self._workfiles_model.get_workfile_info(
folder_id, task_id, rootless_path
diff --git a/client/ayon_core/tools/workfiles/models/workfiles.py b/client/ayon_core/tools/workfiles/models/workfiles.py
index 5b5591fe43..5055c203be 100644
--- a/client/ayon_core/tools/workfiles/models/workfiles.py
+++ b/client/ayon_core/tools/workfiles/models/workfiles.py
@@ -79,6 +79,7 @@ class WorkfilesModel:
# Published workfiles
self._repre_by_id = {}
+ self._version_by_repre_id = {}
self._published_workfile_items_cache = NestedCacheItem(
levels=1, default_factory=list
)
@@ -95,6 +96,7 @@ class WorkfilesModel:
self._workarea_file_items_cache.reset()
self._repre_by_id = {}
+ self._version_by_repre_id = {}
self._published_workfile_items_cache.reset()
self._workfile_entities_by_task_id = {}
@@ -586,7 +588,7 @@ class WorkfilesModel:
version_entities = list(ayon_api.get_versions(
project_name,
product_ids=product_ids,
- fields={"id", "author", "taskId"},
+ fields={"id", "author", "taskId", "attrib.comment"},
))
repre_entities = []
@@ -600,6 +602,20 @@ class WorkfilesModel:
repre_entity["id"]: repre_entity
for repre_entity in repre_entities
})
+
+ # Map versions by representation ID for easy lookup
+ version_by_id = {
+ version_entity["id"]: version_entity
+ for version_entity in version_entities
+ }
+ for repre_entity in repre_entities:
+ repre_id = repre_entity["id"]
+ version_id = repre_entity.get("versionId")
+ if version_id and version_id in version_by_id:
+ self._version_by_repre_id[repre_id] = version_by_id[
+ version_id
+ ]
+
project_entity = self._controller.get_project_entity(project_name)
prepared_data = ListPublishedWorkfilesOptionalData(
@@ -626,6 +642,52 @@ class WorkfilesModel:
]
return items
+ def get_published_workfile_info(
+ self, representation_id: str
+ ) -> Optional[PublishedWorkfileInfo]:
+ """Get published workfile info by representation ID.
+
+ Args:
+ representation_id (str): Representation id.
+
+ Returns:
+ Optional[PublishedWorkfileInfo]: Published workfile info or None
+ if not found.
+
+ """
+ if not representation_id:
+ return None
+
+ # Search through all cached published workfile items
+ cache_items = self._published_workfile_items_cache._data_by_key
+ for folder_cache in cache_items.values():
+ if folder_cache.is_valid:
+ for item in folder_cache.get_data():
+ if item.representation_id == representation_id:
+ return item
+ return None
+
+ def get_published_workfile_version_comment(
+ self, representation_id: str
+ ) -> Optional[str]:
+ """Get version comment for published workfile.
+
+ Args:
+ representation_id (str): Representation id.
+
+ Returns:
+ Optional[str]: Version comment or None.
+
+ """
+ if not representation_id:
+ return None
+
+ version_entity = self._version_by_repre_id.get(representation_id)
+ if version_entity:
+ attrib = version_entity.get("attrib") or {}
+ return attrib.get("comment")
+ return None
+
@property
def _project_name(self) -> str:
return self._controller.get_current_project_name()
diff --git a/client/ayon_core/tools/workfiles/widgets/side_panel.py b/client/ayon_core/tools/workfiles/widgets/side_panel.py
index b1b91d9721..2834f1dec8 100644
--- a/client/ayon_core/tools/workfiles/widgets/side_panel.py
+++ b/client/ayon_core/tools/workfiles/widgets/side_panel.py
@@ -1,17 +1,13 @@
import datetime
-from qtpy import QtWidgets, QtCore
+from qtpy import QtCore, QtWidgets
def file_size_to_string(file_size):
if not file_size:
return "N/A"
size = 0
- size_ending_mapping = {
- "KB": 1024 ** 1,
- "MB": 1024 ** 2,
- "GB": 1024 ** 3
- }
+ size_ending_mapping = {"KB": 1024**1, "MB": 1024**2, "GB": 1024**3}
ending = "B"
for _ending, _size in size_ending_mapping.items():
if file_size < _size:
@@ -70,7 +66,11 @@ class SidePanelWidget(QtWidgets.QWidget):
btn_description_save.clicked.connect(self._on_save_click)
controller.register_event_callback(
- "selection.workarea.changed", self._on_selection_change
+ "selection.workarea.changed", self._on_workarea_selection_change
+ )
+ controller.register_event_callback(
+ "selection.representation.changed",
+ self._on_representation_selection_change,
)
self._details_input = details_input
@@ -82,10 +82,11 @@ class SidePanelWidget(QtWidgets.QWidget):
self._task_id = None
self._filepath = None
self._rootless_path = None
+ self._representation_id = None
self._orig_description = ""
self._controller = controller
- self._set_context(None, None, None, None)
+ self._set_context(None, None, None, None, None)
def set_published_mode(self, published_mode):
"""Change published mode.
@@ -95,14 +96,21 @@ class SidePanelWidget(QtWidgets.QWidget):
"""
self._description_widget.setVisible(not published_mode)
+ # Clear the context when switching modes to avoid showing stale data
+ self._set_context(None, None, None, None, None)
- def _on_selection_change(self, event):
+ def _on_workarea_selection_change(self, event):
folder_id = event["folder_id"]
task_id = event["task_id"]
filepath = event["path"]
rootless_path = event["rootless_path"]
- self._set_context(folder_id, task_id, rootless_path, filepath)
+ self._set_context(folder_id, task_id, rootless_path, filepath, None)
+
+ def _on_representation_selection_change(self, event):
+ representation_id = event["representation_id"]
+
+ self._set_context(None, None, None, None, representation_id)
def _on_description_change(self):
text = self._description_input.toPlainText()
@@ -118,23 +126,45 @@ class SidePanelWidget(QtWidgets.QWidget):
self._orig_description = description
self._btn_description_save.setEnabled(False)
- def _set_context(self, folder_id, task_id, rootless_path, filepath):
+ def _set_context(
+ self, folder_id, task_id, rootless_path, filepath, representation_id
+ ):
workfile_info = None
- # Check if folder, task and file are selected
+ published_workfile_info = None
+
+ # Check if folder, task and file are selected (workarea mode)
if folder_id and task_id and rootless_path:
workfile_info = self._controller.get_workfile_info(
folder_id, task_id, rootless_path
)
- enabled = workfile_info is not None
+ # Check if representation is selected (published mode)
+ elif representation_id:
+ published_workfile_info = (
+ self._controller.get_published_workfile_info(representation_id)
+ )
+
+ # Get version comment for published workfiles
+ version_comment = None
+ if representation_id and published_workfile_info:
+ version_comment = (
+ self._controller.get_published_workfile_version_comment(
+ representation_id
+ )
+ )
+
+ enabled = (
+ workfile_info is not None or published_workfile_info is not None
+ )
self._details_input.setEnabled(enabled)
- self._description_input.setEnabled(enabled)
- self._btn_description_save.setEnabled(enabled)
+ self._description_input.setEnabled(workfile_info is not None)
+ self._btn_description_save.setEnabled(workfile_info is not None)
self._folder_id = folder_id
self._task_id = task_id
self._filepath = filepath
self._rootless_path = rootless_path
+ self._representation_id = representation_id
# Disable inputs and remove texts if any required arguments are
# missing
@@ -144,19 +174,28 @@ class SidePanelWidget(QtWidgets.QWidget):
self._description_input.setPlainText("")
return
- description = workfile_info.description
- size_value = file_size_to_string(workfile_info.file_size)
+ # Use published workfile info if available, otherwise use workarea
+ # info
+ info = (
+ published_workfile_info
+ if published_workfile_info
+ else workfile_info
+ )
+
+ description = info.description if hasattr(info, "description") else ""
+ size_value = file_size_to_string(info.file_size)
# Append html string
datetime_format = "%b %d %Y %H:%M:%S"
- file_created = workfile_info.file_created
- modification_time = workfile_info.file_modified
+ file_created = info.file_created
+ modification_time = info.file_modified
if file_created:
file_created = datetime.datetime.fromtimestamp(file_created)
if modification_time:
modification_time = datetime.datetime.fromtimestamp(
- modification_time)
+ modification_time
+ )
user_items_by_name = self._controller.get_user_items_by_name()
@@ -167,34 +206,47 @@ class SidePanelWidget(QtWidgets.QWidget):
return username
created_lines = []
- if workfile_info.created_by:
- created_lines.append(
- convert_username(workfile_info.created_by)
- )
- if file_created:
- created_lines.append(file_created.strftime(datetime_format))
+ # For published workfiles, use 'author' field
+ if published_workfile_info:
+ if published_workfile_info.author:
+ created_lines.append(
+ convert_username(published_workfile_info.author)
+ )
+ if file_created:
+ created_lines.append(file_created.strftime(datetime_format))
+ else:
+ # For workarea workfiles, use 'created_by' field
+ if workfile_info.created_by:
+ created_lines.append(
+ convert_username(workfile_info.created_by)
+ )
+ if file_created:
+ created_lines.append(file_created.strftime(datetime_format))
if created_lines:
created_lines.insert(0, "Created:")
modified_lines = []
- if workfile_info.updated_by:
- modified_lines.append(
- convert_username(workfile_info.updated_by)
- )
+ # For workarea workfiles, show 'updated_by'
+ if workfile_info and workfile_info.updated_by:
+ modified_lines.append(convert_username(workfile_info.updated_by))
if modification_time:
- modified_lines.append(
- modification_time.strftime(datetime_format)
- )
+ modified_lines.append(modification_time.strftime(datetime_format))
if modified_lines:
modified_lines.insert(0, "Modified:")
- lines = (
+ lines = [
"Size:",
size_value,
- "
".join(created_lines),
- "
".join(modified_lines),
- )
+ ]
+ # Add version comment for published workfiles
+ if version_comment:
+ lines.append(f"Comment:
{version_comment}")
+ if created_lines:
+ lines.append("
".join(created_lines))
+ if modified_lines:
+ lines.append("
".join(modified_lines))
+
self._orig_description = description
self._description_input.setPlainText(description)