This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
================================================================================
|
||||
|
||||
【已完成】
|
||||
- V10.192 補 `/observability/ppt_audit_history` 最近可預覽簡報 workbench:最新 4 份 PPT 直接在控制台下方提供線上預覽與下載,降低使用者找檔案的操作成本;完整檔案清單仍保留在下方表格。
|
||||
- V10.190 補 `/observability/ppt_audit_file/<filename>` 站內線上預覽:PPTX 由 LibreOffice 轉 PDF 快取後以 iframe 預覽,保留原始 PPTX 下載;Dockerfile 加 `libreoffice-impress`,compose 預設啟用 `PPT_VISION_ENABLED=true`,PPT 產線頁新增視覺 QA 停用原因與更精簡的控制台式排版。
|
||||
- V10.188 補強 `/observability/ppt_audit_history` PPT 視覺 QA 產線:頁面明確呈現每日、每週、每月、每季、每半年、每年定期產出節奏,並顯示 `ppt_generation_runs` DB 寫入紀錄;保留自動補齊缺漏與資料庫/檔案覆蓋狀態。
|
||||
- V10.187 修正 `/daily_sales`、`/growth_analysis` 圖表空白:Chart JSON 改從 `<template>.content.textContent` 讀取,補空資料診斷狀態;成長分析改用 realtime 明細新鮮度覆蓋過期月結摘要,並為 growth cache 加入資料指紋。
|
||||
|
||||
@@ -320,7 +320,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
|
||||
# ==========================================
|
||||
# 系統版本與路徑
|
||||
# ==========================================
|
||||
SYSTEM_VERSION = "V10.191"
|
||||
SYSTEM_VERSION = "V10.192"
|
||||
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
|
||||
public_url = PUBLIC_URL # 用於模板顯示
|
||||
|
||||
|
||||
@@ -89,6 +89,45 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
{% if files %}
|
||||
<section class="ppt-deck-workbench" aria-label="最近可預覽簡報">
|
||||
<div class="ppt-workbench-head">
|
||||
<div>
|
||||
<div class="ppt-label">Preview Workbench</div>
|
||||
<h2 class="ppt-panel-title">最近可預覽簡報</h2>
|
||||
</div>
|
||||
<small class="text-muted">最新 {{ files[:4]|length }} 份,直接線上預覽或下載原始 PPTX</small>
|
||||
</div>
|
||||
<div class="ppt-deck-rail">
|
||||
{% for f in files[:4] %}
|
||||
<article class="ppt-deck-card {% if not f.file_exists or not f.is_valid_ppt %}is-disabled{% endif %}">
|
||||
<div class="ppt-deck-meta">
|
||||
<span class="ppt-label">{{ selected_report_type.label }}</span>
|
||||
<span>{{ f.mtime }}</span>
|
||||
</div>
|
||||
<h3>{{ f.name }}</h3>
|
||||
<div class="ppt-deck-facts">
|
||||
<span>{{ f.size_kb if f.size_kb is not none else '—' }} KB</span>
|
||||
<span>{{ f.source }}</span>
|
||||
{% if f.file_exists and f.is_valid_ppt %}<span class="status-good">可預覽</span>{% else %}<span class="status-bad">需回補</span>{% endif %}
|
||||
</div>
|
||||
<div class="ppt-file-actions">
|
||||
{% if f.file_exists and f.is_valid_ppt %}
|
||||
<a class="btn btn-outline-primary btn-sm" href="{{ url_for('admin_observability.ppt_audit_file', filename=f.name) }}" target="_blank" rel="noopener">
|
||||
<i class="fas fa-eye me-1"></i>線上預覽
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if f.file_exists %}
|
||||
<a class="btn btn-outline-secondary btn-sm" href="{{ url_for('admin_observability.ppt_audit_file', filename=f.name, action='download') }}">
|
||||
<i class="fas fa-download me-1"></i>下載
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
<section class="ppt-panel mt-3"
|
||||
data-ppt-auto-generation
|
||||
data-auto-start="{{ 'true' if auto_generation.can_auto_start else 'false' }}"
|
||||
|
||||
@@ -217,6 +217,27 @@ def test_ppt_audit_history_shows_ppt_schedule_and_db_runs(client, monkeypatch):
|
||||
assert '每日日報' in html
|
||||
|
||||
|
||||
def test_ppt_audit_history_shows_recent_preview_workbench(client, monkeypatch, tmp_path):
|
||||
"""有檔案時,頁面上方要先給可預覽簡報入口。"""
|
||||
import zipfile
|
||||
|
||||
reports_dir = tmp_path / 'reports'
|
||||
reports_dir.mkdir()
|
||||
pptx = reports_dir / 'ocbot_daily_20260517.pptx'
|
||||
with zipfile.ZipFile(pptx, 'w') as zf:
|
||||
zf.writestr('[Content_Types].xml', '<Types></Types>')
|
||||
|
||||
monkeypatch.setenv('REPORTS_DIR', str(reports_dir))
|
||||
|
||||
r = client.get('/observability/ppt_audit_history?month=2026-05')
|
||||
html = r.data.decode('utf-8')
|
||||
|
||||
assert r.status_code == 200
|
||||
assert '最近可預覽簡報' in html
|
||||
assert 'ocbot_daily_20260517.pptx' in html
|
||||
assert '線上預覽' in html
|
||||
|
||||
|
||||
def test_ppt_audit_file_view_renders_online_preview(client, monkeypatch, tmp_path):
|
||||
"""PPTX view 入口應回站內預覽頁,而不是把 PPTX 直接丟給瀏覽器。"""
|
||||
import zipfile
|
||||
|
||||
@@ -123,6 +123,65 @@
|
||||
gap: var(--momo-space-1, 4px);
|
||||
}
|
||||
|
||||
.ppt-deck-workbench {
|
||||
margin-top: var(--momo-space-4, 16px);
|
||||
padding: var(--momo-space-4, 16px);
|
||||
border: 1px solid var(--obs-line);
|
||||
border-radius: var(--momo-radius-lg, 8px);
|
||||
background:
|
||||
radial-gradient(circle, rgba(45, 40, 32, 0.08) 1px, transparent 1.2px),
|
||||
rgba(255, 255, 255, 0.5);
|
||||
background-size: 10px 10px, auto;
|
||||
box-shadow: var(--momo-shadow-md, 0 16px 38px rgba(70, 46, 28, 0.08));
|
||||
}
|
||||
|
||||
.ppt-workbench-head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: var(--momo-space-3, 12px);
|
||||
margin-bottom: var(--momo-space-3, 12px);
|
||||
}
|
||||
|
||||
.ppt-deck-rail {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: var(--momo-space-3, 12px);
|
||||
}
|
||||
|
||||
.ppt-deck-card {
|
||||
display: grid;
|
||||
align-content: space-between;
|
||||
min-height: 168px;
|
||||
padding: var(--momo-space-3, 12px);
|
||||
border: 1px solid var(--obs-line);
|
||||
border-radius: var(--momo-radius-md, 6px);
|
||||
background: rgba(255, 255, 255, 0.64);
|
||||
}
|
||||
|
||||
.ppt-deck-card.is-disabled {
|
||||
opacity: 0.68;
|
||||
}
|
||||
|
||||
.ppt-deck-meta,
|
||||
.ppt-deck-facts {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--momo-space-2, 8px);
|
||||
color: var(--obs-muted);
|
||||
font-size: var(--momo-text-caption, 12px);
|
||||
}
|
||||
|
||||
.ppt-deck-card h3 {
|
||||
margin: var(--momo-space-2, 8px) 0;
|
||||
color: var(--obs-ink);
|
||||
font-size: var(--momo-text-body, 14px);
|
||||
font-weight: var(--momo-font-weight-black, 800);
|
||||
line-height: 1.35;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.ppt-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.2fr) minmax(330px, 0.8fr);
|
||||
@@ -439,6 +498,10 @@
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.ppt-deck-rail {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.ppt-auto-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
@@ -452,6 +515,7 @@
|
||||
.ppt-command,
|
||||
.ppt-auto-grid,
|
||||
.ppt-mini-grid,
|
||||
.ppt-deck-rail,
|
||||
.ppt-coverage-score,
|
||||
.ppt-coverage-list {
|
||||
grid-template-columns: 1fr;
|
||||
@@ -459,6 +523,7 @@
|
||||
|
||||
.ppt-panel-head,
|
||||
.ppt-table-title,
|
||||
.ppt-workbench-head,
|
||||
.ppt-run-log-head,
|
||||
.ppt-run-row,
|
||||
.ppt-coverage-row {
|
||||
|
||||
Reference in New Issue
Block a user