Add ability to send a bug report from within the app
Some checks failed
CI / test (push) Successful in 3m23s
Lint / test (push) Failing after 27s
Trivy / test (push) Successful in 21s

This commit is contained in:
Miguel Jacq 2025-11-17 16:06:33 +11:00
parent 6bc5b66d3f
commit eedf48dc6a
Signed by: mig5
GPG key ID: 59B3F0C24135C6A9
9 changed files with 345 additions and 25 deletions

View file

@ -0,0 +1,131 @@
from __future__ import annotations
import importlib.metadata
from pathlib import Path
import requests
from PySide6.QtWidgets import (
QDialog,
QVBoxLayout,
QLabel,
QTextEdit,
QDialogButtonBox,
QMessageBox,
)
from . import strings
BUG_REPORT_HOST = "https://nr.mig5.net"
ROUTE = "forms/bouquin/bugs"
class BugReportDialog(QDialog):
"""
Dialog to collect a bug report
"""
MAX_CHARS = 5000
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle(strings._("report_a_bug"))
self._attachment_path: Path | None = None
layout = QVBoxLayout(self)
header = QLabel(strings._("bug_report_explanation"))
header.setWordWrap(True)
layout.addWidget(header)
self.text_edit = QTextEdit()
self.text_edit.setPlaceholderText(strings._("bug_report_placeholder"))
layout.addWidget(self.text_edit)
self.text_edit.textChanged.connect(self._enforce_max_length)
# Buttons: Cancel / Send
button_box = QDialogButtonBox(QDialogButtonBox.Cancel)
self.send_button = button_box.addButton(
strings._("send"), QDialogButtonBox.AcceptRole
)
button_box.accepted.connect(self._send)
button_box.rejected.connect(self.reject)
layout.addWidget(button_box)
self.text_edit.setFocus()
# ------------Helpers ------------ #
def _enforce_max_length(self):
text = self.text_edit.toPlainText()
if len(text) <= self.MAX_CHARS:
return
# Remember cursor position
cursor = self.text_edit.textCursor()
pos = cursor.position()
# Trim and restore without re-entering this slot
self.text_edit.blockSignals(True)
self.text_edit.setPlainText(text[: self.MAX_CHARS])
self.text_edit.blockSignals(False)
# Clamp cursor position to end of text
if pos > self.MAX_CHARS:
pos = self.MAX_CHARS
cursor.setPosition(pos)
self.text_edit.setTextCursor(cursor)
def _send(self):
text = self.text_edit.toPlainText().strip()
if not text:
QMessageBox.warning(
self,
strings._("report_a_bug"),
strings._("bug_report_empty"),
)
return
# Get current app version
try:
version = importlib.metadata.version("bouquin")
except importlib.metadata.PackageNotFoundError:
version = "unknown"
payload: dict[str, str] = {
"message": text,
"version": version,
}
# POST as JSON
try:
resp = requests.post(
f"{BUG_REPORT_HOST}/{ROUTE}",
json=payload,
timeout=10,
)
except Exception as e:
QMessageBox.critical(
self,
strings._("report_a_bug"),
strings._("bug_report_send_failed") + f"\n{e}",
)
return
if resp.status_code == 201:
QMessageBox.information(
self,
strings._("report_a_bug"),
strings._("bug_report_sent_ok"),
)
self.accept()
else:
QMessageBox.critical(
self,
strings._("report_a_bug"),
strings._("bug_report_send_failed") + f" (HTTP {resp.status_code})",
)

View file

@ -148,6 +148,11 @@
"stats_metric_words": "Words",
"stats_metric_revisions": "Revisions",
"stats_no_data": "No statistics available yet.",
"select_notebook": "Select notebook"
"select_notebook": "Select notebook",
"bug_report_explanation": "Describe what went wrong, what you expected to happen, and any steps to reproduce.\n\nWe do not collect anything else except the Bouquin version number.\n\nIf you wish to be contacted, please leave contact information.\n\nYour request will be sent over HTTPS.",
"bug_report_placeholder": "Type your bug report here",
"bug_report_empty": "Please enter some details about the bug before sending.",
"bug_report_send_failed": "Could not send bug report.",
"bug_report_sent_ok": "Bug report sent. Thank you!",
"send": "Send"
}

View file

@ -56,6 +56,7 @@ from .search import Search
from .settings import APP_ORG, APP_NAME, load_db_config, save_db_config
from .settings_dialog import SettingsDialog
from .statistics_dialog import StatisticsDialog
from .bug_report_dialog import BugReportDialog
from . import strings
from .tags_widget import PageTagsWidget
from .toolbar import ToolBar
@ -1292,14 +1293,8 @@ class MainWindow(QMainWindow):
)
def _open_bugs(self):
url_str = "https://nr.mig5.net/forms/mig5/contact"
url = QUrl.fromUserInput(url_str)
if not QDesktopServices.openUrl(url):
QMessageBox.warning(
self,
strings._("report_a_bug"),
strings._("couldnt_open") + url.toDisplayString(),
)
dlg = BugReportDialog(self)
dlg.exec()
def _open_version(self):
version = importlib.metadata.version("bouquin")