"""HabitsWidget — the usage-habits summary + AI recommendations panel of the Dashboard (R08-T13, extracted from ``ui/dashboard_tab.py::DashboardTab``, lines 154-184/348-372/376-438 of the original 437-line file: the habits/AI layout, ``refresh()``'s habits-HTML section, ``_apply_saving_strategy``, ``_ai_analyze``). """ from __future__ import annotations from datetime import date from typing import List, Optional from PySide6.QtWidgets import QHBoxLayout, QLabel, QPushButton, QTextBrowser, QVBoxLayout, QWidget from PySide6.QtCore import Signal from cowork_local.application.monitoring import DashboardQueryService from cowork_local.core.worker import AgentWorker from cowork_local.i18n import tr from cowork_local.ui.icons import icon from cowork_local.ui.widgets import fmt_tokens class HabitsWidget(QWidget): """Thẻ "Thói quen dùng model" của Dashboard, kèm phần AI phân tích. Chỉ gửi SỐ LIỆU đã tổng hợp lên provider, không bao giờ gửi nội dung prompt thật — xem :meth:`_ai_analyze`. """ status_message = Signal(str) def __init__(self, ctx, query: DashboardQueryService, parent=None): """Bảng thói quen dùng model, kèm nút nhờ model tự nhận xét. Kỳ đang xem được nhớ lại ở mỗi lần ``refresh()`` để nút nhận xét dùng đúng khoảng thời gian đang hiện trên màn hình. """ super().__init__(parent) self.ctx = ctx self._query = query self._ai_worker: Optional[AgentWorker] = None self._period_range = (None, None) # set on each refresh(); _ai_analyze reuses it root = QVBoxLayout(self) root.setContentsMargins(0, 0, 0, 0) self._habits_title = QLabel() self._habits_title.setStyleSheet("font-weight:600;") habits_head = QHBoxLayout() self.ai_analyze_btn = QPushButton() self.ai_analyze_btn.setIcon(icon("sparkle")) self.ai_analyze_btn.clicked.connect(self._ai_analyze) # Apply an AI-suggested cost-saving strategy — only after the user # clicks to approve it. self.apply_strategy_btn = QPushButton() self.apply_strategy_btn.setIcon(icon("bolt")) self.apply_strategy_btn.setVisible(False) self.apply_strategy_btn.clicked.connect(self._apply_saving_strategy) habits_head.addWidget(self._habits_title, 1) habits_head.addWidget(self.apply_strategy_btn) habits_head.addWidget(self.ai_analyze_btn) root.addLayout(habits_head) self.habits = QTextBrowser() self.habits.setOpenExternalLinks(False) self.habits.setMinimumHeight(160) root.addWidget(self.habits, 1) self._ai_title = QLabel() self._ai_title.setStyleSheet("font-weight:600;") self._ai_title.setVisible(False) root.addWidget(self._ai_title) self.ai_advice = QTextBrowser() self.ai_advice.setOpenExternalLinks(False) self.ai_advice.setMinimumHeight(140) self.ai_advice.setVisible(False) root.addWidget(self.ai_advice, 1) self.retranslate() def retranslate(self) -> None: """Áp lại chữ theo ngôn ngữ đang chọn.""" self.ai_analyze_btn.setText(tr("dashboard.ai_analyze_btn")) self.ai_analyze_btn.setToolTip(tr("dashboard.ai_analyze_tooltip")) self.apply_strategy_btn.setText(tr("dashboard.strategy_btn")) self.apply_strategy_btn.setToolTip(tr("dashboard.strategy_tooltip")) self._habits_title.setText(tr("dashboard.habits_title")) def refresh(self, start: date, end: date) -> None: """Vẽ lại bảng thói quen dùng model cho một kỳ, và nhớ kỳ đó cho nút AI phân tích.""" self._period_range = (start, end) summary = self._query.summary(start, end) s, events = summary["stats"], summary["events"] lines: List[str] = [] if not events: lines.append(f"{tr('dashboard.no_data')}") else: lines.append(f"{tr('dashboard.h_top')}") lines.append("