From 4a6f2501350b822315df89a2ae80f3881c20f41c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 5 Feb 2024 16:05:13 +0100 Subject: [PATCH] removed log viewer module --- .../ayon_core/modules/log_viewer/__init__.py | 6 - .../modules/log_viewer/log_view_module.py | 46 --- .../modules/log_viewer/tray/__init__.py | 0 .../ayon_core/modules/log_viewer/tray/app.py | 37 -- .../modules/log_viewer/tray/models.py | 138 ------- .../modules/log_viewer/tray/widgets.py | 340 ------------------ 6 files changed, 567 deletions(-) delete mode 100644 client/ayon_core/modules/log_viewer/__init__.py delete mode 100644 client/ayon_core/modules/log_viewer/log_view_module.py delete mode 100644 client/ayon_core/modules/log_viewer/tray/__init__.py delete mode 100644 client/ayon_core/modules/log_viewer/tray/app.py delete mode 100644 client/ayon_core/modules/log_viewer/tray/models.py delete mode 100644 client/ayon_core/modules/log_viewer/tray/widgets.py diff --git a/client/ayon_core/modules/log_viewer/__init__.py b/client/ayon_core/modules/log_viewer/__init__.py deleted file mode 100644 index 672f47c015..0000000000 --- a/client/ayon_core/modules/log_viewer/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .log_view_module import LogViewModule - - -__all__ = ( - "LogViewModule", -) diff --git a/client/ayon_core/modules/log_viewer/log_view_module.py b/client/ayon_core/modules/log_viewer/log_view_module.py deleted file mode 100644 index 11de8ef887..0000000000 --- a/client/ayon_core/modules/log_viewer/log_view_module.py +++ /dev/null @@ -1,46 +0,0 @@ -from ayon_core import AYON_SERVER_ENABLED -from ayon_core.modules import OpenPypeModule, ITrayModule - - -class LogViewModule(OpenPypeModule, ITrayModule): - name = "log_viewer" - - def initialize(self, modules_settings): - logging_settings = modules_settings[self.name] - self.enabled = logging_settings["enabled"] - if AYON_SERVER_ENABLED: - self.enabled = False - - # Tray attributes - self.window = None - - def tray_init(self): - try: - from .tray.app import LogsWindow - self.window = LogsWindow() - except Exception: - self.log.warning( - "Couldn't set Logging GUI due to error.", exc_info=True - ) - - # Definition of Tray menu - def tray_menu(self, tray_menu): - from qtpy import QtWidgets - # Menu for Tray App - menu = QtWidgets.QMenu('Logging', tray_menu) - - show_action = QtWidgets.QAction("Show Logs", menu) - show_action.triggered.connect(self._show_logs_gui) - menu.addAction(show_action) - - tray_menu.addMenu(menu) - - def tray_start(self): - return - - def tray_exit(self): - return - - def _show_logs_gui(self): - if self.window: - self.window.show() diff --git a/client/ayon_core/modules/log_viewer/tray/__init__.py b/client/ayon_core/modules/log_viewer/tray/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/client/ayon_core/modules/log_viewer/tray/app.py b/client/ayon_core/modules/log_viewer/tray/app.py deleted file mode 100644 index 79ece8ca76..0000000000 --- a/client/ayon_core/modules/log_viewer/tray/app.py +++ /dev/null @@ -1,37 +0,0 @@ -from qtpy import QtWidgets, QtCore -from .widgets import LogsWidget, OutputWidget -from ayon_core import style - - -class LogsWindow(QtWidgets.QWidget): - def __init__(self, parent=None): - super(LogsWindow, self).__init__(parent) - - self.setWindowTitle("Logs viewer") - - self.resize(1400, 800) - log_detail = OutputWidget(parent=self) - logs_widget = LogsWidget(log_detail, parent=self) - - main_layout = QtWidgets.QHBoxLayout(self) - - log_splitter = QtWidgets.QSplitter(self) - log_splitter.setOrientation(QtCore.Qt.Horizontal) - log_splitter.addWidget(logs_widget) - log_splitter.addWidget(log_detail) - - main_layout.addWidget(log_splitter) - - self.logs_widget = logs_widget - self.log_detail = log_detail - - self.setStyleSheet(style.load_stylesheet()) - - self._first_show = True - - def showEvent(self, event): - super(LogsWindow, self).showEvent(event) - - if self._first_show: - self._first_show = False - self.logs_widget.refresh() diff --git a/client/ayon_core/modules/log_viewer/tray/models.py b/client/ayon_core/modules/log_viewer/tray/models.py deleted file mode 100644 index 05f09c5465..0000000000 --- a/client/ayon_core/modules/log_viewer/tray/models.py +++ /dev/null @@ -1,138 +0,0 @@ -import collections -from qtpy import QtCore, QtGui -from ayon_core.lib import Logger - - -class LogModel(QtGui.QStandardItemModel): - COLUMNS = ( - "process_name", - "hostname", - "hostip", - "username", - "system_name", - "started" - ) - colums_mapping = { - "process_name": "Process Name", - "process_id": "Process Id", - "hostname": "Hostname", - "hostip": "Host IP", - "username": "Username", - "system_name": "System name", - "started": "Started at" - } - process_keys = ( - "process_id", "hostname", "hostip", - "username", "system_name", "process_name" - ) - log_keys = ( - "timestamp", "level", "thread", "threadName", "message", "loggerName", - "fileName", "module", "method", "lineNumber" - ) - default_value = "- Not set -" - - ROLE_LOGS = QtCore.Qt.UserRole + 2 - ROLE_PROCESS_ID = QtCore.Qt.UserRole + 3 - - def __init__(self, parent=None): - super(LogModel, self).__init__(parent) - - self.log_by_process = None - self.dbcon = None - - # Crash if connection is not possible to skip this module - if not Logger.initialized: - Logger.initialize() - - connection = Logger.get_log_mongo_connection() - if connection: - Logger.bootstrap_mongo_log() - database = connection[Logger.log_database_name] - self.dbcon = database[Logger.log_collection_name] - - def headerData(self, section, orientation, role): - if ( - role == QtCore.Qt.DisplayRole - and orientation == QtCore.Qt.Horizontal - ): - if section < len(self.COLUMNS): - key = self.COLUMNS[section] - return self.colums_mapping.get(key, key) - - super(LogModel, self).headerData(section, orientation, role) - - def add_process_logs(self, process_logs): - items = [] - first_item = True - for key in self.COLUMNS: - display_value = str(process_logs[key]) - item = QtGui.QStandardItem(display_value) - if first_item: - first_item = False - item.setData(process_logs["_logs"], self.ROLE_LOGS) - item.setData(process_logs["process_id"], self.ROLE_PROCESS_ID) - items.append(item) - self.appendRow(items) - - def refresh(self): - self.log_by_process = collections.defaultdict(list) - self.process_info = {} - - self.clear() - self.beginResetModel() - if self.dbcon: - result = self.dbcon.find({}) - for item in result: - process_id = item.get("process_id") - # backwards (in)compatibility - if not process_id: - continue - - if process_id not in self.process_info: - proc_dict = {"_logs": []} - for key in self.process_keys: - proc_dict[key] = ( - item.get(key) or self.default_value - ) - self.process_info[process_id] = proc_dict - - log_item = {} - for key in self.log_keys: - log_item[key] = item.get(key) or self.default_value - - if "exception" in item: - log_item["exception"] = item["exception"] - - self.process_info[process_id]["_logs"].append(log_item) - - for item in self.process_info.values(): - item["_logs"] = sorted( - item["_logs"], key=lambda item: item["timestamp"] - ) - item["started"] = item["_logs"][0]["timestamp"] - self.add_process_logs(item) - - self.endResetModel() - - -class LogsFilterProxy(QtCore.QSortFilterProxyModel): - def __init__(self, *args, **kwargs): - super(LogsFilterProxy, self).__init__(*args, **kwargs) - self.col_usernames = None - self.filter_usernames = set() - - def update_users_filter(self, users): - self.filter_usernames = set() - for user in users or tuple(): - self.filter_usernames.add(user) - self.invalidateFilter() - - def filterAcceptsRow(self, source_row, source_parent): - if self.col_usernames is not None: - index = self.sourceModel().index( - source_row, self.col_usernames, source_parent - ) - user = index.data(QtCore.Qt.DisplayRole) - if user not in self.filter_usernames: - return False - return True diff --git a/client/ayon_core/modules/log_viewer/tray/widgets.py b/client/ayon_core/modules/log_viewer/tray/widgets.py deleted file mode 100644 index 399d174fa6..0000000000 --- a/client/ayon_core/modules/log_viewer/tray/widgets.py +++ /dev/null @@ -1,340 +0,0 @@ -import html -from qtpy import QtCore, QtWidgets -import qtawesome -from .models import LogModel, LogsFilterProxy - - -class SearchComboBox(QtWidgets.QComboBox): - """Searchable ComboBox with empty placeholder value as first value""" - - def __init__(self, parent=None, placeholder=""): - super(SearchComboBox, self).__init__(parent) - - self.setEditable(True) - self.setInsertPolicy(QtWidgets.QComboBox.NoInsert) - self.lineEdit().setPlaceholderText(placeholder) - - # Apply completer settings - completer = self.completer() - completer.setCompletionMode(completer.PopupCompletion) - completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive) - - # Force style sheet on popup menu - # It won't take the parent stylesheet for some reason - # todo: better fix for completer popup stylesheet - if parent: - popup = completer.popup() - popup.setStyleSheet(parent.styleSheet()) - - self.currentIndexChanged.connect(self.onIndexChange) - - def onIndexChange(self, index): - print(index) - - def populate(self, items): - self.clear() - self.addItems([""]) # ensure first item is placeholder - self.addItems(items) - - def get_valid_value(self): - """Return the current text if it's a valid value else None - - Note: The empty placeholder value is valid and returns as "" - - """ - - text = self.currentText() - lookup = set(self.itemText(i) for i in range(self.count())) - if text not in lookup: - return None - - return text - - -class SelectableMenu(QtWidgets.QMenu): - - selection_changed = QtCore.Signal() - - def mouseReleaseEvent(self, event): - action = self.activeAction() - if action and action.isEnabled(): - action.trigger() - self.selection_changed.emit() - else: - super(SelectableMenu, self).mouseReleaseEvent(event) - - -class CustomCombo(QtWidgets.QWidget): - - selection_changed = QtCore.Signal() - - def __init__(self, title, parent=None): - super(CustomCombo, self).__init__(parent) - toolbutton = QtWidgets.QToolButton(self) - toolbutton.setText(title) - - toolmenu = SelectableMenu(self) - - toolbutton.setMenu(toolmenu) - toolbutton.setPopupMode(QtWidgets.QToolButton.MenuButtonPopup) - - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.addWidget(toolbutton) - - toolmenu.selection_changed.connect(self.selection_changed) - - self.toolbutton = toolbutton - self.toolmenu = toolmenu - self.main_layout = layout - - def populate(self, items): - self.toolmenu.clear() - self.addItems(items) - - def addItems(self, items): - for item in items: - action = self.toolmenu.addAction(item) - action.setCheckable(True) - action.setChecked(True) - self.toolmenu.addAction(action) - - def items(self): - for action in self.toolmenu.actions(): - yield action - - -class LogsWidget(QtWidgets.QWidget): - """A widget that lists the published subsets for an asset""" - - def __init__(self, detail_widget, parent=None): - super(LogsWidget, self).__init__(parent=parent) - - model = LogModel() - proxy_model = LogsFilterProxy() - proxy_model.setSourceModel(model) - proxy_model.col_usernames = model.COLUMNS.index("username") - - filter_layout = QtWidgets.QHBoxLayout() - - user_filter = CustomCombo("Users", self) - users = model.dbcon.distinct("username") - user_filter.populate(users) - user_filter.selection_changed.connect(self._user_changed) - - proxy_model.update_users_filter(users) - - level_filter = CustomCombo("Levels", self) - levels = model.dbcon.distinct("level") - level_filter.addItems(levels) - level_filter.selection_changed.connect(self._level_changed) - - detail_widget.update_level_filter(levels) - - icon = qtawesome.icon("fa.refresh", color="white") - refresh_btn = QtWidgets.QPushButton(icon, "") - - filter_layout.addWidget(user_filter) - filter_layout.addWidget(level_filter) - filter_layout.addStretch(1) - filter_layout.addWidget(refresh_btn) - - view = QtWidgets.QTreeView(self) - view.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) - - layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.addLayout(filter_layout) - layout.addWidget(view) - - view.setModel(proxy_model) - - view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) - view.setSortingEnabled(True) - view.sortByColumn( - model.COLUMNS.index("started"), - QtCore.Qt.DescendingOrder - ) - - refresh_triggered_timer = QtCore.QTimer() - refresh_triggered_timer.setSingleShot(True) - refresh_triggered_timer.setInterval(200) - - refresh_triggered_timer.timeout.connect(self._on_refresh_timeout) - view.selectionModel().selectionChanged.connect(self._on_index_change) - refresh_btn.clicked.connect(self._on_refresh_clicked) - - # Store to memory - self.model = model - self.proxy_model = proxy_model - self.view = view - - self.user_filter = user_filter - self.level_filter = level_filter - - self.detail_widget = detail_widget - self.refresh_btn = refresh_btn - - self._refresh_triggered_timer = refresh_triggered_timer - - def refresh(self): - self._refresh_triggered_timer.start() - - def _on_refresh_timeout(self): - self.model.refresh() - self.detail_widget.refresh() - - def _on_refresh_clicked(self): - self.refresh() - - def _on_index_change(self, to_index, from_index): - index = self._selected_log() - if index: - logs = index.data(self.model.ROLE_LOGS) - else: - logs = [] - self.detail_widget.set_detail(logs) - - def _user_changed(self): - checked_values = set() - for action in self.user_filter.items(): - if action.isChecked(): - checked_values.add(action.text()) - self.proxy_model.update_users_filter(checked_values) - - def _level_changed(self): - checked_values = set() - for action in self.level_filter.items(): - if action.isChecked(): - checked_values.add(action.text()) - self.detail_widget.update_level_filter(checked_values) - - def on_context_menu(self, point): - # TODO will be any actions? it's ready - return - - point_index = self.view.indexAt(point) - if not point_index.isValid(): - return - - # Get selected subsets without groups - selection = self.view.selectionModel() - rows = selection.selectedRows(column=0) - - def _selected_log(self): - selection = self.view.selectionModel() - rows = selection.selectedRows(column=0) - if len(rows) == 1: - return rows[0] - return None - - -class OutputWidget(QtWidgets.QWidget): - def __init__(self, parent=None): - super(OutputWidget, self).__init__(parent=parent) - layout = QtWidgets.QVBoxLayout(self) - - show_timecode_checkbox = QtWidgets.QCheckBox("Show timestamp", self) - - output_text = QtWidgets.QTextEdit(self) - output_text.setReadOnly(True) - # output_text.setLineWrapMode(QtWidgets.QTextEdit.FixedPixelWidth) - - layout.addWidget(show_timecode_checkbox) - layout.addWidget(output_text) - - show_timecode_checkbox.stateChanged.connect( - self.on_show_timecode_change - ) - self.setLayout(layout) - self.output_text = output_text - self.show_timecode_checkbox = show_timecode_checkbox - - self.refresh() - - def refresh(self): - self.set_detail() - - def show_timecode(self): - return self.show_timecode_checkbox.isChecked() - - def on_show_timecode_change(self): - self.set_detail(self.las_logs) - - def update_level_filter(self, levels): - self.filter_levels = set() - for level in levels or tuple(): - self.filter_levels.add(level.lower()) - - self.set_detail(self.las_logs) - - def add_line(self, line): - self.output_text.append(line) - - def set_detail(self, logs=None): - self.las_logs = logs - self.output_text.clear() - if not logs: - return - - show_timecode = self.show_timecode() - for log in logs: - level = log["level"].lower() - if level not in self.filter_levels: - continue - - line_f = "{message}" - if level == "debug": - line_f = ( - " -" - " {{ {logger_name} }}: [" - " {message}" - " ]" - ) - elif level == "info": - line_f = ( - ">>> [" - " {message}" - " ]" - ) - elif level == "warning": - line_f = ( - "*** WRN:" - " >>> {{ {logger_name} }}: [" - " {message}" - " ]" - ) - elif level == "error": - line_f = ( - "!!! ERR:" - " {timestamp}" - " >>> {{ {logger_name} }}: [" - " {message}" - " ]" - ) - - logger_name = log["loggerName"] - timestamp = "" - if not show_timecode: - timestamp = log["timestamp"] - message = log["message"] - exc = log.get("exception") - if exc: - message = exc["message"] - - line = line_f.format( - message=html.escape(message), - logger_name=logger_name, - timestamp=timestamp - ) - - if show_timecode: - timestamp = log["timestamp"] - line = timestamp.strftime("%Y-%d-%m %H:%M:%S") + " " + line - - self.add_line(line) - - if not exc: - continue - for _line in exc["stackTrace"].split("\n"): - self.add_line(_line)