refactor(ui): xoá 108 dòng MS365 chết trong settings_dialog.py
Sót lại từ lần dời UI Connector sang Monitoring → Tools → Connector. Năm hàm:
_refresh_ms365_status 13 _show_ms365_device_code 52
_ms365_sign_in 34 _ms365_sign_out 4
_close_ms365_code_dialog 5
Chứng minh chết trước khi xoá, không xoá theo cảm tính:
* Dựng đồ thị lời gọi bằng ast: **mọi** lời gọi tới năm hàm này đều xuất phát
từ bên trong chính năm hàm đó. Không một đường vào nào từ ngoài cụm — cả
trong file lẫn toàn repo.
* Ba thuộc tính chúng đọc — ms365_status, ms365_signin_btn, ms365_signout_btn
— **chưa từng được gán ở đâu**. Gọi vào là AttributeError, không phải chạy sai
mà là sập.
* _ms365_workers chỉ được append bên trong _ms365_sign_in, nên chết theo.
Dọn kèm 6 import chỉ còn dòng import: AgentWorker, icon, EXT_CATEGORIES,
ExtConnectorEditDialog, AppContext, QTreeWidget.
Viết lại docstring đầu file — bản cũ vẫn mô tả file này chứa nhóm Connector
(CAD/CAE/MS365/Other), thứ đã không còn ở đây từ lâu.
settings_dialog.py: 407 -> 303 dòng. Cộng cả R08-T07 thì từ 727 xuống 303.
632 test xanh. check_dialogs, check_no_hscroll, check_design_parity,
check_orphans, check_probes_bite đều qua.
Ghi lại một phát hiện phụ, CHƯA xử lý: i18n.py có 28 khoá settings.ms365_*
mồ côi — 20 khoá đã không ai dùng từ trước lần dời connector, 8 khoá vừa mồ
côi theo commit này. Chỉ 2 khoá còn sống (ms365_local_connected,
ms365_local_none, dùng ở ui/connectors_panel.py). Xoá khoá dịch là đụng vào
dữ liệu ba ngôn ngữ ở file khác nên để anh Nam quyết riêng.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
c77ce36191
commit
4c3b097977
+15
-119
@@ -1,6 +1,16 @@
|
||||
"""Settings dialog: AI provider, Sandbox, the unified Connectors (MCP) group
|
||||
(CAD / CAE / MS365 / Other — MCP servers + REST connectors in one place),
|
||||
and the merged "Parameter" group (Cowork / Attachments / GraphRAG caps)."""
|
||||
"""Hộp thoại Cài đặt — khung lắp ráp.
|
||||
|
||||
Năm mục, mỗi mục một trang: Chung, AI Provider, Bảo mật sandbox, Tham số,
|
||||
Auto Model Routing. Bốn mục đầu... đúng hơn: bốn trong năm mục đã bóc sang
|
||||
``presentation/settings/`` (R08-T07); file này còn giữ mục Bảo mật sandbox,
|
||||
phần lắp ráp danh sách mục bên trái, và ``_save`` gọi ``apply_to`` của từng
|
||||
widget con.
|
||||
|
||||
Không còn phần Connector nào ở đây: nó đã dời sang Monitoring → Tools →
|
||||
Connector từ trước. Ngày 25/08 dọn nốt 108 dòng MS365 chết còn sót lại của
|
||||
lần dời đó — năm hàm gọi lẫn nhau, không đường vào, và đọc ba thuộc tính
|
||||
chưa từng được gán nên gọi vào là AttributeError.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from PySide6.QtCore import Qt
|
||||
@@ -8,17 +18,13 @@ from PySide6.QtGui import QGuiApplication
|
||||
from PySide6.QtWidgets import (
|
||||
QCheckBox, QComboBox, QDialog, QDialogButtonBox, QFileDialog, QFormLayout,
|
||||
QGroupBox, QHBoxLayout, QLabel, QLineEdit, QListWidget, QListWidgetItem,
|
||||
QMessageBox, QPushButton, QScrollArea, QSpinBox, QTreeWidget,
|
||||
QMessageBox, QPushButton, QScrollArea, QSpinBox,
|
||||
QTreeWidgetItem, QVBoxLayout, QWidget,
|
||||
)
|
||||
|
||||
from ..core.ext_connectors import CATEGORIES as EXT_CATEGORIES
|
||||
from ..core.worker import AgentWorker
|
||||
from ..i18n import tr
|
||||
from ..state import AppContext
|
||||
from .icons import icon, IconLabel
|
||||
from .icons import IconLabel
|
||||
from .widgets import ToggleSwitch
|
||||
from .ext_connector_dialog import ExtConnectorEditDialog
|
||||
|
||||
|
||||
from ..presentation.settings.general_settings_widget import GeneralSettingsWidget
|
||||
@@ -133,9 +139,7 @@ class SettingsDialog(QDialog):
|
||||
root.addWidget(self.sandbox_group)
|
||||
|
||||
# Connectors (MCP / REST API) are managed entirely in Monitoring → Tools
|
||||
# → Connector now — no connector UI in Settings. (_ms365_workers is kept
|
||||
# for the dead-but-retained MS365 OAuth sign-in handlers below.)
|
||||
self._ms365_workers = []
|
||||
|
||||
# --- Parameter ---
|
||||
# Đã bóc sang presentation/settings/parameter_settings_widget.py (R08-T07).
|
||||
@@ -249,118 +253,10 @@ class SettingsDialog(QDialog):
|
||||
|
||||
|
||||
# ---- MS365 zero-config sign-in ("connect like Claude") ---------------
|
||||
def _refresh_ms365_status(self) -> None:
|
||||
from ..core.ms365_auth import current_identity
|
||||
who = current_identity(self.ctx.config)
|
||||
if who:
|
||||
self.ms365_status.setText(tr("settings.ms365_signed_in", who=who))
|
||||
self.ms365_signin_btn.setEnabled(False)
|
||||
self.ms365_signout_btn.setEnabled(True)
|
||||
else:
|
||||
self.ms365_status.setText(tr("settings.ms365_signed_out"))
|
||||
self.ms365_signin_btn.setEnabled(True)
|
||||
self.ms365_signout_btn.setEnabled(False)
|
||||
self.ms365_signin_btn.setText(tr("settings.ms365_signin_btn"))
|
||||
self.ms365_signout_btn.setText(tr("settings.ms365_signout_btn"))
|
||||
|
||||
def _ms365_sign_in(self) -> None:
|
||||
from ..core.ms365_auth import current_identity, sign_in
|
||||
self.ms365_signin_btn.setEnabled(False)
|
||||
self.ms365_status.setText(tr("settings.ms365_signing_in"))
|
||||
cfg = self.ctx.config
|
||||
|
||||
def job(worker):
|
||||
# on_code fires (worker thread) with the MSAL device-flow dict —
|
||||
# marshal it to the UI thread via the worker's event signal.
|
||||
return sign_in(lambda flow: worker.event.emit({"device_flow": flow}), cfg)
|
||||
|
||||
def on_event(ev: dict) -> None:
|
||||
if "device_flow" in ev:
|
||||
self._show_ms365_device_code(ev["device_flow"])
|
||||
|
||||
def done(_result) -> None:
|
||||
self._close_ms365_code_dialog()
|
||||
self.ctx.save()
|
||||
self._refresh_ms365_status()
|
||||
QMessageBox.information(
|
||||
self, tr("settings.ms365_signin_btn"),
|
||||
tr("settings.ms365_signed_in", who=current_identity(cfg)))
|
||||
|
||||
def failed(err: str) -> None:
|
||||
self._close_ms365_code_dialog()
|
||||
self._refresh_ms365_status()
|
||||
QMessageBox.warning(self, tr("settings.ms365_signin_btn"), err)
|
||||
|
||||
w = AgentWorker(job)
|
||||
w.event.connect(on_event)
|
||||
w.finished_ok.connect(done)
|
||||
w.failed.connect(failed)
|
||||
self._ms365_workers.append(w)
|
||||
w.start()
|
||||
|
||||
def _close_ms365_code_dialog(self) -> None:
|
||||
dlg = getattr(self, "_ms365_code_dialog", None)
|
||||
if dlg is not None:
|
||||
dlg.close()
|
||||
self._ms365_code_dialog = None
|
||||
|
||||
def _show_ms365_device_code(self, flow: dict) -> None:
|
||||
"""Auto-open the sign-in page + show the one-time code in a COPYABLE,
|
||||
non-modal dialog (so the worker keeps polling and can auto-close it on
|
||||
success). The code is also copied to the clipboard immediately."""
|
||||
import webbrowser
|
||||
|
||||
code = flow.get("user_code", "")
|
||||
url = flow.get("verification_uri", "https://microsoft.com/devicelogin")
|
||||
# Auto-copy the code so the user can just paste it.
|
||||
QGuiApplication.clipboard().setText(code)
|
||||
# Auto-open the browser to the (code-prefilled, if available) sign-in page.
|
||||
try:
|
||||
webbrowser.open(flow.get("verification_uri_complete") or url)
|
||||
except Exception: # noqa: BLE001 — a headless box just shows the link to click
|
||||
pass
|
||||
|
||||
self._close_ms365_code_dialog()
|
||||
dlg = QDialog(self)
|
||||
dlg.setWindowTitle(tr("settings.ms365_signin_btn"))
|
||||
dlg.setMinimumWidth(420)
|
||||
lay = QVBoxLayout(dlg)
|
||||
info = QLabel(tr("settings.ms365_code_hint", url=url))
|
||||
info.setWordWrap(True)
|
||||
info.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextBrowserInteraction)
|
||||
info.setOpenExternalLinks(True)
|
||||
lay.addWidget(info)
|
||||
|
||||
code_row = QHBoxLayout()
|
||||
code_edit = QLineEdit(code)
|
||||
code_edit.setReadOnly(True)
|
||||
f = code_edit.font()
|
||||
f.setPointSize(f.pointSize() + 4)
|
||||
f.setBold(True)
|
||||
code_edit.setFont(f)
|
||||
code_edit.setCursorPosition(0)
|
||||
copy_btn = QPushButton(tr("settings.ms365_copy_code"))
|
||||
copy_btn.setIcon(icon("document"))
|
||||
copy_btn.clicked.connect(lambda: QGuiApplication.clipboard().setText(code))
|
||||
open_btn = QPushButton(tr("settings.ms365_open_link"))
|
||||
open_btn.setIcon(icon("link"))
|
||||
open_btn.clicked.connect(lambda: webbrowser.open(flow.get("verification_uri_complete") or url))
|
||||
code_row.addWidget(code_edit, 1)
|
||||
code_row.addWidget(copy_btn)
|
||||
code_row.addWidget(open_btn)
|
||||
lay.addLayout(code_row)
|
||||
|
||||
buttons = QDialogButtonBox(QDialogButtonBox.Close)
|
||||
buttons.rejected.connect(dlg.reject)
|
||||
lay.addWidget(buttons)
|
||||
|
||||
self._ms365_code_dialog = dlg
|
||||
dlg.show() # non-modal — sign-in polling continues; done() closes it
|
||||
|
||||
def _ms365_sign_out(self) -> None:
|
||||
from ..core.ms365_auth import sign_out_default
|
||||
sign_out_default(self.ctx.config)
|
||||
self._refresh_ms365_status()
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user