diff --git a/TODO_NEXT_STEPS.txt b/TODO_NEXT_STEPS.txt index df5197f..af8b159 100644 --- a/TODO_NEXT_STEPS.txt +++ b/TODO_NEXT_STEPS.txt @@ -4,6 +4,7 @@ ================================================================================ 【已完成】 + - V10.196 補 `/observability/ppt_audit_history` Action Queue:把待補齊、可預覽、視覺 QA、DB 寫入集中成工作隊列,讓使用者不用在多張卡與表格間找下一個處理點。 - V10.194 重整 `/observability/ppt_audit_history` 產線資訊階層:新增 Pipeline Health、五段式流程階段、排程/覆蓋/DB/預覽/視覺 QA 狀態摘要,讓「已產生」改為可判斷的目標產生、其他版本、待排程補齊等狀態。 - V10.192 補 `/observability/ppt_audit_history` 最近可預覽簡報 workbench:最新 4 份 PPT 直接在控制台下方提供線上預覽與下載,降低使用者找檔案的操作成本;完整檔案清單仍保留在下方表格。 - V10.190 補 `/observability/ppt_audit_file/` 站內線上預覽:PPTX 由 LibreOffice 轉 PDF 快取後以 iframe 預覽,保留原始 PPTX 下載;Dockerfile 加 `libreoffice-impress`,compose 預設啟用 `PPT_VISION_ENABLED=true`,PPT 產線頁新增視覺 QA 停用原因與更精簡的控制台式排版。 diff --git a/config.py b/config.py index 1d754cd..adbdbb0 100644 --- a/config.py +++ b/config.py @@ -320,7 +320,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.195" +SYSTEM_VERSION = "V10.196" LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log') public_url = PUBLIC_URL # 用於模板顯示 diff --git a/routes/admin_observability_routes.py b/routes/admin_observability_routes.py index fa95b9f..8932db5 100644 --- a/routes/admin_observability_routes.py +++ b/routes/admin_observability_routes.py @@ -2229,13 +2229,14 @@ def budget_update(budget_id: int): # /observability/ppt_audit_history — Phase 29 PPT 視覺審核歷史 # ───────────────────────────────────────────────────────────────────────────── -def _build_ppt_pipeline_view(files, auto_generation, audit_stats, generation_runs, vision_status): +def _build_ppt_pipeline_view(files, auto_generation, audit_stats, generation_runs, vision_status, audit_records=None): """Compose page-level PPT pipeline health so the template stays declarative.""" files = files or [] auto_generation = auto_generation or {} audit_stats = audit_stats or {} generation_runs = generation_runs or [] vision_status = vision_status or {} + audit_records = audit_records or [] def _as_int(value): try: @@ -2345,6 +2346,88 @@ def _build_ppt_pipeline_view(files, auto_generation, audit_stats, generation_run 'status': qa_status, }, ] + missing_items = [ + item for item in auto_generation.get('items', []) + if not item.get('ready') + ] + preview_items = [ + item for item in files + if item.get('file_exists') and item.get('is_valid_ppt') + ] + audit_attention = [ + item for item in audit_records + if item.get('audit_status') in ('failed', 'error') + ] + + action_lanes = [ + { + 'key': 'missing', + 'label': '待補齊', + 'status': 'partial' if missing_items else 'ready', + 'count': len(missing_items), + 'empty_text': '目前定義簡報都已對齊本期目標。', + 'entries': [ + { + 'title': item.get('label') or item.get('key') or '未命名簡報', + 'meta': item.get('target_label') or '最新資料', + 'detail': item.get('status_hint') or item.get('status_label') or '等待排程補齊', + 'status_label': item.get('status_label') or '待補齊', + } + for item in missing_items[:4] + ], + }, + { + 'key': 'preview', + 'label': '可預覽', + 'status': 'ready' if preview_items else 'planned', + 'count': len(preview_items), + 'empty_text': '目前沒有可線上預覽的 PPTX 檔案。', + 'entries': [ + { + 'title': item.get('name') or '未命名檔案', + 'meta': item.get('mtime') or '時間未知', + 'detail': f"{item.get('size_kb') if item.get('size_kb') is not None else '—'} KB · {item.get('source') or 'filesystem'}", + 'status_label': '線上預覽', + 'filename': item.get('name'), + } + for item in preview_items[:4] + ], + }, + { + 'key': 'audit', + 'label': '視覺 QA', + 'status': 'error' if audit_attention else ('ready' if audit_total else 'planned'), + 'count': len(audit_attention) if audit_attention else audit_total, + 'empty_text': '目前沒有需要處理的視覺 QA 失敗紀錄。', + 'entries': [ + { + '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}", + 'status_label': '需修復' if item.get('audit_status') == 'failed' else '需排查', + 'filename': item.get('pptx_filename'), + } + for item in audit_attention[:4] + ], + }, + { + 'key': 'database', + 'label': 'DB 寫入', + 'status': 'error' if run_error_count else ('ready' if generation_runs else 'planned'), + 'count': len(generation_runs), + 'empty_text': '本月尚未看到 ppt_generation_runs 寫入紀錄。', + 'entries': [ + { + 'title': item.get('report_label') or item.get('report_type') or '未知簡報', + 'meta': item.get('started_at') or '時間未知', + 'detail': f"{item.get('schedule_label') or '手動'} · {item.get('target_label') or '最新資料'}", + 'status_label': item.get('status_label') or item.get('status') or '未知', + 'filename': item.get('file_name') or '', + } + for item in generation_runs[:4] + ], + }, + ] return { 'status': health_status, @@ -2363,6 +2446,7 @@ def _build_ppt_pipeline_view(files, auto_generation, audit_stats, generation_run 'latest_run': latest_run, 'latest_file': latest_file, 'stages': stages, + 'action_lanes': action_lanes, } @admin_observability_bp.route('/ppt_audit_history') @@ -2728,6 +2812,7 @@ def ppt_audit_history(): audit_stats=audit_30d_stats, generation_runs=generation_runs, vision_status=vision_status, + audit_records=audit_records, ) return render_template( diff --git a/templates/admin/ppt_audit_history.html b/templates/admin/ppt_audit_history.html index 188519b..1f4befa 100644 --- a/templates/admin/ppt_audit_history.html +++ b/templates/admin/ppt_audit_history.html @@ -153,6 +153,46 @@ {% endfor %} +
+
+
+
Action Queue
+

接下來要處理的事

+
+ 把缺漏、預覽、視覺 QA、DB 寫入集中成工作隊列 +
+
+ {% for lane in pipeline_view.action_lanes %} +
+
+ {{ lane.label }} + {{ lane.count }} +
+
+ {% for item in lane.entries %} +
+
+ {{ item.title }} + {{ item.meta }} +

{{ item.detail }}

+
+
+ {{ item.status_label }} + {% if item.filename and lane.key in ['preview', 'database'] %} + + 預覽 + + {% endif %} +
+
+ {% else %} +
{{ lane.empty_text }}
+ {% endfor %} +
+
+ {% endfor %} +
+