mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 12:54:40 +01:00
Merge branch 'develop' into enhancement/product-name-template-settings
This commit is contained in:
commit
4051d679dd
7 changed files with 251 additions and 51 deletions
|
|
@ -29,6 +29,7 @@ from .lib import (
|
||||||
get_publish_template_name,
|
get_publish_template_name,
|
||||||
|
|
||||||
publish_plugins_discover,
|
publish_plugins_discover,
|
||||||
|
filter_crashed_publish_paths,
|
||||||
load_help_content_from_plugin,
|
load_help_content_from_plugin,
|
||||||
load_help_content_from_filepath,
|
load_help_content_from_filepath,
|
||||||
|
|
||||||
|
|
@ -87,6 +88,7 @@ __all__ = (
|
||||||
"get_publish_template_name",
|
"get_publish_template_name",
|
||||||
|
|
||||||
"publish_plugins_discover",
|
"publish_plugins_discover",
|
||||||
|
"filter_crashed_publish_paths",
|
||||||
"load_help_content_from_plugin",
|
"load_help_content_from_plugin",
|
||||||
"load_help_content_from_filepath",
|
"load_help_content_from_filepath",
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
"""Library functions for publishing."""
|
"""Library functions for publishing."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
import inspect
|
import inspect
|
||||||
import copy
|
import copy
|
||||||
|
|
@ -8,19 +10,19 @@ import warnings
|
||||||
import hashlib
|
import hashlib
|
||||||
import xml.etree.ElementTree
|
import xml.etree.ElementTree
|
||||||
from typing import TYPE_CHECKING, Optional, Union, List, Any
|
from typing import TYPE_CHECKING, Optional, Union, List, Any
|
||||||
import clique
|
|
||||||
import speedcopy
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import pyblish.util
|
|
||||||
import pyblish.plugin
|
|
||||||
import pyblish.api
|
|
||||||
|
|
||||||
from ayon_api import (
|
from ayon_api import (
|
||||||
get_server_api_connection,
|
get_server_api_connection,
|
||||||
get_representations,
|
get_representations,
|
||||||
get_last_version_by_product_name
|
get_last_version_by_product_name
|
||||||
)
|
)
|
||||||
|
import clique
|
||||||
|
import pyblish.util
|
||||||
|
import pyblish.plugin
|
||||||
|
import pyblish.api
|
||||||
|
import speedcopy
|
||||||
|
|
||||||
from ayon_core.lib import (
|
from ayon_core.lib import (
|
||||||
import_filepath,
|
import_filepath,
|
||||||
Logger,
|
Logger,
|
||||||
|
|
@ -246,6 +248,67 @@ def load_help_content_from_plugin(
|
||||||
return load_help_content_from_filepath(filepath)
|
return load_help_content_from_filepath(filepath)
|
||||||
|
|
||||||
|
|
||||||
|
def filter_crashed_publish_paths(
|
||||||
|
project_name: str,
|
||||||
|
crashed_paths: set[str],
|
||||||
|
*,
|
||||||
|
project_settings: Optional[dict[str, Any]] = None,
|
||||||
|
) -> set[str]:
|
||||||
|
"""Filter crashed paths happened during plugins discovery.
|
||||||
|
|
||||||
|
Check if plugins discovery has enabled strict mode and filter crashed
|
||||||
|
paths that happened during discover based on regexes from settings.
|
||||||
|
|
||||||
|
Publishing should not start if any paths are returned.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
project_name (str): Project name in which context plugins discovery
|
||||||
|
happened.
|
||||||
|
crashed_paths (set[str]): Crashed paths from plugins discovery report.
|
||||||
|
project_settings (Optional[dict[str, Any]]): Project settings.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
set[str]: Filtered crashed paths.
|
||||||
|
|
||||||
|
"""
|
||||||
|
filtered_paths = set()
|
||||||
|
# Nothing crashed all good...
|
||||||
|
if not crashed_paths:
|
||||||
|
return filtered_paths
|
||||||
|
|
||||||
|
if project_settings is None:
|
||||||
|
project_settings = get_project_settings(project_name)
|
||||||
|
|
||||||
|
discover_validation = (
|
||||||
|
project_settings["core"]["tools"]["publish"]["discover_validation"]
|
||||||
|
)
|
||||||
|
# Strict mode is not enabled.
|
||||||
|
if not discover_validation["enabled"]:
|
||||||
|
return filtered_paths
|
||||||
|
|
||||||
|
regexes = [
|
||||||
|
re.compile(value, re.IGNORECASE)
|
||||||
|
for value in discover_validation["ignore_paths"]
|
||||||
|
if value
|
||||||
|
]
|
||||||
|
is_windows = platform.system().lower() == "windows"
|
||||||
|
# Fitler path with regexes from settings
|
||||||
|
for path in crashed_paths:
|
||||||
|
# Normalize paths to use forward slashes on windows
|
||||||
|
if is_windows:
|
||||||
|
path = path.replace("\\", "/")
|
||||||
|
is_invalid = True
|
||||||
|
for regex in regexes:
|
||||||
|
if regex.match(path):
|
||||||
|
is_invalid = False
|
||||||
|
break
|
||||||
|
|
||||||
|
if is_invalid:
|
||||||
|
filtered_paths.add(path)
|
||||||
|
|
||||||
|
return filtered_paths
|
||||||
|
|
||||||
|
|
||||||
def publish_plugins_discover(
|
def publish_plugins_discover(
|
||||||
paths: Optional[list[str]] = None) -> DiscoverResult:
|
paths: Optional[list[str]] = None) -> DiscoverResult:
|
||||||
"""Find and return available pyblish plug-ins.
|
"""Find and return available pyblish plug-ins.
|
||||||
|
|
@ -1099,14 +1162,16 @@ def main_cli_publish(
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
context = get_global_context()
|
||||||
|
project_settings = get_project_settings(context["project_name"])
|
||||||
|
|
||||||
install_ayon_plugins()
|
install_ayon_plugins()
|
||||||
|
|
||||||
if addons_manager is None:
|
if addons_manager is None:
|
||||||
addons_manager = AddonsManager()
|
addons_manager = AddonsManager(project_settings)
|
||||||
|
|
||||||
applications_addon = addons_manager.get_enabled_addon("applications")
|
applications_addon = addons_manager.get_enabled_addon("applications")
|
||||||
if applications_addon is not None:
|
if applications_addon is not None:
|
||||||
context = get_global_context()
|
|
||||||
env = applications_addon.get_farm_publish_environment_variables(
|
env = applications_addon.get_farm_publish_environment_variables(
|
||||||
context["project_name"],
|
context["project_name"],
|
||||||
context["folder_path"],
|
context["folder_path"],
|
||||||
|
|
@ -1129,17 +1194,33 @@ def main_cli_publish(
|
||||||
log.info("Running publish ...")
|
log.info("Running publish ...")
|
||||||
|
|
||||||
discover_result = publish_plugins_discover()
|
discover_result = publish_plugins_discover()
|
||||||
publish_plugins = discover_result.plugins
|
|
||||||
print(discover_result.get_report(only_errors=False))
|
print(discover_result.get_report(only_errors=False))
|
||||||
|
|
||||||
|
filtered_crashed_paths = filter_crashed_publish_paths(
|
||||||
|
context["project_name"],
|
||||||
|
set(discover_result.crashed_file_paths),
|
||||||
|
project_settings=project_settings,
|
||||||
|
)
|
||||||
|
if filtered_crashed_paths:
|
||||||
|
joined_paths = "\n".join([
|
||||||
|
f"- {path}"
|
||||||
|
for path in filtered_crashed_paths
|
||||||
|
])
|
||||||
|
log.error(
|
||||||
|
"Plugin discovery strict mode is enabled."
|
||||||
|
" Crashed plugin paths that prevent from publishing:"
|
||||||
|
f"\n{joined_paths}"
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
publish_plugins = discover_result.plugins
|
||||||
|
|
||||||
# Error exit as soon as any error occurs.
|
# Error exit as soon as any error occurs.
|
||||||
error_format = ("Failed {plugin.__name__}: "
|
error_format = "Failed {plugin.__name__}: {error} -- {error.traceback}"
|
||||||
"{error} -- {error.traceback}")
|
|
||||||
|
|
||||||
for result in pyblish.util.publish_iter(plugins=publish_plugins):
|
for result in pyblish.util.publish_iter(plugins=publish_plugins):
|
||||||
if result["error"]:
|
if result["error"]:
|
||||||
log.error(error_format.format(**result))
|
log.error(error_format.format(**result))
|
||||||
# uninstall()
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
log.info("Publish finished.")
|
log.info("Publish finished.")
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ from ayon_core.pipeline.plugin_discover import DiscoverResult
|
||||||
from ayon_core.pipeline.publish import (
|
from ayon_core.pipeline.publish import (
|
||||||
get_publish_instance_label,
|
get_publish_instance_label,
|
||||||
PublishError,
|
PublishError,
|
||||||
|
filter_crashed_publish_paths,
|
||||||
)
|
)
|
||||||
from ayon_core.tools.publisher.abstract import AbstractPublisherBackend
|
from ayon_core.tools.publisher.abstract import AbstractPublisherBackend
|
||||||
|
|
||||||
|
|
@ -107,11 +108,14 @@ class PublishReportMaker:
|
||||||
creator_discover_result: Optional[DiscoverResult] = None,
|
creator_discover_result: Optional[DiscoverResult] = None,
|
||||||
convertor_discover_result: Optional[DiscoverResult] = None,
|
convertor_discover_result: Optional[DiscoverResult] = None,
|
||||||
publish_discover_result: Optional[DiscoverResult] = None,
|
publish_discover_result: Optional[DiscoverResult] = None,
|
||||||
|
blocking_crashed_paths: Optional[list[str]] = None,
|
||||||
):
|
):
|
||||||
self._create_discover_result: Union[DiscoverResult, None] = None
|
self._create_discover_result: Union[DiscoverResult, None] = None
|
||||||
self._convert_discover_result: Union[DiscoverResult, None] = None
|
self._convert_discover_result: Union[DiscoverResult, None] = None
|
||||||
self._publish_discover_result: Union[DiscoverResult, None] = None
|
self._publish_discover_result: Union[DiscoverResult, None] = None
|
||||||
|
|
||||||
|
self._blocking_crashed_paths: list[str] = []
|
||||||
|
|
||||||
self._all_instances_by_id: Dict[str, pyblish.api.Instance] = {}
|
self._all_instances_by_id: Dict[str, pyblish.api.Instance] = {}
|
||||||
self._plugin_data_by_id: Dict[str, Any] = {}
|
self._plugin_data_by_id: Dict[str, Any] = {}
|
||||||
self._current_plugin_id: Optional[str] = None
|
self._current_plugin_id: Optional[str] = None
|
||||||
|
|
@ -120,6 +124,7 @@ class PublishReportMaker:
|
||||||
creator_discover_result,
|
creator_discover_result,
|
||||||
convertor_discover_result,
|
convertor_discover_result,
|
||||||
publish_discover_result,
|
publish_discover_result,
|
||||||
|
blocking_crashed_paths,
|
||||||
)
|
)
|
||||||
|
|
||||||
def reset(
|
def reset(
|
||||||
|
|
@ -127,12 +132,14 @@ class PublishReportMaker:
|
||||||
creator_discover_result: Union[DiscoverResult, None],
|
creator_discover_result: Union[DiscoverResult, None],
|
||||||
convertor_discover_result: Union[DiscoverResult, None],
|
convertor_discover_result: Union[DiscoverResult, None],
|
||||||
publish_discover_result: Union[DiscoverResult, None],
|
publish_discover_result: Union[DiscoverResult, None],
|
||||||
|
blocking_crashed_paths: list[str],
|
||||||
):
|
):
|
||||||
"""Reset report and clear all data."""
|
"""Reset report and clear all data."""
|
||||||
|
|
||||||
self._create_discover_result = creator_discover_result
|
self._create_discover_result = creator_discover_result
|
||||||
self._convert_discover_result = convertor_discover_result
|
self._convert_discover_result = convertor_discover_result
|
||||||
self._publish_discover_result = publish_discover_result
|
self._publish_discover_result = publish_discover_result
|
||||||
|
self._blocking_crashed_paths = blocking_crashed_paths
|
||||||
|
|
||||||
self._all_instances_by_id = {}
|
self._all_instances_by_id = {}
|
||||||
self._plugin_data_by_id = {}
|
self._plugin_data_by_id = {}
|
||||||
|
|
@ -242,9 +249,10 @@ class PublishReportMaker:
|
||||||
"instances": instances_details,
|
"instances": instances_details,
|
||||||
"context": self._extract_context_data(publish_context),
|
"context": self._extract_context_data(publish_context),
|
||||||
"crashed_file_paths": crashed_file_paths,
|
"crashed_file_paths": crashed_file_paths,
|
||||||
|
"blocking_crashed_paths": list(self._blocking_crashed_paths),
|
||||||
"id": uuid.uuid4().hex,
|
"id": uuid.uuid4().hex,
|
||||||
"created_at": now.isoformat(),
|
"created_at": now.isoformat(),
|
||||||
"report_version": "1.1.0",
|
"report_version": "1.1.1",
|
||||||
}
|
}
|
||||||
|
|
||||||
def _add_plugin_data_item(self, plugin: pyblish.api.Plugin):
|
def _add_plugin_data_item(self, plugin: pyblish.api.Plugin):
|
||||||
|
|
@ -959,11 +967,16 @@ class PublishModel:
|
||||||
self._publish_plugins_proxy = PublishPluginsProxy(
|
self._publish_plugins_proxy = PublishPluginsProxy(
|
||||||
publish_plugins
|
publish_plugins
|
||||||
)
|
)
|
||||||
|
blocking_crashed_paths = filter_crashed_publish_paths(
|
||||||
|
create_context.get_current_project_name(),
|
||||||
|
set(create_context.publish_discover_result.crashed_file_paths),
|
||||||
|
project_settings=create_context.get_current_project_settings(),
|
||||||
|
)
|
||||||
self._publish_report.reset(
|
self._publish_report.reset(
|
||||||
create_context.creator_discover_result,
|
create_context.creator_discover_result,
|
||||||
create_context.convertor_discover_result,
|
create_context.convertor_discover_result,
|
||||||
create_context.publish_discover_result,
|
create_context.publish_discover_result,
|
||||||
|
blocking_crashed_paths,
|
||||||
)
|
)
|
||||||
for plugin in create_context.publish_plugins_mismatch_targets:
|
for plugin in create_context.publish_plugins_mismatch_targets:
|
||||||
self._publish_report.set_plugin_skipped(plugin.id)
|
self._publish_report.set_plugin_skipped(plugin.id)
|
||||||
|
|
|
||||||
|
|
@ -139,3 +139,6 @@ class PublishReport:
|
||||||
self.logs = logs
|
self.logs = logs
|
||||||
|
|
||||||
self.crashed_plugin_paths = report_data["crashed_file_paths"]
|
self.crashed_plugin_paths = report_data["crashed_file_paths"]
|
||||||
|
self.blocking_crashed_paths = report_data.get(
|
||||||
|
"blocking_crashed_paths", []
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ from ayon_core.tools.utils import (
|
||||||
SeparatorWidget,
|
SeparatorWidget,
|
||||||
IconButton,
|
IconButton,
|
||||||
paint_image_with_color,
|
paint_image_with_color,
|
||||||
|
get_qt_icon,
|
||||||
)
|
)
|
||||||
from ayon_core.resources import get_image_path
|
from ayon_core.resources import get_image_path
|
||||||
from ayon_core.style import get_objected_colors
|
from ayon_core.style import get_objected_colors
|
||||||
|
|
@ -46,10 +47,13 @@ def get_pretty_milliseconds(value):
|
||||||
|
|
||||||
|
|
||||||
class PluginLoadReportModel(QtGui.QStandardItemModel):
|
class PluginLoadReportModel(QtGui.QStandardItemModel):
|
||||||
|
_blocking_icon = None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._traceback_by_filepath = {}
|
self._traceback_by_filepath = {}
|
||||||
self._items_by_filepath = {}
|
self._items_by_filepath = {}
|
||||||
|
self._blocking_crashed_paths = set()
|
||||||
self._is_active = True
|
self._is_active = True
|
||||||
self._need_refresh = False
|
self._need_refresh = False
|
||||||
|
|
||||||
|
|
@ -75,6 +79,7 @@ class PluginLoadReportModel(QtGui.QStandardItemModel):
|
||||||
|
|
||||||
for filepath in to_remove:
|
for filepath in to_remove:
|
||||||
self._traceback_by_filepath.pop(filepath)
|
self._traceback_by_filepath.pop(filepath)
|
||||||
|
self._blocking_crashed_paths = set(report.blocking_crashed_paths)
|
||||||
self._update_items()
|
self._update_items()
|
||||||
|
|
||||||
def _update_items(self):
|
def _update_items(self):
|
||||||
|
|
@ -83,6 +88,7 @@ class PluginLoadReportModel(QtGui.QStandardItemModel):
|
||||||
parent = self.invisibleRootItem()
|
parent = self.invisibleRootItem()
|
||||||
if not self._traceback_by_filepath:
|
if not self._traceback_by_filepath:
|
||||||
parent.removeRows(0, parent.rowCount())
|
parent.removeRows(0, parent.rowCount())
|
||||||
|
self._items_by_filepath = {}
|
||||||
return
|
return
|
||||||
|
|
||||||
new_items = []
|
new_items = []
|
||||||
|
|
@ -91,12 +97,18 @@ class PluginLoadReportModel(QtGui.QStandardItemModel):
|
||||||
set(self._items_by_filepath) - set(self._traceback_by_filepath)
|
set(self._items_by_filepath) - set(self._traceback_by_filepath)
|
||||||
)
|
)
|
||||||
for filepath in self._traceback_by_filepath:
|
for filepath in self._traceback_by_filepath:
|
||||||
if filepath in self._items_by_filepath:
|
item = self._items_by_filepath.get(filepath)
|
||||||
continue
|
if item is None:
|
||||||
item = QtGui.QStandardItem(filepath)
|
item = QtGui.QStandardItem(filepath)
|
||||||
new_items.append(item)
|
new_items.append(item)
|
||||||
new_items_by_filepath[filepath] = item
|
new_items_by_filepath[filepath] = item
|
||||||
self._items_by_filepath[filepath] = item
|
self._items_by_filepath[filepath] = item
|
||||||
|
|
||||||
|
icon = None
|
||||||
|
if filepath.replace("\\", "/") in self._blocking_crashed_paths:
|
||||||
|
icon = self._get_blocking_icon()
|
||||||
|
|
||||||
|
item.setData(icon, QtCore.Qt.DecorationRole)
|
||||||
|
|
||||||
if new_items:
|
if new_items:
|
||||||
parent.appendRows(new_items)
|
parent.appendRows(new_items)
|
||||||
|
|
@ -113,6 +125,16 @@ class PluginLoadReportModel(QtGui.QStandardItemModel):
|
||||||
item = self._items_by_filepath.pop(filepath)
|
item = self._items_by_filepath.pop(filepath)
|
||||||
parent.removeRow(item.row())
|
parent.removeRow(item.row())
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _get_blocking_icon(cls):
|
||||||
|
if cls._blocking_icon is None:
|
||||||
|
cls._blocking_icon = get_qt_icon({
|
||||||
|
"type": "material-symbols",
|
||||||
|
"name": "block",
|
||||||
|
"color": "red",
|
||||||
|
})
|
||||||
|
return cls._blocking_icon
|
||||||
|
|
||||||
|
|
||||||
class DetailWidget(QtWidgets.QTextEdit):
|
class DetailWidget(QtWidgets.QTextEdit):
|
||||||
def __init__(self, text, *args, **kwargs):
|
def __init__(self, text, *args, **kwargs):
|
||||||
|
|
@ -856,7 +878,7 @@ class PublishReportViewerWidget(QtWidgets.QFrame):
|
||||||
report = PublishReport(report_data)
|
report = PublishReport(report_data)
|
||||||
self.set_report(report)
|
self.set_report(report)
|
||||||
|
|
||||||
def set_report(self, report):
|
def set_report(self, report: PublishReport) -> None:
|
||||||
self._ignore_selection_changes = True
|
self._ignore_selection_changes = True
|
||||||
|
|
||||||
self._report_item = report
|
self._report_item = report
|
||||||
|
|
@ -866,6 +888,10 @@ class PublishReportViewerWidget(QtWidgets.QFrame):
|
||||||
self._logs_text_widget.set_report(report)
|
self._logs_text_widget.set_report(report)
|
||||||
self._plugin_load_report_widget.set_report(report)
|
self._plugin_load_report_widget.set_report(report)
|
||||||
self._plugins_details_widget.set_report(report)
|
self._plugins_details_widget.set_report(report)
|
||||||
|
if report.blocking_crashed_paths:
|
||||||
|
self._details_tab_widget.setCurrentWidget(
|
||||||
|
self._plugin_load_report_widget
|
||||||
|
)
|
||||||
|
|
||||||
self._ignore_selection_changes = False
|
self._ignore_selection_changes = False
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
import collections
|
import collections
|
||||||
import copy
|
import copy
|
||||||
from typing import Optional
|
from typing import Optional, Any
|
||||||
|
|
||||||
from qtpy import QtWidgets, QtCore, QtGui
|
from qtpy import QtWidgets, QtCore, QtGui
|
||||||
|
|
||||||
|
|
@ -393,6 +395,9 @@ class PublisherWindow(QtWidgets.QDialog):
|
||||||
self._publish_frame_visible = None
|
self._publish_frame_visible = None
|
||||||
self._tab_on_reset = None
|
self._tab_on_reset = None
|
||||||
|
|
||||||
|
self._create_context_valid: bool = True
|
||||||
|
self._blocked_by_crashed_paths: bool = False
|
||||||
|
|
||||||
self._error_messages_to_show = collections.deque()
|
self._error_messages_to_show = collections.deque()
|
||||||
self._errors_dialog_message_timer = errors_dialog_message_timer
|
self._errors_dialog_message_timer = errors_dialog_message_timer
|
||||||
|
|
||||||
|
|
@ -406,6 +411,8 @@ class PublisherWindow(QtWidgets.QDialog):
|
||||||
self._show_counter = 0
|
self._show_counter = 0
|
||||||
self._window_is_visible = False
|
self._window_is_visible = False
|
||||||
|
|
||||||
|
self._update_footer_state()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def controller(self) -> AbstractPublisherFrontend:
|
def controller(self) -> AbstractPublisherFrontend:
|
||||||
"""Kept for compatibility with traypublisher."""
|
"""Kept for compatibility with traypublisher."""
|
||||||
|
|
@ -664,11 +671,33 @@ class PublisherWindow(QtWidgets.QDialog):
|
||||||
|
|
||||||
self._tab_on_reset = tab
|
self._tab_on_reset = tab
|
||||||
|
|
||||||
def _update_publish_details_widget(self, force=False):
|
def set_current_tab(self, tab):
|
||||||
if not force and not self._is_on_details_tab():
|
if tab == "create":
|
||||||
|
self._go_to_create_tab()
|
||||||
|
elif tab == "publish":
|
||||||
|
self._go_to_publish_tab()
|
||||||
|
elif tab == "report":
|
||||||
|
self._go_to_report_tab()
|
||||||
|
elif tab == "details":
|
||||||
|
self._go_to_details_tab()
|
||||||
|
|
||||||
|
if not self._window_is_visible:
|
||||||
|
self.set_tab_on_reset(tab)
|
||||||
|
|
||||||
|
def _update_publish_details_widget(
|
||||||
|
self,
|
||||||
|
force: bool = False,
|
||||||
|
report_data: Optional[dict[str, Any]] = None,
|
||||||
|
) -> None:
|
||||||
|
if (
|
||||||
|
report_data is None
|
||||||
|
and not force
|
||||||
|
and not self._is_on_details_tab()
|
||||||
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
report_data = self._controller.get_publish_report()
|
if report_data is None:
|
||||||
|
report_data = self._controller.get_publish_report()
|
||||||
self._publish_details_widget.set_report_data(report_data)
|
self._publish_details_widget.set_report_data(report_data)
|
||||||
|
|
||||||
def _on_help_click(self):
|
def _on_help_click(self):
|
||||||
|
|
@ -752,19 +781,6 @@ class PublisherWindow(QtWidgets.QDialog):
|
||||||
def _set_current_tab(self, identifier):
|
def _set_current_tab(self, identifier):
|
||||||
self._tabs_widget.set_current_tab(identifier)
|
self._tabs_widget.set_current_tab(identifier)
|
||||||
|
|
||||||
def set_current_tab(self, tab):
|
|
||||||
if tab == "create":
|
|
||||||
self._go_to_create_tab()
|
|
||||||
elif tab == "publish":
|
|
||||||
self._go_to_publish_tab()
|
|
||||||
elif tab == "report":
|
|
||||||
self._go_to_report_tab()
|
|
||||||
elif tab == "details":
|
|
||||||
self._go_to_details_tab()
|
|
||||||
|
|
||||||
if not self._window_is_visible:
|
|
||||||
self.set_tab_on_reset(tab)
|
|
||||||
|
|
||||||
def _is_current_tab(self, identifier):
|
def _is_current_tab(self, identifier):
|
||||||
return self._tabs_widget.is_current_tab(identifier)
|
return self._tabs_widget.is_current_tab(identifier)
|
||||||
|
|
||||||
|
|
@ -865,26 +881,56 @@ class PublisherWindow(QtWidgets.QDialog):
|
||||||
# Reset style
|
# Reset style
|
||||||
self._comment_input.setStyleSheet("")
|
self._comment_input.setStyleSheet("")
|
||||||
|
|
||||||
def _set_footer_enabled(self, enabled):
|
def _set_create_context_valid(self, valid: bool) -> None:
|
||||||
self._save_btn.setEnabled(True)
|
self._create_context_valid = valid
|
||||||
|
self._update_footer_state()
|
||||||
|
|
||||||
|
def _set_blocked(self, blocked: bool) -> None:
|
||||||
|
self._blocked_by_crashed_paths = blocked
|
||||||
|
self._overview_widget.setEnabled(not blocked)
|
||||||
|
self._update_footer_state()
|
||||||
|
if not blocked:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.set_tab_on_reset("details")
|
||||||
|
self._go_to_details_tab()
|
||||||
|
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self,
|
||||||
|
"Failed to load plugins",
|
||||||
|
(
|
||||||
|
"Failed to load plugins that do prevent you from"
|
||||||
|
" using publish tool.\n"
|
||||||
|
"Please contact your TD or administrator."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def _update_footer_state(self) -> None:
|
||||||
|
enabled = (
|
||||||
|
not self._blocked_by_crashed_paths
|
||||||
|
and self._create_context_valid
|
||||||
|
)
|
||||||
|
save_enabled = not self._blocked_by_crashed_paths
|
||||||
|
|
||||||
|
self._save_btn.setEnabled(save_enabled)
|
||||||
self._reset_btn.setEnabled(True)
|
self._reset_btn.setEnabled(True)
|
||||||
if enabled:
|
self._stop_btn.setEnabled(False)
|
||||||
self._stop_btn.setEnabled(False)
|
self._validate_btn.setEnabled(enabled)
|
||||||
self._validate_btn.setEnabled(True)
|
self._publish_btn.setEnabled(enabled)
|
||||||
self._publish_btn.setEnabled(True)
|
|
||||||
else:
|
|
||||||
self._stop_btn.setEnabled(enabled)
|
|
||||||
self._validate_btn.setEnabled(enabled)
|
|
||||||
self._publish_btn.setEnabled(enabled)
|
|
||||||
|
|
||||||
def _on_publish_reset(self):
|
def _on_publish_reset(self):
|
||||||
self._create_tab.setEnabled(True)
|
self._create_tab.setEnabled(True)
|
||||||
self._set_comment_input_visiblity(True)
|
self._set_comment_input_visiblity(True)
|
||||||
self._set_publish_overlay_visibility(False)
|
self._set_publish_overlay_visibility(False)
|
||||||
self._set_publish_visibility(False)
|
self._set_publish_visibility(False)
|
||||||
self._update_publish_details_widget()
|
|
||||||
|
report_data = self._controller.get_publish_report()
|
||||||
|
blocked = bool(report_data["blocking_crashed_paths"])
|
||||||
|
self._set_blocked(blocked)
|
||||||
|
self._update_publish_details_widget(report_data=report_data)
|
||||||
|
|
||||||
def _on_controller_reset(self):
|
def _on_controller_reset(self):
|
||||||
|
self._update_publish_details_widget(force=True)
|
||||||
self._first_reset, first_reset = False, self._first_reset
|
self._first_reset, first_reset = False, self._first_reset
|
||||||
if self._tab_on_reset is not None:
|
if self._tab_on_reset is not None:
|
||||||
self._tab_on_reset, new_tab = None, self._tab_on_reset
|
self._tab_on_reset, new_tab = None, self._tab_on_reset
|
||||||
|
|
@ -952,7 +998,7 @@ class PublisherWindow(QtWidgets.QDialog):
|
||||||
|
|
||||||
def _validate_create_instances(self):
|
def _validate_create_instances(self):
|
||||||
if not self._controller.is_host_valid():
|
if not self._controller.is_host_valid():
|
||||||
self._set_footer_enabled(True)
|
self._set_create_context_valid(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
active_instances_by_id = {
|
active_instances_by_id = {
|
||||||
|
|
@ -973,7 +1019,7 @@ class PublisherWindow(QtWidgets.QDialog):
|
||||||
if all_valid is None:
|
if all_valid is None:
|
||||||
all_valid = True
|
all_valid = True
|
||||||
|
|
||||||
self._set_footer_enabled(bool(all_valid))
|
self._set_create_context_valid(bool(all_valid))
|
||||||
|
|
||||||
def _on_create_model_reset(self):
|
def _on_create_model_reset(self):
|
||||||
self._validate_create_instances()
|
self._validate_create_instances()
|
||||||
|
|
|
||||||
|
|
@ -356,6 +356,27 @@ class CustomStagingDirProfileModel(BaseSettingsModel):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DiscoverValidationModel(BaseSettingsModel):
|
||||||
|
"""Strictly validate publish plugins discovery.
|
||||||
|
|
||||||
|
Artist won't be able to publish if path to publish plugin fails to be
|
||||||
|
imported.
|
||||||
|
|
||||||
|
"""
|
||||||
|
_isGroup = True
|
||||||
|
enabled: bool = SettingsField(
|
||||||
|
False,
|
||||||
|
description="Enable strict mode of plugins discovery",
|
||||||
|
)
|
||||||
|
ignore_paths: list[str] = SettingsField(
|
||||||
|
default_factory=list,
|
||||||
|
title="Ignored paths (regex)",
|
||||||
|
description=(
|
||||||
|
"Paths that do match regex will be skipped in validation."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PublishToolModel(BaseSettingsModel):
|
class PublishToolModel(BaseSettingsModel):
|
||||||
template_name_profiles: list[PublishTemplateNameProfile] = SettingsField(
|
template_name_profiles: list[PublishTemplateNameProfile] = SettingsField(
|
||||||
default_factory=list,
|
default_factory=list,
|
||||||
|
|
@ -373,6 +394,10 @@ class PublishToolModel(BaseSettingsModel):
|
||||||
title="Custom Staging Dir Profiles"
|
title="Custom Staging Dir Profiles"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
discover_validation: DiscoverValidationModel = SettingsField(
|
||||||
|
default_factory=DiscoverValidationModel,
|
||||||
|
title="Validate plugins discovery",
|
||||||
|
)
|
||||||
comment_minimum_required_chars: int = SettingsField(
|
comment_minimum_required_chars: int = SettingsField(
|
||||||
0,
|
0,
|
||||||
title="Publish comment minimum required characters",
|
title="Publish comment minimum required characters",
|
||||||
|
|
@ -705,6 +730,10 @@ DEFAULT_TOOLS_VALUES = {
|
||||||
"template_name": "simpleUnrealTextureHero"
|
"template_name": "simpleUnrealTextureHero"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"discover_validation": {
|
||||||
|
"enabled": False,
|
||||||
|
"ignore_paths": [],
|
||||||
|
},
|
||||||
"comment_minimum_required_chars": 0,
|
"comment_minimum_required_chars": 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue