KDE bug tracker probably causing more harm than good

I meant in the normal website view.

@ngraham, in lieu of bugzilla.mozilla.org/show_bug.cgi?id=2010435, being resolved, the best that I can check is BZ 5.2 and MBZ, but because both appear to lack a tree table view, I imagine that your best shot is to request it at bugzilla.mozilla.org/enter_bug.cgi?product=Bugzilla&component=User%20Interface. [1] Note, though, that because KDEBZ is still on 5.0.2, we’d still need to upgrade to the latest upstream version, albeit something that should be done anyway.

Regardless, the REST API is quicker than the website, by far, and having its data piped into something that can represent the bugs in that manner wouldn’t be difficult to create:

  1. #!/usr/bin/env python3
    from __future__ import annotations
    
    import sys
    from collections import defaultdict
    from datetime import datetime
    from urllib.parse import parse_qs, urlparse
    
    import requests
    
    from PySide6.QtCore import Qt, QUrl
    from PySide6.QtGui import QDesktopServices, QStandardItem, QStandardItemModel
    from PySide6.QtWidgets import (
        QApplication,
        QAbstractItemView,
        QHBoxLayout,
        QHeaderView,
        QLineEdit,
        QMainWindow,
        QMessageBox,
        QPushButton,
        QTreeView,
        QVBoxLayout,
        QWidget,
    )
    
    BUG_URL_ROLE = Qt.ItemDataRole.UserRole
    BUGZILLA_REST_URL = "https://bugs.kde.org/rest/bug"
    
    
    class MainWindow(QMainWindow):
        def __init__(self) -> None:
            super().__init__()
    
            self.setWindowTitle("KDE Bugzilla Query Viewer")
    
            central = QWidget()
            self.setCentralWidget(central)
    
            layout = QVBoxLayout(central)
    
            search_layout = QHBoxLayout()
            layout.addLayout(search_layout)
    
            self.url_edit = QLineEdit()
            self.url_edit.setPlaceholderText(
                "Paste a Bugzilla buglist URL"
            )
            search_layout.addWidget(self.url_edit)
    
            self.load_button = QPushButton("Load")
            self.load_button.clicked.connect(self.load_query)
            search_layout.addWidget(self.load_button)
    
            self.tree = QTreeView()
            layout.addWidget(self.tree)
    
            self.model = QStandardItemModel()
            self.model.setHorizontalHeaderLabels(
                ["ID", "Summary", "Severity", "Status", "Created"]
            )
    
            self.tree.setModel(self.model)
            self.tree.setUniformRowHeights(True)
    
            # Prevent inline editing entirely.
            self.tree.setEditTriggers(
                QAbstractItemView.EditTrigger.NoEditTriggers
            )
    
            # Open bugs on double-click.
            self.tree.doubleClicked.connect(self.open_bug)
    
            self.tree.header().setSectionResizeMode(
                1, QHeaderView.ResizeMode.Stretch
            )
    
        def extract_rest_params(self, url: str) -> dict[str, str]:
            parsed = urlparse(url)
            query = parse_qs(parsed.query)
    
            params: dict[str, str] = {}
    
            for key in (
                "quicksearch",
                "product",
                "component",
                "severity",
                "bug_status",
                "resolution",
            ):
                if key in query:
                    params[key] = query[key][0]
    
            return params
    
        def load_query(self) -> None:
            try:
                params = self.extract_rest_params(
                    self.url_edit.text().strip()
                )
    
                if not params:
                    raise ValueError(
                        "No supported Bugzilla query parameters were found."
                    )
    
                params["include_fields"] = (
                    "id,summary,severity,status,creation_time"
                )
    
                response = requests.get(
                    BUGZILLA_REST_URL,
                    params=params,
                    timeout=30,
                )
                response.raise_for_status()
    
                bugs = response.json()["bugs"]
    
                bugs.sort(
                    key=lambda bug: datetime.fromisoformat(
                        bug["creation_time"].replace("Z", "+00:00")
                    )
                )
    
                grouped: dict[str, list[dict]] = defaultdict(list)
    
                for bug in bugs:
                    grouped[bug.get("severity") or "unspecified"].append(bug)
    
                self.model.removeRows(0, self.model.rowCount())
    
                for severity in sorted(grouped):
                    severity_item = QStandardItem(severity)
                    severity_item.setEditable(False)
    
                    self.model.appendRow(
                        [
                            severity_item,
                            QStandardItem(),
                            QStandardItem(),
                            QStandardItem(),
                            QStandardItem(),
                        ]
                    )
    
                    for bug in grouped[severity]:
                        created = (
                            datetime.fromisoformat(
                                bug["creation_time"].replace("Z", "+00:00")
                            )
                            .astimezone()
                            .strftime("%Y-%m-%d %H:%M:%S %z")
                        )
    
                        url = (
                            f"https://bugs.kde.org/show_bug.cgi?id={bug['id']}"
                        )
    
                        items = [
                            QStandardItem(str(bug["id"])),
                            QStandardItem(bug["summary"]),
                            QStandardItem(bug["severity"]),
                            QStandardItem(bug["status"]),
                            QStandardItem(created),
                        ]
    
                        for item in items:
                            item.setData(url, BUG_URL_ROLE)
                            item.setEditable(False)
    
                        severity_item.appendRow(items)
    
                self.tree.expandAll()
    
            except Exception as exc:
                QMessageBox.critical(self, "Error", str(exc))
    
        def open_bug(self, index) -> None:
            item = self.model.itemFromIndex(index)
    
            if item is None:
                return
    
            url = item.data(BUG_URL_ROLE)
    
            if url:
                QDesktopServices.openUrl(QUrl(url))
    
    
    def main() -> int:
        app = QApplication(sys.argv)
    
        window = MainWindow()
        window.resize(1200, 700)
        window.show()
    
        return app.exec()
    
    
    if __name__ == "__main__":
        raise SystemExit(main())
    

  1. x.com/bugzilla/status/2063063839688597738 ↩︎