From 050f816ce4cca5c314c8cb19bf2e3af8088f34d7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:35:19 +0200 Subject: [PATCH 01/31] controller has new method 'get_error_info' --- client/ayon_core/tools/publisher/abstract.py | 9 ++- client/ayon_core/tools/publisher/control.py | 4 +- .../tools/publisher/models/__init__.py | 3 +- .../tools/publisher/models/publish.py | 58 ++++++++++++++----- .../tools/publisher/widgets/publish_frame.py | 6 +- 5 files changed, 53 insertions(+), 27 deletions(-) diff --git a/client/ayon_core/tools/publisher/abstract.py b/client/ayon_core/tools/publisher/abstract.py index 362fa38882..78f7756c96 100644 --- a/client/ayon_core/tools/publisher/abstract.py +++ b/client/ayon_core/tools/publisher/abstract.py @@ -26,7 +26,7 @@ ) if TYPE_CHECKING: - from .models import CreatorItem + from .models import CreatorItem, PublishErrorInfo class CardMessageTypes: @@ -537,14 +537,13 @@ def get_publish_max_progress(self) -> int: pass @abstractmethod - def get_publish_error_msg(self) -> Union[str, None]: + def get_publish_error_info(self) -> Union["PublishErrorInfo", None]: """Current error message which cause fail of publishing. Returns: - Union[str, None]: Message which will be showed to artist or - None. - """ + Union[PublishErrorInfo, None]: Error info or None. + """ pass @abstractmethod diff --git a/client/ayon_core/tools/publisher/control.py b/client/ayon_core/tools/publisher/control.py index 257b45de08..5c76e01f0c 100644 --- a/client/ayon_core/tools/publisher/control.py +++ b/client/ayon_core/tools/publisher/control.py @@ -493,8 +493,8 @@ def get_publish_max_progress(self): def get_publish_progress(self): return self._publish_model.get_progress() - def get_publish_error_msg(self): - return self._publish_model.get_error_msg() + def get_publish_error_info(self): + return self._publish_model.get_error_info() def get_publish_report(self): return self._publish_model.get_publish_report() diff --git a/client/ayon_core/tools/publisher/models/__init__.py b/client/ayon_core/tools/publisher/models/__init__.py index bd593be29b..07f061deaa 100644 --- a/client/ayon_core/tools/publisher/models/__init__.py +++ b/client/ayon_core/tools/publisher/models/__init__.py @@ -1,5 +1,5 @@ from .create import CreateModel, CreatorItem -from .publish import PublishModel +from .publish import PublishModel, PublishErrorInfo __all__ = ( @@ -7,4 +7,5 @@ "CreatorItem", "PublishModel", + "PublishErrorInfo", ) diff --git a/client/ayon_core/tools/publisher/models/publish.py b/client/ayon_core/tools/publisher/models/publish.py index a60ef69fac..e15f4f6080 100644 --- a/client/ayon_core/tools/publisher/models/publish.py +++ b/client/ayon_core/tools/publisher/models/publish.py @@ -23,6 +23,40 @@ PLUGIN_ORDER_OFFSET = 0.5 +class PublishErrorInfo: + def __init__(self, description, title, detail): + self.description = description + self.title = title + self.detail = detail + + def __eq__(self, other): + if not isinstance(other, PublishErrorInfo): + return False + return ( + self.description == other.description + and self.title == other.title + and self.detail == other.detail + ) + + def __ne__(self, other): + return not self.__eq__(other) + + @classmethod + def from_exception(cls, exc): + title = "This is not your fault" + detail = ( + "Please report the error to your pipeline support" + " using one of the options below." + ) + if isinstance(exc, KnownPublishError): + msg = str(exc) + else: + msg = ( + "Something went wrong. Send report" + " to your supervisor or Ynput team." + ) + return cls(msg, title, detail) + class PublishReportMaker: """Report for single publishing process. @@ -801,7 +835,7 @@ def __init__(self, controller: AbstractPublisherBackend): self._publish_comment_is_set: bool = False # Any other exception that happened during publishing - self._publish_error_msg: Optional[str] = None + self._publish_error_info: Optional[PublishErrorInfo] = None # Publishing is in progress self._publish_is_running: bool = False # Publishing is over validation order @@ -851,7 +885,7 @@ def reset(self): self._publish_comment_is_set = False self._publish_has_started = False - self._set_publish_error_msg(None) + self._set_publish_error_info(None) self._set_progress(0) self._set_is_running(False) self._set_has_validated(False) @@ -977,8 +1011,8 @@ def get_publish_report(self) -> Dict[str, Any]: def get_validation_errors(self) -> PublishValidationErrorsReport: return self._publish_validation_errors.create_report() - def get_error_msg(self) -> Optional[str]: - return self._publish_error_msg + def get_error_info(self) -> Optional[PublishErrorInfo]: + return self._publish_error_info def set_comment(self, comment: str): # Ignore change of comment when publishing started @@ -1077,9 +1111,9 @@ def _set_progress(self, value: int): {"value": value} ) - def _set_publish_error_msg(self, value: Optional[str]): - if self._publish_error_msg != value: - self._publish_error_msg = value + def _set_publish_error_info(self, value: Optional[PublishErrorInfo]): + if self._publish_error_info != value: + self._publish_error_info = value self._emit_event( "publish.publish_error.changed", {"value": value} @@ -1234,14 +1268,8 @@ def _process_and_continue( self._add_validation_error(result) else: - if isinstance(exception, KnownPublishError): - msg = str(exception) - else: - msg = ( - "Something went wrong. Send report" - " to your supervisor or Ynput team." - ) - self._set_publish_error_msg(msg) + error_info = PublishErrorInfo.from_exception(exception) + self._set_publish_error_info(error_info) self._set_is_crashed(True) result["is_validation_error"] = has_validation_error diff --git a/client/ayon_core/tools/publisher/widgets/publish_frame.py b/client/ayon_core/tools/publisher/widgets/publish_frame.py index 6eaeb6daf2..d9a9e501ef 100644 --- a/client/ayon_core/tools/publisher/widgets/publish_frame.py +++ b/client/ayon_core/tools/publisher/widgets/publish_frame.py @@ -411,10 +411,8 @@ def _set_error_msg(self): """Show error message to artist on publish crash.""" self._set_main_label("Error happened") - - self._message_label_top.setText( - self._controller.get_publish_error_msg() - ) + error_info = self._controller.get_publish_error_info() + self._message_label_top.setText(error_info.description) self._set_success_property(1) From 830dd068696dc2651f496d97d7fb3c16a335ada9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:39:22 +0200 Subject: [PATCH 02/31] added new 'PublishArtistError' --- client/ayon_core/pipeline/__init__.py | 2 + client/ayon_core/pipeline/publish/__init__.py | 2 + .../pipeline/publish/publish_plugins.py | 37 +++++++++++++------ 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/client/ayon_core/pipeline/__init__.py b/client/ayon_core/pipeline/__init__.py index 8fd00ee6b6..513dd10897 100644 --- a/client/ayon_core/pipeline/__init__.py +++ b/client/ayon_core/pipeline/__init__.py @@ -51,6 +51,7 @@ ) from .publish import ( + PublishArtistError, PublishValidationError, PublishXmlValidationError, KnownPublishError, @@ -164,6 +165,7 @@ "get_repres_contexts", # --- Publish --- + "PublishArtistError", "PublishValidationError", "PublishXmlValidationError", "KnownPublishError", diff --git a/client/ayon_core/pipeline/publish/__init__.py b/client/ayon_core/pipeline/publish/__init__.py index ab19b6e360..cf07d47e9a 100644 --- a/client/ayon_core/pipeline/publish/__init__.py +++ b/client/ayon_core/pipeline/publish/__init__.py @@ -9,6 +9,7 @@ AbstractMetaInstancePlugin, AbstractMetaContextPlugin, + PublishArtistError, PublishValidationError, PublishXmlValidationError, KnownPublishError, @@ -62,6 +63,7 @@ "AbstractMetaInstancePlugin", "AbstractMetaContextPlugin", + "PublishArtistError", "PublishValidationError", "PublishXmlValidationError", "KnownPublishError", diff --git a/client/ayon_core/pipeline/publish/publish_plugins.py b/client/ayon_core/pipeline/publish/publish_plugins.py index 6b1984d92b..ababe1da1e 100644 --- a/client/ayon_core/pipeline/publish/publish_plugins.py +++ b/client/ayon_core/pipeline/publish/publish_plugins.py @@ -25,27 +25,40 @@ class AbstractMetaContextPlugin(ABCMeta, ExplicitMetaPlugin): pass -class PublishValidationError(Exception): - """Validation error happened during publishing. - - This exception should be used when validation publishing failed. +class PublishArtistError(Exception): + """Publishing crashed because of known error. - Has additional UI specific attributes that may be handy for artist. + Message will be shown in UI for artist. Args: - message(str): Message of error. Short explanation an issue. - title(str): Title showed in UI. All instances are grouped under - single title. - description(str): Detailed description of an error. It is possible - to use Markdown syntax. - """ + message (str): Message of error. Short explanation an issue. + title (Optional[str]): Title showed in UI. + description (Optional[str]): Detailed description of an error. + It is possible to use Markdown syntax. + """ def __init__(self, message, title=None, description=None, detail=None): self.message = message self.title = title self.description = description or message self.detail = detail - super(PublishValidationError, self).__init__(message) + super().__init__(message) + + +class PublishValidationError(PublishArtistError): + """Validation error happened during publishing. + + This exception should be used when validation publishing failed. + + Publishing does not stop during validation order if this + exception is raised. + + Has additional UI specific attributes that may be handy for artist. + + Argument 'title' is used to group errors. + + """ + pass class PublishXmlValidationError(PublishValidationError): From f5c331bcb982b20ed5bb02b97ceb88e851a1276a Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:44:08 +0200 Subject: [PATCH 03/31] change docstring of KnownPublishError --- client/ayon_core/pipeline/__init__.py | 4 ++-- client/ayon_core/pipeline/publish/__init__.py | 6 ++++-- .../pipeline/publish/publish_plugins.py | 17 ++++++++--------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/client/ayon_core/pipeline/__init__.py b/client/ayon_core/pipeline/__init__.py index 513dd10897..cb9d4ee46a 100644 --- a/client/ayon_core/pipeline/__init__.py +++ b/client/ayon_core/pipeline/__init__.py @@ -51,10 +51,10 @@ ) from .publish import ( + KnownPublishError, PublishArtistError, PublishValidationError, PublishXmlValidationError, - KnownPublishError, AYONPyblishPluginMixin, OpenPypePyblishPluginMixin, OptionalPyblishPluginMixin, @@ -165,10 +165,10 @@ "get_repres_contexts", # --- Publish --- + "KnownPublishError", "PublishArtistError", "PublishValidationError", "PublishXmlValidationError", - "KnownPublishError", "AYONPyblishPluginMixin", "OpenPypePyblishPluginMixin", "OptionalPyblishPluginMixin", diff --git a/client/ayon_core/pipeline/publish/__init__.py b/client/ayon_core/pipeline/publish/__init__.py index cf07d47e9a..3f0bc0bb85 100644 --- a/client/ayon_core/pipeline/publish/__init__.py +++ b/client/ayon_core/pipeline/publish/__init__.py @@ -9,10 +9,11 @@ AbstractMetaInstancePlugin, AbstractMetaContextPlugin, + KnownPublishError, PublishArtistError, PublishValidationError, PublishXmlValidationError, - KnownPublishError, + AYONPyblishPluginMixin, OpenPypePyblishPluginMixin, OptionalPyblishPluginMixin, @@ -63,10 +64,11 @@ "AbstractMetaInstancePlugin", "AbstractMetaContextPlugin", + "KnownPublishError", "PublishArtistError", "PublishValidationError", "PublishXmlValidationError", - "KnownPublishError", + "AYONPyblishPluginMixin", "OpenPypePyblishPluginMixin", "OptionalPyblishPluginMixin", diff --git a/client/ayon_core/pipeline/publish/publish_plugins.py b/client/ayon_core/pipeline/publish/publish_plugins.py index ababe1da1e..70040d7395 100644 --- a/client/ayon_core/pipeline/publish/publish_plugins.py +++ b/client/ayon_core/pipeline/publish/publish_plugins.py @@ -25,6 +25,14 @@ class AbstractMetaContextPlugin(ABCMeta, ExplicitMetaPlugin): pass +class KnownPublishError(Exception): + """Publishing crashed because of known error. + + Artist can't affect source of the error. + """ + pass + + class PublishArtistError(Exception): """Publishing crashed because of known error. @@ -81,15 +89,6 @@ def __init__( ) -class KnownPublishError(Exception): - """Publishing crashed because of known error. - - Message will be shown in UI for artist. - """ - - pass - - class AYONPyblishPluginMixin: # TODO # executable_in_thread = False From f45af87787a8f06e656ddbbb24df043d940271f4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:48:00 +0200 Subject: [PATCH 04/31] handle PublishArtistError in a different way --- client/ayon_core/tools/publisher/models/publish.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/publisher/models/publish.py b/client/ayon_core/tools/publisher/models/publish.py index e15f4f6080..4afcc5772d 100644 --- a/client/ayon_core/tools/publisher/models/publish.py +++ b/client/ayon_core/tools/publisher/models/publish.py @@ -15,7 +15,10 @@ OptionalPyblishPluginMixin, ) from ayon_core.pipeline.plugin_discover import DiscoverResult -from ayon_core.pipeline.publish import get_publish_instance_label +from ayon_core.pipeline.publish import ( + get_publish_instance_label, + PublishArtistError, +) from ayon_core.tools.publisher.abstract import AbstractPublisherBackend PUBLISH_EVENT_SOURCE = "publisher.publish.model" @@ -43,6 +46,12 @@ def __ne__(self, other): @classmethod def from_exception(cls, exc): + if isinstance(exc, PublishArtistError): + return cls( + exc.description or exc.message, + title=exc.title, + detail=exc.detail, + ) title = "This is not your fault" detail = ( "Please report the error to your pipeline support" From 90bc20a783d8a95ee9d8a9eb9c325970153c268f Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:48:20 +0200 Subject: [PATCH 05/31] modified crash widget to show error info --- .../tools/publisher/widgets/report_page.py | 176 +++++++++++------- 1 file changed, 110 insertions(+), 66 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/report_page.py b/client/ayon_core/tools/publisher/widgets/report_page.py index ecf1376ec0..f63b58eab2 100644 --- a/client/ayon_core/tools/publisher/widgets/report_page.py +++ b/client/ayon_core/tools/publisher/widgets/report_page.py @@ -1454,6 +1454,73 @@ def set_plugins_filter(self, plugin_ids=None): self._update_instances() +class ErrorDetailWidget(QtWidgets.QWidget): + def __init__(self, parent): + super().__init__(parent) + + error_detail_top = ClickableFrame(self) + + error_detail_expand_btn = ClassicExpandBtn(error_detail_top) + error_detail_expand_label = QtWidgets.QLabel( + "Details", error_detail_top) + + line_widget = SeparatorWidget(1, parent=error_detail_top) + + error_detail_top_l = QtWidgets.QHBoxLayout(error_detail_top) + error_detail_top_l.setContentsMargins(0, 0, 10, 0) + error_detail_top_l.addWidget(error_detail_expand_btn, 0) + error_detail_top_l.addWidget(error_detail_expand_label, 0) + error_detail_top_l.addWidget(line_widget, 1) + + error_detail_input = ExpandingTextEdit(self) + error_detail_input.setObjectName("InfoText") + error_detail_input.setTextInteractionFlags( + QtCore.Qt.TextBrowserInteraction + ) + + main_layout = QtWidgets.QVBoxLayout(self) + main_layout.setContentsMargins(0, 0, 0, 0) + main_layout.addWidget(error_detail_top, 0) + main_layout.addWidget(error_detail_input, 0) + main_layout.addStretch(1) + + error_detail_top.clicked.connect(self._on_detail_toggle) + + error_detail_input.setVisible(not error_detail_expand_btn.collapsed) + + self._error_detail_top = error_detail_top + self._error_detail_expand_btn = error_detail_expand_btn + self._error_detail_input = error_detail_input + + def set_detail(self, detail): + if not detail: + self._set_visible_inputs(False) + return + + if commonmark: + self._error_detail_input.setHtml( + commonmark.commonmark(detail) + ) + + elif hasattr(self._error_detail_input, "setMarkdown"): + self._error_detail_input.setMarkdown(detail) + + else: + self._error_detail_input.setText(detail) + + self._set_visible_inputs(True) + + def _set_visible_inputs(self, visible): + self._error_detail_top.setVisible(visible) + self._error_detail_input.setVisible(visible) + + def _on_detail_toggle(self): + self._error_detail_expand_btn.set_collapsed() + self._error_detail_input.setVisible( + not self._error_detail_expand_btn.collapsed + ) + + class CrashWidget(QtWidgets.QWidget): """Widget shown when publishing crashes. @@ -1466,20 +1533,16 @@ def __init__( ): super().__init__(parent) - main_label = QtWidgets.QLabel("This is not your fault", self) - main_label.setAlignment(QtCore.Qt.AlignCenter) - main_label.setObjectName("PublishCrashMainLabel") + title_label = QtWidgets.QLabel("", self) + title_label.setAlignment(QtCore.Qt.AlignCenter) + title_label.setObjectName("PublishCrashMainLabel") - report_label = QtWidgets.QLabel( - ( - "Please report the error to your pipeline support" - " using one of the options below." - ), - self - ) - report_label.setAlignment(QtCore.Qt.AlignCenter) - report_label.setWordWrap(True) - report_label.setObjectName("PublishCrashReportLabel") + description_label = QtWidgets.QLabel("", self) + description_label.setAlignment(QtCore.Qt.AlignCenter) + description_label.setWordWrap(True) + description_label.setObjectName("PublishCrashReportLabel") + + detail_widget = ErrorDetailWidget(self) btns_widget = QtWidgets.QWidget(self) copy_clipboard_btn = QtWidgets.QPushButton( @@ -1488,6 +1551,8 @@ def __init__( "Save to disk", btns_widget) btns_layout = QtWidgets.QHBoxLayout(btns_widget) + btns_layout.setContentsMargins(0, 0, 0, 0) + btns_layout.setSpacing(0) btns_layout.addStretch(1) btns_layout.addWidget(copy_clipboard_btn, 0) btns_layout.addSpacing(20) @@ -1495,19 +1560,38 @@ def __init__( btns_layout.addStretch(1) layout = QtWidgets.QVBoxLayout(self) - layout.addStretch(1) - layout.addWidget(main_label, 0) + layout.setContentsMargins(5, 5, 5, 5) + layout.setSpacing(0) + layout.addSpacing(20) + layout.addWidget(title_label, 0) layout.addSpacing(20) - layout.addWidget(report_label, 0) + layout.addWidget(description_label, 0) layout.addSpacing(20) + layout.addWidget(detail_widget, 1) + layout.addSpacing(10) layout.addWidget(btns_widget, 0) - layout.addStretch(2) + layout.addSpacing(10) copy_clipboard_btn.clicked.connect(self._on_copy_to_clipboard) save_to_disk_btn.clicked.connect(self._on_save_to_disk_click) + self._title_label = title_label + self._description_label = description_label + self._detail_widget = detail_widget self._controller: AbstractPublisherFrontend = controller + def update_error_info(self): + error_info = self._controller.get_publish_error_info() + if error_info is None: + self._title_label.setText("Placeholder title") + self._description_label.setText("A bug happened if you see this") + self._detail_widget.set_detail(None) + return + + self._title_label.setText(error_info.title) + self._description_label.setText(error_info.description) + self._detail_widget.set_detail(error_info.detail) + def _on_copy_to_clipboard(self): self._controller.emit_event( "copy_report.request", {}, "report_page") @@ -1517,7 +1601,7 @@ def _on_save_to_disk_click(self): "export_report.request", {}, "report_page") -class ErrorDetailsWidget(QtWidgets.QWidget): +class PublishFailWidget(QtWidgets.QWidget): def __init__(self, parent): super().__init__(parent) @@ -1529,35 +1613,7 @@ def __init__(self, parent): QtCore.Qt.TextBrowserInteraction ) - # Error 'Details' widget -> Collapsible - error_details_widget = QtWidgets.QWidget(inputs_widget) - - error_details_top = ClickableFrame(error_details_widget) - - error_details_expand_btn = ClassicExpandBtn(error_details_top) - error_details_expand_label = QtWidgets.QLabel( - "Details", error_details_top) - - line_widget = SeparatorWidget(1, parent=error_details_top) - - error_details_top_l = QtWidgets.QHBoxLayout(error_details_top) - error_details_top_l.setContentsMargins(0, 0, 10, 0) - error_details_top_l.addWidget(error_details_expand_btn, 0) - error_details_top_l.addWidget(error_details_expand_label, 0) - error_details_top_l.addWidget(line_widget, 1) - - error_details_input = ExpandingTextEdit(error_details_widget) - error_details_input.setObjectName("InfoText") - error_details_input.setTextInteractionFlags( - QtCore.Qt.TextBrowserInteraction - ) - error_details_input.setVisible(not error_details_expand_btn.collapsed) - - error_details_layout = QtWidgets.QVBoxLayout(error_details_widget) - error_details_layout.setContentsMargins(0, 0, 0, 0) - error_details_layout.addWidget(error_details_top, 0) - error_details_layout.addWidget(error_details_input, 0) - error_details_layout.addStretch(1) + error_details_widget = ErrorDetailWidget(inputs_widget) # Description and Details layout inputs_layout = QtWidgets.QVBoxLayout(inputs_widget) @@ -1570,17 +1626,8 @@ def __init__(self, parent): main_layout.setContentsMargins(0, 0, 0, 0) main_layout.addWidget(inputs_widget, 1) - error_details_top.clicked.connect(self._on_detail_toggle) - - self._error_details_widget = error_details_widget self._error_description_input = error_description_input - self._error_details_expand_btn = error_details_expand_btn - self._error_details_input = error_details_input - - def _on_detail_toggle(self): - self._error_details_expand_btn.set_collapsed() - self._error_details_input.setVisible( - not self._error_details_expand_btn.collapsed) + self._error_details_widget = error_details_widget def set_error_item(self, error_item): detail = "" @@ -1589,23 +1636,18 @@ def set_error_item(self, error_item): description = error_item.description or description detail = error_item.detail or detail + self._error_details_widget.set_detail(detail) + if commonmark: self._error_description_input.setHtml( commonmark.commonmark(description) ) - self._error_details_input.setHtml( - commonmark.commonmark(detail) - ) - elif hasattr(self._error_details_input, "setMarkdown"): + elif hasattr(self._error_description_input, "setMarkdown"): self._error_description_input.setMarkdown(description) - self._error_details_input.setMarkdown(detail) else: self._error_description_input.setText(description) - self._error_details_input.setText(detail) - - self._error_details_widget.setVisible(bool(detail)) class ReportsWidget(QtWidgets.QWidget): @@ -1671,7 +1713,7 @@ def __init__( detail_input_scroll = QtWidgets.QScrollArea(pages_widget) - detail_inputs_widget = ErrorDetailsWidget(detail_input_scroll) + detail_inputs_widget = PublishFailWidget(detail_input_scroll) detail_inputs_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) detail_input_scroll.setWidget(detail_inputs_widget) @@ -1768,6 +1810,8 @@ def update_data(self): self._crash_widget.setVisible(is_crashed) self._logs_view.setVisible(not is_crashed) + self._crash_widget.update_error_info() + # Instance view & logs update instance_items = self._get_instance_items() self._instances_view.update_instances(instance_items) From 2c362fb35fa73c35aa017402ded1619291e01d2c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:48:31 +0200 Subject: [PATCH 06/31] removed detail from default behavior --- client/ayon_core/tools/publisher/models/publish.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/client/ayon_core/tools/publisher/models/publish.py b/client/ayon_core/tools/publisher/models/publish.py index 4afcc5772d..020436a466 100644 --- a/client/ayon_core/tools/publisher/models/publish.py +++ b/client/ayon_core/tools/publisher/models/publish.py @@ -53,10 +53,6 @@ def from_exception(cls, exc): detail=exc.detail, ) title = "This is not your fault" - detail = ( - "Please report the error to your pipeline support" - " using one of the options below." - ) if isinstance(exc, KnownPublishError): msg = str(exc) else: @@ -64,7 +60,7 @@ def from_exception(cls, exc): "Something went wrong. Send report" " to your supervisor or Ynput team." ) - return cls(msg, title, detail) + return cls(msg, title) class PublishReportMaker: From a1ed54c44db24e9ff797c20eff5a6cb7372a5270 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:57:26 +0200 Subject: [PATCH 07/31] added some typehints --- .../tools/publisher/models/publish.py | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/tools/publisher/models/publish.py b/client/ayon_core/tools/publisher/models/publish.py index 020436a466..b01cfb30f9 100644 --- a/client/ayon_core/tools/publisher/models/publish.py +++ b/client/ayon_core/tools/publisher/models/publish.py @@ -27,12 +27,17 @@ class PublishErrorInfo: - def __init__(self, description, title, detail): - self.description = description - self.title = title - self.detail = detail + def __init__( + self, + description: str, + title: Optional[str], + detail: Optional[str] + ): + self.description: str = description + self.title: Optional[str] = title + self.detail: Optional[str] = detail - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: if not isinstance(other, PublishErrorInfo): return False return ( @@ -41,11 +46,11 @@ def __eq__(self, other): and self.detail == other.detail ) - def __ne__(self, other): + def __ne__(self, other: Any) -> bool: return not self.__eq__(other) @classmethod - def from_exception(cls, exc): + def from_exception(cls, exc) -> "PublishErrorInfo": if isinstance(exc, PublishArtistError): return cls( exc.description or exc.message, @@ -60,7 +65,7 @@ def from_exception(cls, exc): "Something went wrong. Send report" " to your supervisor or Ynput team." ) - return cls(msg, title) + return cls(msg, title, None) class PublishReportMaker: From 82ac6b332ca0aedd28d8b6f6bb162482656a80c7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 2 Sep 2024 14:11:13 +0200 Subject: [PATCH 08/31] normalize collapse/expand pixmaps --- client/ayon_core/tools/utils/widgets.py | 31 +++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/utils/widgets.py b/client/ayon_core/tools/utils/widgets.py index 73c8819758..4c2b418c41 100644 --- a/client/ayon_core/tools/utils/widgets.py +++ b/client/ayon_core/tools/utils/widgets.py @@ -540,11 +540,38 @@ class ClassicExpandBtnLabel(ExpandBtnLabel): right_arrow_path = get_style_image_path("right_arrow") down_arrow_path = get_style_image_path("down_arrow") + def _normalize_pixmap(self, pixmap): + if pixmap.width() == pixmap.height(): + return pixmap + width = pixmap.width() + height = pixmap.height() + size = max(width, height) + pos_x = 0 + pos_y = 0 + if width > height: + pos_y = (size - height) // 2 + else: + pos_x = (size - width) // 2 + + new_pix = QtGui.QPixmap(size, size) + new_pix.fill(QtCore.Qt.transparent) + painter = QtGui.QPainter(new_pix) + render_hints = ( + QtGui.QPainter.Antialiasing + | QtGui.QPainter.SmoothPixmapTransform + ) + if hasattr(QtGui.QPainter, "HighQualityAntialiasing"): + render_hints |= QtGui.QPainter.HighQualityAntialiasing + painter.setRenderHints(render_hints) + painter.drawPixmap(QtCore.QPoint(pos_x, pos_y), pixmap) + painter.end() + return new_pix + def _create_collapsed_pixmap(self): - return QtGui.QPixmap(self.right_arrow_path) + return self._normalize_pixmap(QtGui.QPixmap(self.right_arrow_path)) def _create_expanded_pixmap(self): - return QtGui.QPixmap(self.down_arrow_path) + return self._normalize_pixmap(QtGui.QPixmap(self.down_arrow_path)) class ClassicExpandBtn(ExpandBtn): From cd6739763c326a888dd1bef7e069340f29f7bc3a Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 2 Sep 2024 14:21:41 +0200 Subject: [PATCH 09/31] fix hiding details --- .../ayon_core/tools/publisher/widgets/report_page.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/report_page.py b/client/ayon_core/tools/publisher/widgets/report_page.py index f63b58eab2..e9fbd13808 100644 --- a/client/ayon_core/tools/publisher/widgets/report_page.py +++ b/client/ayon_core/tools/publisher/widgets/report_page.py @@ -1486,12 +1486,18 @@ def __init__(self, parent): error_detail_top.clicked.connect(self._on_detail_toggle) - error_detail_input.setVisible(not error_detail_expand_btn.collapsed) - self._error_detail_top = error_detail_top self._error_detail_expand_btn = error_detail_expand_btn self._error_detail_input = error_detail_input + def showEvent(self, event): + super().showEvent(event) + # Calling this in __init__ does not seem to propagate the visibility + # correctly + self._error_detail_input.setVisible( + not self._error_detail_expand_btn.collapsed + ) + def set_detail(self, detail): if not detail: self._set_visible_inputs(False) From 10684239e936ea88ade6c60d0e5c8e0adca6861f Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 2 Sep 2024 14:21:55 +0200 Subject: [PATCH 10/31] added line at the start of details too (shorter) --- client/ayon_core/tools/publisher/widgets/report_page.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/report_page.py b/client/ayon_core/tools/publisher/widgets/report_page.py index e9fbd13808..09b722ea54 100644 --- a/client/ayon_core/tools/publisher/widgets/report_page.py +++ b/client/ayon_core/tools/publisher/widgets/report_page.py @@ -1460,17 +1460,19 @@ def __init__(self, parent): error_detail_top = ClickableFrame(self) + line_l_widget = SeparatorWidget(1, parent=error_detail_top) error_detail_expand_btn = ClassicExpandBtn(error_detail_top) error_detail_expand_label = QtWidgets.QLabel( "Details", error_detail_top) - line_widget = SeparatorWidget(1, parent=error_detail_top) + line_r_widget = SeparatorWidget(1, parent=error_detail_top) error_detail_top_l = QtWidgets.QHBoxLayout(error_detail_top) error_detail_top_l.setContentsMargins(0, 0, 10, 0) + error_detail_top_l.addWidget(line_l_widget, 1) error_detail_top_l.addWidget(error_detail_expand_btn, 0) error_detail_top_l.addWidget(error_detail_expand_label, 0) - error_detail_top_l.addWidget(line_widget, 1) + error_detail_top_l.addWidget(line_r_widget, 9) error_detail_input = ExpandingTextEdit(self) error_detail_input.setObjectName("InfoText") From 71623cd0bdbc2bf5aaa378b455d042dc95d6d27a Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 2 Sep 2024 14:32:52 +0200 Subject: [PATCH 11/31] fill title if is not avaialble in exception --- client/ayon_core/tools/publisher/models/publish.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/publisher/models/publish.py b/client/ayon_core/tools/publisher/models/publish.py index b01cfb30f9..7fc4ca8bb4 100644 --- a/client/ayon_core/tools/publisher/models/publish.py +++ b/client/ayon_core/tools/publisher/models/publish.py @@ -51,13 +51,13 @@ def __ne__(self, other: Any) -> bool: @classmethod def from_exception(cls, exc) -> "PublishErrorInfo": + title = "This is not your fault" if isinstance(exc, PublishArtistError): return cls( exc.description or exc.message, - title=exc.title, + title=exc.title or title, detail=exc.detail, ) - title = "This is not your fault" if isinstance(exc, KnownPublishError): msg = str(exc) else: From 47e921ac43cd51c684efff8da9538b6d1ef977e7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 2 Sep 2024 15:31:15 +0200 Subject: [PATCH 12/31] renamed 'PublishArtistError' to 'PublishError' --- client/ayon_core/pipeline/__init__.py | 4 ++-- client/ayon_core/pipeline/publish/__init__.py | 4 ++-- client/ayon_core/pipeline/publish/publish_plugins.py | 4 ++-- client/ayon_core/tools/publisher/models/publish.py | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/pipeline/__init__.py b/client/ayon_core/pipeline/__init__.py index cb9d4ee46a..e17ef66557 100644 --- a/client/ayon_core/pipeline/__init__.py +++ b/client/ayon_core/pipeline/__init__.py @@ -52,7 +52,7 @@ from .publish import ( KnownPublishError, - PublishArtistError, + PublishError, PublishValidationError, PublishXmlValidationError, AYONPyblishPluginMixin, @@ -166,7 +166,7 @@ # --- Publish --- "KnownPublishError", - "PublishArtistError", + "PublishError", "PublishValidationError", "PublishXmlValidationError", "AYONPyblishPluginMixin", diff --git a/client/ayon_core/pipeline/publish/__init__.py b/client/ayon_core/pipeline/publish/__init__.py index 3f0bc0bb85..80671a43ac 100644 --- a/client/ayon_core/pipeline/publish/__init__.py +++ b/client/ayon_core/pipeline/publish/__init__.py @@ -10,7 +10,7 @@ AbstractMetaContextPlugin, KnownPublishError, - PublishArtistError, + PublishError, PublishValidationError, PublishXmlValidationError, @@ -65,7 +65,7 @@ "AbstractMetaContextPlugin", "KnownPublishError", - "PublishArtistError", + "PublishError", "PublishValidationError", "PublishXmlValidationError", diff --git a/client/ayon_core/pipeline/publish/publish_plugins.py b/client/ayon_core/pipeline/publish/publish_plugins.py index 70040d7395..4e7c5900c7 100644 --- a/client/ayon_core/pipeline/publish/publish_plugins.py +++ b/client/ayon_core/pipeline/publish/publish_plugins.py @@ -33,7 +33,7 @@ class KnownPublishError(Exception): pass -class PublishArtistError(Exception): +class PublishError(Exception): """Publishing crashed because of known error. Message will be shown in UI for artist. @@ -53,7 +53,7 @@ def __init__(self, message, title=None, description=None, detail=None): super().__init__(message) -class PublishValidationError(PublishArtistError): +class PublishValidationError(PublishError): """Validation error happened during publishing. This exception should be used when validation publishing failed. diff --git a/client/ayon_core/tools/publisher/models/publish.py b/client/ayon_core/tools/publisher/models/publish.py index 7fc4ca8bb4..e0ceea825a 100644 --- a/client/ayon_core/tools/publisher/models/publish.py +++ b/client/ayon_core/tools/publisher/models/publish.py @@ -17,7 +17,7 @@ from ayon_core.pipeline.plugin_discover import DiscoverResult from ayon_core.pipeline.publish import ( get_publish_instance_label, - PublishArtistError, + PublishError, ) from ayon_core.tools.publisher.abstract import AbstractPublisherBackend @@ -52,7 +52,7 @@ def __ne__(self, other: Any) -> bool: @classmethod def from_exception(cls, exc) -> "PublishErrorInfo": title = "This is not your fault" - if isinstance(exc, PublishArtistError): + if isinstance(exc, PublishError): return cls( exc.description or exc.message, title=exc.title or title, From 4875cbe67f6f35cf7d4b169eba94772371b6b79a Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 2 Sep 2024 16:20:41 +0200 Subject: [PATCH 13/31] added deprecation warning --- client/ayon_core/pipeline/publish/publish_plugins.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/ayon_core/pipeline/publish/publish_plugins.py b/client/ayon_core/pipeline/publish/publish_plugins.py index 4e7c5900c7..d8ed4ec10c 100644 --- a/client/ayon_core/pipeline/publish/publish_plugins.py +++ b/client/ayon_core/pipeline/publish/publish_plugins.py @@ -29,6 +29,10 @@ class KnownPublishError(Exception): """Publishing crashed because of known error. Artist can't affect source of the error. + + Deprecated: + Please use `PublishError` instead. Marked as deprecated 24/09/02. + """ pass From c70a44f1f4ad784cdfe689e7941d0b4e73c17a43 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 2 Sep 2024 17:11:31 +0200 Subject: [PATCH 14/31] updated readme --- client/ayon_core/pipeline/publish/README.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/pipeline/publish/README.md b/client/ayon_core/pipeline/publish/README.md index ee2124dfd3..316a0ee0f9 100644 --- a/client/ayon_core/pipeline/publish/README.md +++ b/client/ayon_core/pipeline/publish/README.md @@ -4,17 +4,19 @@ AYON is using `pyblish` for publishing process which is a little bit extented an ## Exceptions AYON define few specific exceptions that should be used in publish plugins. -### Validation exception -Validation plugins should raise `PublishValidationError` to show to an artist what's wrong and give him actions to fix it. The exception says that error happened in plugin can be fixed by artist himself (with or without action on plugin). Any other errors will stop publishing immediately. Exception `PublishValidationError` raised after validation order has same effect as any other exception. +### Publish error +Exception `PublishError` can be raised on known error. The message is shown to artist. +- **message** Error message. +- **title** Short description of error (2-5 words). Title can be used for grouping of exceptions per plugin. +- **description** Override of 'message' for UI, you can add markdown and html. By default, is filled with 'message'. +- **detail** Additional detail message that is hidden under collapsed component. -Exception `PublishValidationError` 3 arguments: -- **message** Which is not used in UI but for headless publishing. -- **title** Short description of error (2-5 words). Title is used for grouping of exceptions per plugin. -- **description** Detailed description of happened issue where markdown and html can be used. +Arguments `title`, `description` and `detail` are optional. Title is filled with generic message "This is not your fault" if is not passed. +### Validation exception +Validation plugins should raise `PublishValidationError` to show to an artist what's wrong and give him actions to fix it. The exception says that error happened in plugin can be fixed by artist himself (with or without action on plugin). Any other errors will stop publishing immediately. Exception `PublishValidationError` raised after validation order has same effect as any other exception. -### Known errors -When there is a known error that can't be fixed by user (e.g. can't connect to deadline service, etc.) `KnownPublishError` should be raise. The only difference is that it's message is shown in UI to artist otherwise a neutral message without context is shown. +Exception expect same arguments as `PublishError`. Value of `title` is filled with plugin label if is not passed. ## Plugin extension Publish plugins can be extended by additional logic when inherits from `AYONPyblishPluginMixin` which can be used as mixin (additional inheritance of class). From 17f76c761f93539f4d00b0fe9981755852fac5df Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 10 Sep 2024 14:01:47 +0200 Subject: [PATCH 15/31] use explicit margins --- client/ayon_core/tools/publisher/widgets/report_page.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/report_page.py b/client/ayon_core/tools/publisher/widgets/report_page.py index 09b722ea54..4eb71ccc99 100644 --- a/client/ayon_core/tools/publisher/widgets/report_page.py +++ b/client/ayon_core/tools/publisher/widgets/report_page.py @@ -1740,11 +1740,8 @@ def __init__( pages_layout.addWidget(crash_widget, 1) details_layout = QtWidgets.QVBoxLayout(details_widget) - margins = details_layout.contentsMargins() - margins.setTop(margins.top() * 2) - margins.setBottom(margins.bottom() * 2) - details_layout.setContentsMargins(margins) - details_layout.setSpacing(margins.top()) + details_layout.setContentsMargins(8, 16, 8, 16) + details_layout.setSpacing(8) details_layout.addWidget(actions_widget, 0) details_layout.addWidget(pages_widget, 1) From 56b8ca9d38dbce36bf8a3dd6e47332b203305062 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 10 Sep 2024 14:02:01 +0200 Subject: [PATCH 16/31] added is_unknown_error to error info --- client/ayon_core/tools/publisher/models/publish.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/publisher/models/publish.py b/client/ayon_core/tools/publisher/models/publish.py index e0ceea825a..b0a360fcda 100644 --- a/client/ayon_core/tools/publisher/models/publish.py +++ b/client/ayon_core/tools/publisher/models/publish.py @@ -30,10 +30,12 @@ class PublishErrorInfo: def __init__( self, description: str, + is_unknown_error: bool, title: Optional[str], - detail: Optional[str] + detail: Optional[str], ): self.description: str = description + self.is_unknown_error = is_unknown_error self.title: Optional[str] = title self.detail: Optional[str] = detail @@ -42,6 +44,7 @@ def __eq__(self, other: Any) -> bool: return False return ( self.description == other.description + and self.is_unknown_error == other.is_unknown_error and self.title == other.title and self.detail == other.detail ) @@ -55,6 +58,7 @@ def from_exception(cls, exc) -> "PublishErrorInfo": if isinstance(exc, PublishError): return cls( exc.description or exc.message, + is_unknown_error=False, title=exc.title or title, detail=exc.detail, ) @@ -65,7 +69,7 @@ def from_exception(cls, exc) -> "PublishErrorInfo": "Something went wrong. Send report" " to your supervisor or Ynput team." ) - return cls(msg, title, None) + return cls(msg, True, title, None) class PublishReportMaker: From 58fc67eed880d25a9a060d9e0a81b6480a3b08b9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 10 Sep 2024 14:02:19 +0200 Subject: [PATCH 17/31] make logs visible based on unknown error --- .../tools/publisher/widgets/report_page.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/report_page.py b/client/ayon_core/tools/publisher/widgets/report_page.py index 4eb71ccc99..d0af34035f 100644 --- a/client/ayon_core/tools/publisher/widgets/report_page.py +++ b/client/ayon_core/tools/publisher/widgets/report_page.py @@ -1588,8 +1588,7 @@ def __init__( self._detail_widget = detail_widget self._controller: AbstractPublisherFrontend = controller - def update_error_info(self): - error_info = self._controller.get_publish_error_info() + def update_error_info(self, error_info): if error_info is None: self._title_label.setText("Placeholder title") self._description_label.setText("A bug happened if you see this") @@ -1799,8 +1798,9 @@ def _get_instance_items(self): def update_data(self): view = self._instances_view validation_error_mode = False + is_crashed = self._controller.publish_has_crashed() if ( - not self._controller.publish_has_crashed() + not is_crashed and self._controller.publish_has_validation_errors() ): view = self._validation_error_view @@ -1811,11 +1811,15 @@ def update_data(self): self._detail_input_scroll.setVisible(validation_error_mode) self._views_layout.setCurrentWidget(view) - is_crashed = self._controller.publish_has_crashed() + error_info = self._controller.get_publish_error_info() + logs_visible = True + if is_crashed and error_info.is_unknown_error: + logs_visible = False + self._crash_widget.setVisible(is_crashed) - self._logs_view.setVisible(not is_crashed) + self._logs_view.setVisible(logs_visible) - self._crash_widget.update_error_info() + self._crash_widget.update_error_info(error_info) # Instance view & logs update instance_items = self._get_instance_items() From 079a7967b784b59500eefb39d542534edf75e762 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 10 Sep 2024 14:02:28 +0200 Subject: [PATCH 18/31] move crash widget to left --- client/ayon_core/tools/publisher/widgets/report_page.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/report_page.py b/client/ayon_core/tools/publisher/widgets/report_page.py index d0af34035f..ebe84457f9 100644 --- a/client/ayon_core/tools/publisher/widgets/report_page.py +++ b/client/ayon_core/tools/publisher/widgets/report_page.py @@ -1708,6 +1708,9 @@ def __init__( pages_widget = QtWidgets.QWidget(details_widget) + # Crash information + crash_widget = CrashWidget(controller, details_widget) + # Logs view logs_view = InstancesLogsView(pages_widget) @@ -1727,16 +1730,13 @@ def __init__( detail_input_scroll.setWidgetResizable(True) detail_input_scroll.setViewportMargins(0, 0, 0, 0) - # Crash information - crash_widget = CrashWidget(controller, details_widget) - # Layout pages pages_layout = QtWidgets.QHBoxLayout(pages_widget) pages_layout.setContentsMargins(0, 0, 0, 0) + pages_layout.addWidget(crash_widget, 1) pages_layout.addWidget(logs_view, 1) pages_layout.addWidget(detail_inputs_spacer, 0) pages_layout.addWidget(detail_input_scroll, 1) - pages_layout.addWidget(crash_widget, 1) details_layout = QtWidgets.QVBoxLayout(details_widget) details_layout.setContentsMargins(8, 16, 8, 16) From 0b352273959db9b1a6508d2f3821684664ad8b50 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 13 Sep 2024 17:03:35 +0200 Subject: [PATCH 19/31] fix images used for collapsed widget --- client/ayon_core/tools/utils/widgets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/utils/widgets.py b/client/ayon_core/tools/utils/widgets.py index 4c2b418c41..9092283221 100644 --- a/client/ayon_core/tools/utils/widgets.py +++ b/client/ayon_core/tools/utils/widgets.py @@ -466,10 +466,10 @@ def __init__(self, parent): self._collapsed = True def _create_collapsed_pixmap(self): - return QtGui.QPixmap(self.branch_closed_path) + return QtGui.QPixmap(self.branch_open_path) def _create_expanded_pixmap(self): - return QtGui.QPixmap(self.branch_open_path) + return QtGui.QPixmap(self.branch_closed_path) @property def collapsed(self): From a14df299c8f4fc1d8e8dde049eb62b5ae96f4804 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 13 Sep 2024 17:04:06 +0200 Subject: [PATCH 20/31] don't show detail until it should be visible --- .../tools/publisher/widgets/report_page.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/report_page.py b/client/ayon_core/tools/publisher/widgets/report_page.py index ebe84457f9..cc0f425c30 100644 --- a/client/ayon_core/tools/publisher/widgets/report_page.py +++ b/client/ayon_core/tools/publisher/widgets/report_page.py @@ -1486,19 +1486,14 @@ def __init__(self, parent): main_layout.addWidget(error_detail_input, 0) main_layout.addStretch(1) + error_detail_input.setVisible(not error_detail_expand_btn.collapsed) + error_detail_top.clicked.connect(self._on_detail_toggle) self._error_detail_top = error_detail_top self._error_detail_expand_btn = error_detail_expand_btn self._error_detail_input = error_detail_input - def showEvent(self, event): - super().showEvent(event) - # Calling this in __init__ does not seem to propagate the visibility - # correctly - self._error_detail_input.setVisible( - not self._error_detail_expand_btn.collapsed - ) def set_detail(self, detail): if not detail: @@ -1520,7 +1515,10 @@ def set_detail(self, detail): def _set_visible_inputs(self, visible): self._error_detail_top.setVisible(visible) - self._error_detail_input.setVisible(visible) + input_visible = visible + if input_visible: + input_visible = not self._error_detail_expand_btn.collapsed + self._error_detail_input.setVisible(input_visible) def _on_detail_toggle(self): self._error_detail_expand_btn.set_collapsed() From 9cbfecf92c643d3d2e5f40942ccb2d7ce944919d Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 13 Sep 2024 17:04:25 +0200 Subject: [PATCH 21/31] fill title in exception if is missing --- client/ayon_core/tools/publisher/models/publish.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/ayon_core/tools/publisher/models/publish.py b/client/ayon_core/tools/publisher/models/publish.py index b0a360fcda..58598602d5 100644 --- a/client/ayon_core/tools/publisher/models/publish.py +++ b/client/ayon_core/tools/publisher/models/publish.py @@ -1282,6 +1282,11 @@ def _process_and_continue( self._add_validation_error(result) else: + if isinstance(exception, PublishError): + if not exception.title: + exception.title = plugin.label or plugin.__name__ + self._add_validation_error(result) + error_info = PublishErrorInfo.from_exception(exception) self._set_publish_error_info(error_info) self._set_is_crashed(True) From d3d2f836773210e4ef0d0b5e43d048af2d604cec Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 13 Sep 2024 17:04:51 +0200 Subject: [PATCH 22/31] added message to error info and simplified initialization --- .../tools/publisher/models/publish.py | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/client/ayon_core/tools/publisher/models/publish.py b/client/ayon_core/tools/publisher/models/publish.py index 58598602d5..a3bfad091a 100644 --- a/client/ayon_core/tools/publisher/models/publish.py +++ b/client/ayon_core/tools/publisher/models/publish.py @@ -29,14 +29,16 @@ class PublishErrorInfo: def __init__( self, - description: str, + message: str, is_unknown_error: bool, - title: Optional[str], - detail: Optional[str], + description: Optional[str] = None, + title: Optional[str] = None, + detail: Optional[str] = None, ): - self.description: str = description + self.message: str = message self.is_unknown_error = is_unknown_error - self.title: Optional[str] = title + self.description: str = description or message + self.title: Optional[str] = title or "Unknown error" self.detail: Optional[str] = detail def __eq__(self, other: Any) -> bool: @@ -54,12 +56,12 @@ def __ne__(self, other: Any) -> bool: @classmethod def from_exception(cls, exc) -> "PublishErrorInfo": - title = "This is not your fault" if isinstance(exc, PublishError): return cls( - exc.description or exc.message, - is_unknown_error=False, - title=exc.title or title, + exc.message, + False, + exc.description, + title=exc.title, detail=exc.detail, ) if isinstance(exc, KnownPublishError): @@ -69,7 +71,7 @@ def from_exception(cls, exc) -> "PublishErrorInfo": "Something went wrong. Send report" " to your supervisor or Ynput team." ) - return cls(msg, True, title, None) + return cls(msg, True) class PublishReportMaker: From d62b31c981632e2de591e71d247fd13d328de7d8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 13 Sep 2024 17:05:18 +0200 Subject: [PATCH 23/31] fix error message shown in progress bar --- client/ayon_core/tools/publisher/widgets/publish_frame.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/publisher/widgets/publish_frame.py b/client/ayon_core/tools/publisher/widgets/publish_frame.py index d9a9e501ef..55051d2c40 100644 --- a/client/ayon_core/tools/publisher/widgets/publish_frame.py +++ b/client/ayon_core/tools/publisher/widgets/publish_frame.py @@ -412,7 +412,12 @@ def _set_error_msg(self): self._set_main_label("Error happened") error_info = self._controller.get_publish_error_info() - self._message_label_top.setText(error_info.description) + + error_message = "Unknown error happened" + if error_info is not None: + error_message = error_info.message + + self._message_label_top.setText(error_message) self._set_success_property(1) From b4e0e32e1bfdd108a2aa134ae6963b608549579c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 13 Sep 2024 17:05:39 +0200 Subject: [PATCH 24/31] show publish error as validation error --- .../tools/publisher/widgets/report_page.py | 75 +++++++++---------- 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/report_page.py b/client/ayon_core/tools/publisher/widgets/report_page.py index cc0f425c30..0580777669 100644 --- a/client/ayon_core/tools/publisher/widgets/report_page.py +++ b/client/ayon_core/tools/publisher/widgets/report_page.py @@ -1539,16 +1539,20 @@ def __init__( ): super().__init__(parent) - title_label = QtWidgets.QLabel("", self) - title_label.setAlignment(QtCore.Qt.AlignCenter) - title_label.setObjectName("PublishCrashMainLabel") - - description_label = QtWidgets.QLabel("", self) - description_label.setAlignment(QtCore.Qt.AlignCenter) - description_label.setWordWrap(True) - description_label.setObjectName("PublishCrashReportLabel") - - detail_widget = ErrorDetailWidget(self) + main_label = QtWidgets.QLabel("This is not your fault", self) + main_label.setAlignment(QtCore.Qt.AlignCenter) + main_label.setObjectName("PublishCrashMainLabel") + + report_label = QtWidgets.QLabel( + ( + "Please report the error to your pipeline support" + " using one of the options below." + ), + self + ) + report_label.setAlignment(QtCore.Qt.AlignCenter) + report_label.setWordWrap(True) + report_label.setObjectName("PublishCrashReportLabel") btns_widget = QtWidgets.QWidget(self) copy_clipboard_btn = QtWidgets.QPushButton( @@ -1569,34 +1573,18 @@ def __init__( layout.setContentsMargins(5, 5, 5, 5) layout.setSpacing(0) layout.addSpacing(20) - layout.addWidget(title_label, 0) + layout.addWidget(main_label, 0) layout.addSpacing(20) - layout.addWidget(description_label, 0) + layout.addWidget(report_label, 0) layout.addSpacing(20) - layout.addWidget(detail_widget, 1) - layout.addSpacing(10) layout.addWidget(btns_widget, 0) layout.addSpacing(10) copy_clipboard_btn.clicked.connect(self._on_copy_to_clipboard) save_to_disk_btn.clicked.connect(self._on_save_to_disk_click) - self._title_label = title_label - self._description_label = description_label - self._detail_widget = detail_widget self._controller: AbstractPublisherFrontend = controller - def update_error_info(self, error_info): - if error_info is None: - self._title_label.setText("Placeholder title") - self._description_label.setText("A bug happened if you see this") - self._detail_widget.set_detail(None) - return - - self._title_label.setText(error_info.title) - self._description_label.setText(error_info.description) - self._detail_widget.set_detail(error_info.detail) - def _on_copy_to_clipboard(self): self._controller.emit_event( "copy_report.request", {}, "report_page") @@ -1618,6 +1606,7 @@ def __init__(self, parent): QtCore.Qt.TextBrowserInteraction ) + # Error 'Details' widget -> Collapsible error_details_widget = ErrorDetailWidget(inputs_widget) # Description and Details layout @@ -1794,30 +1783,36 @@ def _get_instance_items(self): return instance_items def update_data(self): - view = self._instances_view - validation_error_mode = False is_crashed = self._controller.publish_has_crashed() + error_info = None + if is_crashed: + error_info = self._controller.get_publish_error_info() + + validation_error_mode = False if ( + error_info is not None + and not error_info.is_unknown_error + ): + validation_error_mode = True + + elif ( not is_crashed and self._controller.publish_has_validation_errors() ): - view = self._validation_error_view validation_error_mode = True + if validation_error_mode: + view = self._validation_error_view + else: + view = self._instances_view + self._actions_widget.set_visible_mode(validation_error_mode) self._detail_inputs_spacer.setVisible(validation_error_mode) self._detail_input_scroll.setVisible(validation_error_mode) self._views_layout.setCurrentWidget(view) - error_info = self._controller.get_publish_error_info() - logs_visible = True - if is_crashed and error_info.is_unknown_error: - logs_visible = False - - self._crash_widget.setVisible(is_crashed) - self._logs_view.setVisible(logs_visible) - - self._crash_widget.update_error_info(error_info) + self._crash_widget.setVisible(not validation_error_mode) + self._logs_view.setVisible(validation_error_mode) # Instance view & logs update instance_items = self._get_instance_items() From 5be47d4f5b7a324d72c38447d8f8bc12e12aeb63 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 13 Sep 2024 17:05:50 +0200 Subject: [PATCH 25/31] better typehint --- client/ayon_core/tools/publisher/abstract.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/publisher/abstract.py b/client/ayon_core/tools/publisher/abstract.py index f5c8306631..04a1985cbb 100644 --- a/client/ayon_core/tools/publisher/abstract.py +++ b/client/ayon_core/tools/publisher/abstract.py @@ -543,11 +543,11 @@ def get_publish_max_progress(self) -> int: pass @abstractmethod - def get_publish_error_info(self) -> Union["PublishErrorInfo", None]: + def get_publish_error_info(self) -> Optional["PublishErrorInfo"]: """Current error message which cause fail of publishing. Returns: - Union[PublishErrorInfo, None]: Error info or None. + Optional[PublishErrorInfo]: Error info or None. """ pass From 7ca0a354cac0f8d5011ae280c97fb411a6215684 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 13 Sep 2024 17:27:44 +0200 Subject: [PATCH 26/31] remove double line --- client/ayon_core/tools/publisher/widgets/report_page.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/tools/publisher/widgets/report_page.py b/client/ayon_core/tools/publisher/widgets/report_page.py index 0580777669..64281a7046 100644 --- a/client/ayon_core/tools/publisher/widgets/report_page.py +++ b/client/ayon_core/tools/publisher/widgets/report_page.py @@ -1494,7 +1494,6 @@ def __init__(self, parent): self._error_detail_expand_btn = error_detail_expand_btn self._error_detail_input = error_detail_input - def set_detail(self, detail): if not detail: self._set_visible_inputs(False) From 882d61f2f34460ee13ccd621ee03103049645cd5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 13 Sep 2024 18:53:39 +0200 Subject: [PATCH 27/31] refactored validation error handling to publish error handling --- client/ayon_core/style/style.css | 16 +- client/ayon_core/tools/publisher/abstract.py | 2 +- client/ayon_core/tools/publisher/control.py | 4 +- .../tools/publisher/models/publish.py | 82 ++++---- .../tools/publisher/widgets/report_page.py | 195 ++++++++++-------- 5 files changed, 159 insertions(+), 140 deletions(-) diff --git a/client/ayon_core/style/style.css b/client/ayon_core/style/style.css index 8578522c79..aaaf358f97 100644 --- a/client/ayon_core/style/style.css +++ b/client/ayon_core/style/style.css @@ -1118,39 +1118,39 @@ ValidationArtistMessage QLabel { font-weight: bold; } -#ValidationActionButton { +#PublishActionButton { border-radius: 0.2em; padding: 4px 6px 4px 6px; background: {color:bg-buttons}; } -#ValidationActionButton:hover { +#PublishActionButton:hover { background: {color:bg-buttons-hover}; color: {color:font-hover}; } -#ValidationActionButton:disabled { +#PublishActionButton:disabled { background: {color:bg-buttons-disabled}; } -#ValidationErrorTitleFrame { +#PublishErrorTitleFrame { border-radius: 0.2em; background: {color:bg-buttons}; } -#ValidationErrorTitleFrame:hover { +#PublishErrorTitleFrame:hover { background: {color:bg-buttons-hover}; } -#ValidationErrorTitleFrame[selected="1"] { +#PublishErrorTitleFrame[selected="1"] { background: {color:bg-view-selection}; } -#ValidationErrorInstanceList { +#PublishErrorInstanceList { border-radius: 0; } -#ValidationErrorInstanceList::item { +#PublishErrorInstanceList::item { border-bottom: 1px solid {color:border}; border-left: 1px solid {color:border}; } diff --git a/client/ayon_core/tools/publisher/abstract.py b/client/ayon_core/tools/publisher/abstract.py index 04a1985cbb..6bea4cc247 100644 --- a/client/ayon_core/tools/publisher/abstract.py +++ b/client/ayon_core/tools/publisher/abstract.py @@ -557,7 +557,7 @@ def get_publish_report(self) -> Dict[str, Any]: pass @abstractmethod - def get_validation_errors(self): + def get_publish_errors_report(self): pass @abstractmethod diff --git a/client/ayon_core/tools/publisher/control.py b/client/ayon_core/tools/publisher/control.py index bc7ed994bd..c7fd75b3c3 100644 --- a/client/ayon_core/tools/publisher/control.py +++ b/client/ayon_core/tools/publisher/control.py @@ -502,8 +502,8 @@ def get_publish_error_info(self): def get_publish_report(self): return self._publish_model.get_publish_report() - def get_validation_errors(self): - return self._publish_model.get_validation_errors() + def get_publish_errors_report(self): + return self._publish_model.get_publish_errors_report() def set_comment(self, comment): """Set comment from ui to pyblish context. diff --git a/client/ayon_core/tools/publisher/models/publish.py b/client/ayon_core/tools/publisher/models/publish.py index a3bfad091a..fbfc2dcdf1 100644 --- a/client/ayon_core/tools/publisher/models/publish.py +++ b/client/ayon_core/tools/publisher/models/publish.py @@ -529,10 +529,10 @@ def _create_action_item( ) -class ValidationErrorItem: - """Data driven validation error item. +class PublishErrorItem: + """Data driven publish error item. - Prepared data container with information about validation error and it's + Prepared data container with information about publish error and it's source plugin. Can be converted to raw data and recreated should be used for controller @@ -540,11 +540,11 @@ class ValidationErrorItem: Args: instance_id (Optional[str]): Pyblish instance id to which is - validation error connected. + publish error connected. instance_label (Optional[str]): Prepared instance label. - plugin_id (str): Pyblish plugin id which triggered the validation + plugin_id (str): Pyblish plugin id which triggered the publish error. Id is generated using 'PublishPluginsProxy'. - context_validation (bool): Error happened on context. + is_context_plugin (bool): Error happened on context. title (str): Error title. description (str): Error description. detail (str): Error detail. @@ -555,7 +555,8 @@ def __init__( instance_id: Optional[str], instance_label: Optional[str], plugin_id: str, - context_validation: bool, + is_context_plugin: bool, + is_validation_error: bool, title: str, description: str, detail: str @@ -563,7 +564,8 @@ def __init__( self.instance_id: Optional[str] = instance_id self.instance_label: Optional[str] = instance_label self.plugin_id: str = plugin_id - self.context_validation: bool = context_validation + self.is_context_plugin: bool = is_context_plugin + self.is_validation_error: bool = is_validation_error self.title: str = title self.description: str = description self.detail: str = detail @@ -579,7 +581,8 @@ def to_data(self) -> Dict[str, Any]: "instance_id": self.instance_id, "instance_label": self.instance_label, "plugin_id": self.plugin_id, - "context_validation": self.context_validation, + "is_context_plugin": self.is_context_plugin, + "is_validation_error": self.is_validation_error, "title": self.title, "description": self.description, "detail": self.detail, @@ -589,13 +592,13 @@ def to_data(self) -> Dict[str, Any]: def from_result( cls, plugin_id: str, - error: PublishValidationError, + error: PublishError, instance: Union[pyblish.api.Instance, None] ): """Create new object based on resukt from controller. Returns: - ValidationErrorItem: New object with filled data. + PublishErrorItem: New object with filled data. """ instance_label = None @@ -611,6 +614,7 @@ def from_result( instance_label, plugin_id, instance is None, + isinstance(error, PublishValidationError), error.title, error.description, error.detail, @@ -621,11 +625,11 @@ def from_data(cls, data): return cls(**data) -class PublishValidationErrorsReport: - """Publish validation errors report that can be parsed to raw data. +class PublishErrorsReport: + """Publish errors report that can be parsed to raw data. Args: - error_items (List[ValidationErrorItem]): List of validation errors. + error_items (List[PublishErrorItem]): List of publish errors. plugin_action_items (Dict[str, List[PublishPluginActionItem]]): Action items by plugin id. @@ -634,7 +638,7 @@ def __init__(self, error_items, plugin_action_items): self._error_items = error_items self._plugin_action_items = plugin_action_items - def __iter__(self) -> Iterable[ValidationErrorItem]: + def __iter__(self) -> Iterable[PublishErrorItem]: for item in self._error_items: yield item @@ -708,7 +712,7 @@ def to_data(self): @classmethod def from_data( cls, data: Dict[str, Any] - ) -> "PublishValidationErrorsReport": + ) -> "PublishErrorsReport": """Recreate object from data. Args: @@ -716,11 +720,11 @@ def from_data( using 'to_data' method. Returns: - PublishValidationErrorsReport: New object based on data. + PublishErrorsReport: New object based on data. """ error_items = [ - ValidationErrorItem.from_data(error_item) + PublishErrorItem.from_data(error_item) for error_item in data["error_items"] ] plugin_action_items = {} @@ -732,12 +736,12 @@ def from_data( return cls(error_items, plugin_action_items) -class PublishValidationErrors: - """Object to keep track about validation errors by plugin.""" +class PublishErrors: + """Object to keep track about publish errors by plugin.""" def __init__(self): self._plugins_proxy: Union[PublishPluginsProxy, None] = None - self._error_items: List[ValidationErrorItem] = [] + self._error_items: List[PublishErrorItem] = [] self._plugin_action_items: Dict[ str, List[PublishPluginActionItem] ] = {} @@ -763,29 +767,29 @@ def reset(self, plugins_proxy: PublishPluginsProxy): self._error_items = [] self._plugin_action_items = {} - def create_report(self) -> PublishValidationErrorsReport: + def create_report(self) -> PublishErrorsReport: """Create report based on currently existing errors. Returns: - PublishValidationErrorsReport: Validation error report with all + PublishErrorsReport: Publish error report with all error information and publish plugin action items. """ - return PublishValidationErrorsReport( + return PublishErrorsReport( self._error_items, self._plugin_action_items ) def add_error( self, plugin: pyblish.api.Plugin, - error: PublishValidationError, + error: PublishError, instance: Union[pyblish.api.Instance, None] ): """Add error from pyblish result. Args: plugin (pyblish.api.Plugin): Plugin which triggered error. - error (PublishValidationError): Validation error. + error (PublishError): Publish error. instance (Union[pyblish.api.Instance, None]): Instance on which was error raised or None if was raised on context. """ @@ -800,7 +804,7 @@ def add_error( error.title = plugin_label self._error_items.append( - ValidationErrorItem.from_result(plugin_id, error, instance) + PublishErrorItem.from_result(plugin_id, error, instance) ) if plugin_id in self._plugin_action_items: return @@ -874,10 +878,8 @@ def __init__(self, controller: AbstractPublisherBackend): self._publish_context = None # Pyblish report self._publish_report: PublishReportMaker = PublishReportMaker() - # Store exceptions of validation error - self._publish_validation_errors: PublishValidationErrors = ( - PublishValidationErrors() - ) + # Store exceptions of publish error + self._publish_errors: PublishErrors = PublishErrors() # This information is not much important for controller but for widget # which can change (and set) the comment. @@ -931,7 +933,7 @@ def reset(self): ) for plugin in create_context.publish_plugins_mismatch_targets: self._publish_report.set_plugin_skipped(plugin.id) - self._publish_validation_errors.reset(self._publish_plugins_proxy) + self._publish_errors.reset(self._publish_plugins_proxy) self._set_max_progress(len(publish_plugins)) @@ -1024,8 +1026,8 @@ def get_publish_report(self) -> Dict[str, Any]: self._publish_context ) - def get_validation_errors(self) -> PublishValidationErrorsReport: - return self._publish_validation_errors.create_report() + def get_publish_errors_report(self) -> PublishErrorsReport: + return self._publish_errors.create_report() def get_error_info(self) -> Optional[PublishErrorInfo]: return self._publish_error_info @@ -1275,31 +1277,33 @@ def _process_and_continue( exception = result.get("error") if exception: - has_validation_error = False if ( isinstance(exception, PublishValidationError) and not self._publish_has_validated ): - has_validation_error = True + result["is_validation_error"] = True self._add_validation_error(result) else: if isinstance(exception, PublishError): if not exception.title: exception.title = plugin.label or plugin.__name__ - self._add_validation_error(result) + self._add_publish_error_to_report(result) error_info = PublishErrorInfo.from_exception(exception) self._set_publish_error_info(error_info) self._set_is_crashed(True) - result["is_validation_error"] = has_validation_error + result["is_validation_error"] = False self._publish_report.add_result(plugin.id, result) def _add_validation_error(self, result: Dict[str, Any]): self._set_has_validation_errors(True) - self._publish_validation_errors.add_error( + self._add_publish_error_to_report(result) + + def _add_publish_error_to_report(self, result: Dict[str, Any]): + self._publish_errors.add_error( result["plugin"], result["error"], result["instance"] diff --git a/client/ayon_core/tools/publisher/widgets/report_page.py b/client/ayon_core/tools/publisher/widgets/report_page.py index 64281a7046..d491c300d3 100644 --- a/client/ayon_core/tools/publisher/widgets/report_page.py +++ b/client/ayon_core/tools/publisher/widgets/report_page.py @@ -26,7 +26,7 @@ CONTEXT_LABEL, ) -from .widgets import IconValuePixmapLabel +from .widgets import PublishPixmapLabel, IconValuePixmapLabel from .icons import ( get_pixmap, get_image, @@ -42,7 +42,7 @@ class VerticalScrollArea(QtWidgets.QScrollArea): - """Scroll area for validation error titles. + """Scroll area for publish error titles. The biggest difference is that the scroll area has scroll bar on left side and resize of content will also resize scrollarea itself. @@ -126,7 +126,7 @@ class ActionButton(BaseClickableFrame): def __init__(self, plugin_action_item, parent): super().__init__(parent) - self.setObjectName("ValidationActionButton") + self.setObjectName("PublishActionButton") self.plugin_action_item = plugin_action_item @@ -155,10 +155,10 @@ def _mouse_release_callback(self): ) -class ValidateActionsWidget(QtWidgets.QFrame): +class PublishActionsWidget(QtWidgets.QFrame): """Wrapper widget for plugin actions. - Change actions based on selected validation error. + Change actions based on selected publish error. """ def __init__( @@ -243,16 +243,16 @@ def _on_action_click(self, plugin_id, action_id): self._controller.run_action(plugin_id, action_id) -# --- Validation error titles --- -class ValidationErrorInstanceList(QtWidgets.QListView): - """List of publish instances that caused a validation error. +# --- Publish error titles --- +class PublishErrorInstanceList(QtWidgets.QListView): + """List of publish instances that caused a publish error. - Instances are collected per plugin's validation error title. + Instances are collected per plugin's publish error title. """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.setObjectName("ValidationErrorInstanceList") + self.setObjectName("PublishErrorInstanceList") self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) @@ -270,18 +270,19 @@ def sizeHint(self): return result -class ValidationErrorTitleWidget(QtWidgets.QWidget): - """Title of validation error. +class PublishErrorTitleWidget(QtWidgets.QWidget): + """Title of publish error. Widget is used as radio button so requires clickable functionality and changing style on selection/deselection. - Has toggle button to show/hide instances on which validation error happened + Has toggle button to show/hide instances on which publish error happened if there is a list (Valdation error may happen on context). """ selected = QtCore.Signal(str) instance_changed = QtCore.Signal(str) + _error_pixmap = None def __init__(self, title_id, error_info, parent): super().__init__(parent) @@ -290,30 +291,17 @@ def __init__(self, title_id, error_info, parent): self._error_info = error_info self._selected = False - title_frame = ClickableFrame(self) - title_frame.setObjectName("ValidationErrorTitleFrame") - - toggle_instance_btn = QtWidgets.QToolButton(title_frame) - toggle_instance_btn.setObjectName("ArrowBtn") - toggle_instance_btn.setArrowType(QtCore.Qt.RightArrow) - toggle_instance_btn.setMaximumWidth(14) - - label_widget = QtWidgets.QLabel(error_info["title"], title_frame) - - title_frame_layout = QtWidgets.QHBoxLayout(title_frame) - title_frame_layout.addWidget(label_widget, 1) - title_frame_layout.addWidget(toggle_instance_btn, 0) - instances_model = QtGui.QStandardItemModel() instance_ids = [] items = [] - context_validation = False + is_context_plugin = False + is_crashing_error = False for error_item in error_info["error_items"]: - context_validation = error_item.context_validation - if context_validation: - toggle_instance_btn.setArrowType(QtCore.Qt.NoArrow) + is_crashing_error = not error_item.is_validation_error + is_context_plugin = error_item.is_context_plugin + if is_context_plugin: instance_ids.append(CONTEXT_ID) # Add fake item to have minimum size hint of view widget items.append(QtGui.QStandardItem(CONTEXT_LABEL)) @@ -333,7 +321,33 @@ def __init__(self, title_id, error_info, parent): root_item = instances_model.invisibleRootItem() root_item.appendRows(items) - instances_view = ValidationErrorInstanceList(self) + title_frame = ClickableFrame(self) + title_frame.setObjectName("PublishErrorTitleFrame") + + toggle_instance_btn = QtWidgets.QToolButton(title_frame) + toggle_instance_btn.setObjectName("ArrowBtn") + toggle_instance_btn.setArrowType(QtCore.Qt.RightArrow) + toggle_instance_btn.setMaximumWidth(14) + if is_context_plugin: + toggle_instance_btn.setArrowType(QtCore.Qt.NoArrow) + + icon_label = None + if is_crashing_error: + error_pixmap = self._get_error_pixmap() + icon_label = PublishPixmapLabel(error_pixmap, self) + + label_widget = QtWidgets.QLabel(error_info["title"], title_frame) + + title_frame_layout = QtWidgets.QHBoxLayout(title_frame) + title_frame_layout.setContentsMargins(8, 8, 8, 8) + title_frame_layout.setSpacing(0) + if icon_label is not None: + title_frame_layout.addWidget(icon_label, 0) + title_frame_layout.addSpacing(6) + title_frame_layout.addWidget(label_widget, 1) + title_frame_layout.addWidget(toggle_instance_btn, 0) + + instances_view = PublishErrorInstanceList(self) instances_view.setModel(instances_model) self.setLayoutDirection(QtCore.Qt.LeftToRight) @@ -352,7 +366,7 @@ def __init__(self, title_id, error_info, parent): layout.addWidget(view_widget, 0) view_widget.setVisible(False) - if not context_validation: + if not is_context_plugin: toggle_instance_btn.clicked.connect(self._on_toggle_btn_click) title_frame.clicked.connect(self._mouse_release_callback) @@ -369,7 +383,8 @@ def __init__(self, title_id, error_info, parent): self._instances_model = instances_model self._instances_view = instances_view - self._context_validation = context_validation + self._is_context_plugin = is_context_plugin + self._is_crashing_error = is_crashing_error self._instance_ids = instance_ids self._expanded = False @@ -411,6 +426,10 @@ def is_selected(self): def id(self): return self._title_id + @property + def is_crashing_error(self): + return self._is_crashing_error + def _change_style_property(self, selected): """Change style of widget based on selection.""" @@ -438,6 +457,12 @@ def set_selected(self, selected=None): self.selected.emit(self._title_id) self._set_expanded(True) + @classmethod + def _get_error_pixmap(cls): + if cls._error_pixmap is None: + cls._error_pixmap = get_pixmap("error") + return cls._error_pixmap + def _on_toggle_btn_click(self): """Show/hide instances list.""" @@ -450,7 +475,7 @@ def _set_expanded(self, expanded=None): elif expanded is self._expanded: return - if expanded and self._context_validation: + if expanded and self._is_context_plugin: return self._expanded = expanded @@ -464,7 +489,7 @@ def _on_selection_change(self): self.instance_changed.emit(self._title_id) def get_selected_instances(self): - if self._context_validation: + if self._is_context_plugin: return [CONTEXT_ID] sel_model = self._instances_view.selectionModel() return [ @@ -477,21 +502,7 @@ def get_available_instances(self): return list(self._instance_ids) -class ValidationArtistMessage(QtWidgets.QWidget): - def __init__(self, message, parent): - super().__init__(parent) - - artist_msg_label = QtWidgets.QLabel(message, self) - artist_msg_label.setAlignment(QtCore.Qt.AlignCenter) - - main_layout = QtWidgets.QHBoxLayout(self) - main_layout.setContentsMargins(0, 0, 0, 0) - main_layout.addWidget( - artist_msg_label, 1, QtCore.Qt.AlignCenter - ) - - -class ValidationErrorsView(QtWidgets.QWidget): +class PublishErrorsView(QtWidgets.QWidget): selection_changed = QtCore.Signal() def __init__(self, parent): @@ -510,8 +521,9 @@ def __init__(self, parent): # scroll widget errors_layout.setContentsMargins(5, 0, 0, 0) - layout = QtWidgets.QVBoxLayout(self) - layout.addWidget(errors_scroll, 1) + main_layout = QtWidgets.QVBoxLayout(self) + main_layout.setContentsMargins(8, 8, 8, 8) + main_layout.addWidget(errors_scroll, 1) self._errors_widget = errors_widget self._errors_layout = errors_layout @@ -533,28 +545,30 @@ def set_errors(self, grouped_error_items): """Set errors into context and created titles. Args: - validation_error_report (PublishValidationErrorsReport): Report - with information about validation errors and publish plugin + grouped_error_items (List[Dict[str, Any]]): Report + with information about publish errors and publish plugin actions. """ self._clear() - first_id = None + select_id = None for title_item in grouped_error_items: title_id = title_item["id"] - if first_id is None: - first_id = title_id - widget = ValidationErrorTitleWidget(title_id, title_item, self) + if select_id is None: + select_id = title_id + widget = PublishErrorTitleWidget(title_id, title_item, self) widget.selected.connect(self._on_select) widget.instance_changed.connect(self._on_instance_change) + if widget.is_crashing_error: + select_id = title_id self._errors_layout.addWidget(widget) self._title_widgets[title_id] = widget self._errors_layout.addStretch(1) - if first_id: - self._title_widgets[first_id].set_selected(True) + if select_id: + self._title_widgets[select_id].set_selected(True) else: self.selection_changed.emit() @@ -1319,6 +1333,7 @@ def __init__(self, parent): content_widget = QtWidgets.QWidget(content_wrap_widget) content_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) content_layout = QtWidgets.QVBoxLayout(content_widget) + content_layout.setContentsMargins(8, 8, 8, 8) content_layout.setSpacing(15) scroll_area.setWidget(content_wrap_widget) @@ -1657,7 +1672,7 @@ class ReportsWidget(QtWidgets.QWidget): │ │ │ │ │ │ └──────┴───────────────────┘ - # Validation errors layout + # Publish errors layout ┌──────┬─────────┬─────────┐ │Views │ Actions │ │ │ ├─────────┤ Details │ @@ -1676,12 +1691,12 @@ def __init__( instances_view = PublishInstancesViewWidget(controller, views_widget) - validation_error_view = ValidationErrorsView(views_widget) + publish_error_view = PublishErrorsView(views_widget) views_layout = QtWidgets.QStackedLayout(views_widget) views_layout.setContentsMargins(0, 0, 0, 0) views_layout.addWidget(instances_view) - views_layout.addWidget(validation_error_view) + views_layout.addWidget(publish_error_view) views_layout.setCurrentWidget(instances_view) @@ -1690,7 +1705,7 @@ def __init__( details_widget.setObjectName("PublishInstancesDetails") # Actions widget - actions_widget = ValidateActionsWidget(controller, details_widget) + actions_widget = PublishActionsWidget(controller, details_widget) pages_widget = QtWidgets.QWidget(details_widget) @@ -1736,12 +1751,12 @@ def __init__( content_layout.addWidget(details_widget, 1) instances_view.selection_changed.connect(self._on_instance_selection) - validation_error_view.selection_changed.connect( + publish_error_view.selection_changed.connect( self._on_error_selection) self._views_layout = views_layout self._instances_view = instances_view - self._validation_error_view = validation_error_view + self._publish_error_view = publish_error_view self._actions_widget = actions_widget self._detail_inputs_widget = detail_inputs_widget @@ -1752,7 +1767,7 @@ def __init__( self._controller: AbstractPublisherFrontend = controller - self._validation_errors_by_id = {} + self._publish_errors_by_id = {} def _get_instance_items(self): report = self._controller.get_publish_report() @@ -1787,48 +1802,48 @@ def update_data(self): if is_crashed: error_info = self._controller.get_publish_error_info() - validation_error_mode = False + publish_error_mode = False if ( error_info is not None and not error_info.is_unknown_error ): - validation_error_mode = True + publish_error_mode = True elif ( not is_crashed and self._controller.publish_has_validation_errors() ): - validation_error_mode = True + publish_error_mode = True - if validation_error_mode: - view = self._validation_error_view + if publish_error_mode: + view = self._publish_error_view else: view = self._instances_view - self._actions_widget.set_visible_mode(validation_error_mode) - self._detail_inputs_spacer.setVisible(validation_error_mode) - self._detail_input_scroll.setVisible(validation_error_mode) + self._actions_widget.set_visible_mode(publish_error_mode) + self._detail_inputs_spacer.setVisible(publish_error_mode) + self._detail_input_scroll.setVisible(publish_error_mode) self._views_layout.setCurrentWidget(view) - self._crash_widget.setVisible(not validation_error_mode) - self._logs_view.setVisible(validation_error_mode) + self._crash_widget.setVisible(not publish_error_mode) + self._logs_view.setVisible(publish_error_mode) # Instance view & logs update instance_items = self._get_instance_items() self._instances_view.update_instances(instance_items) self._logs_view.update_instances(instance_items) - # Validation errors - validation_errors = self._controller.get_validation_errors() - grouped_error_items = validation_errors.group_items_by_title() + # Publish errors + publish_errors_report = self._controller.get_publish_errors_report() + grouped_error_items = publish_errors_report.group_items_by_title() - validation_errors_by_id = { + publish_errors_by_id = { title_item["id"]: title_item for title_item in grouped_error_items } - self._validation_errors_by_id = validation_errors_by_id - self._validation_error_view.set_errors(grouped_error_items) + self._publish_errors_by_id = publish_errors_by_id + self._publish_error_view.set_errors(grouped_error_items) def _on_instance_selection(self): instance_ids = self._instances_view.get_selected_instance_ids() @@ -1836,8 +1851,8 @@ def _on_instance_selection(self): def _on_error_selection(self): title_id, instance_ids = ( - self._validation_error_view.get_selected_items()) - error_info = self._validation_errors_by_id.get(title_id) + self._publish_error_view.get_selected_items()) + error_info = self._publish_errors_by_id.get(title_id) if error_info is None: self._actions_widget.set_error_info(None) self._detail_inputs_widget.set_error_item(None) @@ -1865,12 +1880,12 @@ class ReportPageWidget(QtWidgets.QFrame): 2. Publishing is paused. ┐ 3. Publishing successfully finished. │> Instances with logs. 4. Publishing crashed. ┘ - 5. Crashed because of validation error. > Errors with logs. + 5. Crashed because of publish error. > Errors with logs. - This widget is shown if validation errors happened during validation part. + This widget is shown if publish errors happened. - Shows validation error titles with instances on which they happened - and validation error detail with possible actions (repair). + Shows publish error titles with instances on which they happened + and publish error detail with possible actions (repair). """ def __init__( From 2fd99f232aee15fdd8d683df552a66664deeaa3d Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 13 Sep 2024 18:56:37 +0200 Subject: [PATCH 28/31] fix expand button --- client/ayon_core/tools/utils/widgets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/utils/widgets.py b/client/ayon_core/tools/utils/widgets.py index 9092283221..4c2b418c41 100644 --- a/client/ayon_core/tools/utils/widgets.py +++ b/client/ayon_core/tools/utils/widgets.py @@ -466,10 +466,10 @@ def __init__(self, parent): self._collapsed = True def _create_collapsed_pixmap(self): - return QtGui.QPixmap(self.branch_open_path) + return QtGui.QPixmap(self.branch_closed_path) def _create_expanded_pixmap(self): - return QtGui.QPixmap(self.branch_closed_path) + return QtGui.QPixmap(self.branch_open_path) @property def collapsed(self): From ac8848dd1975e93847c0d0afc19eb51b403ca69b Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 19 Sep 2024 17:28:59 +0200 Subject: [PATCH 29/31] fix crash widget layour --- client/ayon_core/tools/publisher/widgets/report_page.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/report_page.py b/client/ayon_core/tools/publisher/widgets/report_page.py index d491c300d3..5e48cef0d2 100644 --- a/client/ayon_core/tools/publisher/widgets/report_page.py +++ b/client/ayon_core/tools/publisher/widgets/report_page.py @@ -1586,13 +1586,13 @@ def __init__( layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(5, 5, 5, 5) layout.setSpacing(0) - layout.addSpacing(20) + layout.addStretch(1) layout.addWidget(main_label, 0) - layout.addSpacing(20) + layout.addSpacing(30) layout.addWidget(report_label, 0) - layout.addSpacing(20) + layout.addSpacing(30) layout.addWidget(btns_widget, 0) - layout.addSpacing(10) + layout.addStretch(2) copy_clipboard_btn.clicked.connect(self._on_copy_to_clipboard) save_to_disk_btn.clicked.connect(self._on_save_to_disk_click) From f5182f277cb4e4472ad76f8fa14a3f8460fe4294 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 19 Sep 2024 17:33:31 +0200 Subject: [PATCH 30/31] fix success publish page --- .../ayon_core/tools/publisher/widgets/report_page.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/report_page.py b/client/ayon_core/tools/publisher/widgets/report_page.py index 5e48cef0d2..7ee7ad76f3 100644 --- a/client/ayon_core/tools/publisher/widgets/report_page.py +++ b/client/ayon_core/tools/publisher/widgets/report_page.py @@ -1802,8 +1802,11 @@ def update_data(self): if is_crashed: error_info = self._controller.get_publish_error_info() + publish_finished = self._controller.publish_has_finished() publish_error_mode = False - if ( + if publish_finished: + publish_error_mode = False + elif ( error_info is not None and not error_info.is_unknown_error ): @@ -1819,14 +1822,15 @@ def update_data(self): view = self._publish_error_view else: view = self._instances_view + self._views_layout.setCurrentWidget(view) self._actions_widget.set_visible_mode(publish_error_mode) self._detail_inputs_spacer.setVisible(publish_error_mode) self._detail_input_scroll.setVisible(publish_error_mode) - self._views_layout.setCurrentWidget(view) - self._crash_widget.setVisible(not publish_error_mode) - self._logs_view.setVisible(publish_error_mode) + logs_visible = publish_error_mode or publish_finished + self._logs_view.setVisible(logs_visible) + self._crash_widget.setVisible(not logs_visible) # Instance view & logs update instance_items = self._get_instance_items() From b2b933bc029026034e374079a427514a5cb6a07e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 19 Sep 2024 18:03:24 +0200 Subject: [PATCH 31/31] fix and simplify the visibility --- .../tools/publisher/widgets/report_page.py | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/report_page.py b/client/ayon_core/tools/publisher/widgets/report_page.py index 7ee7ad76f3..b7afcf470a 100644 --- a/client/ayon_core/tools/publisher/widgets/report_page.py +++ b/client/ayon_core/tools/publisher/widgets/report_page.py @@ -1797,38 +1797,31 @@ def _get_instance_items(self): return instance_items def update_data(self): - is_crashed = self._controller.publish_has_crashed() + has_validation_error = self._controller.publish_has_validation_errors() + has_finished = self._controller.publish_has_finished() + has_crashed = self._controller.publish_has_crashed() error_info = None - if is_crashed: + if has_crashed: error_info = self._controller.get_publish_error_info() - publish_finished = self._controller.publish_has_finished() publish_error_mode = False - if publish_finished: - publish_error_mode = False - elif ( - error_info is not None - and not error_info.is_unknown_error - ): - publish_error_mode = True - - elif ( - not is_crashed - and self._controller.publish_has_validation_errors() - ): + if error_info is not None: + publish_error_mode = not error_info.is_unknown_error + elif has_validation_error: publish_error_mode = True if publish_error_mode: view = self._publish_error_view else: view = self._instances_view + self._views_layout.setCurrentWidget(view) self._actions_widget.set_visible_mode(publish_error_mode) self._detail_inputs_spacer.setVisible(publish_error_mode) self._detail_input_scroll.setVisible(publish_error_mode) - logs_visible = publish_error_mode or publish_finished + logs_visible = publish_error_mode or has_finished or not has_crashed self._logs_view.setVisible(logs_visible) self._crash_widget.setVisible(not logs_visible)