This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
================================================================================
|
||||
|
||||
【已完成】
|
||||
- V10.213 補 `/observability/ppt_audit_history` 視覺 QA 診斷摘要:審核歷史與 Action Queue 直接顯示 `ppt_audit_results.issues_found` 的投影片問題,讓「有問題」可追查,不再只剩問題數或空白錯誤欄。
|
||||
- V10.212 修正 PPT 視覺 QA 的 Ollama 三主機 fallback:當 Primary/Secondary request timeout 超過 unhealthy TTL 時,第三輪仍強制打 111 final fallback;PPT 截圖送模型前轉輕量 JPEG、縮小輸出 token,降低單檔審核耗時。
|
||||
- V10.211 補 `/observability/ppt_audit_history` 全類型視覺 QA:審核歷史不再限 daily,頁面新增「立即視覺 QA」非阻塞補跑,結果寫入 `ppt_audit_results`;模型失敗時也保留 slide error,避免產線狀態只剩空白。
|
||||
- V10.210 補 `/observability/ppt_audit_history` 審核歷史同頁回放:每筆 daily 視覺審核紀錄的動作欄新增「回放」按鈕,沿用 PDF 預覽抽屜並保留下載/開新頁,讓問題追查不必再回檔案表找簡報。
|
||||
|
||||
@@ -320,7 +320,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
|
||||
# ==========================================
|
||||
# 系統版本與路徑
|
||||
# ==========================================
|
||||
SYSTEM_VERSION = "V10.212"
|
||||
SYSTEM_VERSION = "V10.213"
|
||||
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
|
||||
public_url = PUBLIC_URL # 用於模板顯示
|
||||
|
||||
|
||||
@@ -2473,7 +2473,7 @@ def _build_ppt_pipeline_view(files, auto_generation, audit_stats, generation_run
|
||||
triage_entries.append({
|
||||
'title': item.get('pptx_filename') or '未命名檔案',
|
||||
'meta': item.get('audited_at') or '時間未知',
|
||||
'detail': item.get('error_msg') or f"問題 {item.get('issues_count', 0)} 個",
|
||||
'detail': item.get('issue_summary') or item.get('error_msg') or f"問題 {item.get('issues_count', 0)} 個",
|
||||
'status_label': '視覺 QA',
|
||||
'filename': item.get('pptx_filename') or '',
|
||||
'report_type': 'daily',
|
||||
@@ -2538,7 +2538,7 @@ def _build_ppt_pipeline_view(files, auto_generation, audit_stats, generation_run
|
||||
{
|
||||
'title': item.get('pptx_filename') or '未命名檔案',
|
||||
'meta': item.get('audited_at') or '時間未知',
|
||||
'detail': item.get('error_msg') or f"問題 {item.get('issues_count', 0)} 個,信心 {item.get('confidence', 0):.2f}",
|
||||
'detail': item.get('issue_summary') or item.get('error_msg') or f"問題 {item.get('issues_count', 0)} 個,信心 {item.get('confidence', 0):.2f}",
|
||||
'status_label': '需修復' if item.get('audit_status') == 'failed' else '需排查',
|
||||
'filename': item.get('pptx_filename'),
|
||||
}
|
||||
@@ -2792,6 +2792,32 @@ def ppt_audit_history():
|
||||
audit_filter_sql = " AND pptx_filename LIKE :audit_prefix"
|
||||
audit_params['audit_prefix'] = f"{report_prefix}%"
|
||||
|
||||
def _summarize_ppt_issues(raw_issues) -> str:
|
||||
"""把 ppt_audit_results.issues_found 壓成表格可讀的診斷摘要。"""
|
||||
if not raw_issues:
|
||||
return ''
|
||||
try:
|
||||
import json as _json
|
||||
issues_payload = _json.loads(raw_issues) if isinstance(raw_issues, str) else raw_issues
|
||||
except Exception:
|
||||
return ''
|
||||
if not isinstance(issues_payload, list):
|
||||
return ''
|
||||
snippets = []
|
||||
for item in issues_payload:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
slide = item.get('slide')
|
||||
for issue in item.get('issues') or []:
|
||||
text = str(issue).strip()
|
||||
if not text:
|
||||
continue
|
||||
prefix = f"S{slide}: " if slide else ""
|
||||
snippets.append(f"{prefix}{text}")
|
||||
if len(snippets) >= 3:
|
||||
return ';'.join(snippets)
|
||||
return ';'.join(snippets)
|
||||
|
||||
# Phase 38+:讀指定月份 / 指定簡報類型 audit 歷史
|
||||
try:
|
||||
session = get_session()
|
||||
@@ -2799,7 +2825,8 @@ def ppt_audit_history():
|
||||
audit_rows = session.execute(
|
||||
sa_text(f"""
|
||||
SELECT audited_at, pptx_filename, audit_status,
|
||||
issues_count, confidence, duration_ms, error_msg
|
||||
issues_count, confidence, duration_ms, error_msg,
|
||||
issues_found
|
||||
FROM ppt_audit_results
|
||||
WHERE audited_at >= :month_start
|
||||
AND audited_at < :month_end
|
||||
@@ -2818,6 +2845,7 @@ def ppt_audit_history():
|
||||
'confidence': float(r[4] or 0),
|
||||
'duration_ms': int(r[5] or 0),
|
||||
'error_msg': r[6],
|
||||
'issue_summary': _summarize_ppt_issues(r[7]),
|
||||
}
|
||||
for r in audit_rows
|
||||
]
|
||||
|
||||
@@ -373,7 +373,7 @@
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm mb-0">
|
||||
<thead class="table-light">
|
||||
<tr><th>時間</th><th>檔名</th><th>結果</th><th class="text-end">問題</th><th class="text-end">信心</th><th class="text-end">耗時</th><th>錯誤</th><th>動作</th></tr>
|
||||
<tr><th>時間</th><th>檔名</th><th>結果</th><th class="text-end">問題</th><th class="text-end">信心</th><th class="text-end">耗時</th><th>診斷摘要</th><th>動作</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for r in audit_records %}
|
||||
@@ -390,7 +390,7 @@
|
||||
<td class="text-end">{{ r.issues_count }}</td>
|
||||
<td class="text-end">{{ "%.2f"|format(r.confidence) }}</td>
|
||||
<td class="text-end">{{ r.duration_ms }}</td>
|
||||
<td><small class="text-muted">{{ (r.error_msg or '')[:80] }}</small></td>
|
||||
<td><small class="text-muted">{{ (r.issue_summary or r.error_msg or '')[:180] }}</small></td>
|
||||
<td>
|
||||
{% if r.pptx_filename %}
|
||||
<a class="btn btn-sm btn-outline-primary"
|
||||
|
||||
@@ -343,7 +343,16 @@ def test_ppt_audit_history_audit_rows_include_inline_replay(client, monkeypatch,
|
||||
result.fetchall.return_value = rows
|
||||
elif 'SELECT audited_at, pptx_filename' in sql:
|
||||
rows = [
|
||||
(datetime(2026, 5, 17, 22, 5), 'ocbot_daily_20260517.pptx', 'passed', 0, 0.93, 1200, '')
|
||||
(
|
||||
datetime(2026, 5, 17, 22, 5),
|
||||
'ocbot_daily_20260517.pptx',
|
||||
'failed',
|
||||
1,
|
||||
0.93,
|
||||
1200,
|
||||
'',
|
||||
[{'slide': 1, 'issues': ['⚠️ 圖表被切掉:右側圖例超出邊界']}],
|
||||
)
|
||||
]
|
||||
result.fetchall.return_value = rows
|
||||
elif 'COALESCE(AVG(confidence)' in sql:
|
||||
@@ -370,6 +379,7 @@ def test_ppt_audit_history_audit_rows_include_inline_replay(client, monkeypatch,
|
||||
assert '審核回放 · ocbot_daily_20260517.pptx' in html
|
||||
assert 'data-ppt-open-preview' in html
|
||||
assert 'ocbot_daily_20260517.pptx?action=pdf' in html
|
||||
assert '圖表被切掉' in html
|
||||
assert '回放' in html
|
||||
|
||||
|
||||
@@ -396,7 +406,7 @@ def test_ppt_audit_history_weekly_rows_include_visual_audit(client, monkeypatch,
|
||||
result.fetchall.return_value = []
|
||||
elif 'SELECT audited_at, pptx_filename' in sql:
|
||||
result.fetchall.return_value = [
|
||||
(datetime(2026, 5, 18, 22, 7), 'ocbot_weekly_20260518.pptx', 'passed', 0, 0.91, 1800, '')
|
||||
(datetime(2026, 5, 18, 22, 7), 'ocbot_weekly_20260518.pptx', 'passed', 0, 0.91, 1800, '', [])
|
||||
]
|
||||
elif 'COALESCE(AVG(confidence)' in sql:
|
||||
result.fetchone.return_value = (1, 1, 0, 0, 0, 0.91, 0)
|
||||
|
||||
Reference in New Issue
Block a user