feat: Excel template export with meeting number auto-generation
- Add meeting_number field (M-YYYYMMDD-XX format) with auto-generation - Refactor Excel export to use cell coordinates instead of placeholders - Export files saved to backend/record/ directory with meeting number filename - Add database migration for meeting_number column - Add start.sh script for managing frontend/backend/sidecar services - Update OpenSpec documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
119
backend/tests/test_excel_export.py
Normal file
119
backend/tests/test_excel_export.py
Normal file
@@ -0,0 +1,119 @@
|
||||
"""Test Excel export functionality with template filling."""
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime, date
|
||||
from openpyxl import load_workbook
|
||||
|
||||
# Add parent directory to path
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from app.routers.export import fill_template_workbook, create_default_workbook, TEMPLATE_DIR, RECORD_DIR
|
||||
|
||||
|
||||
def test_excel_export():
|
||||
"""Test Excel generation with mock data."""
|
||||
|
||||
# Mock meeting data
|
||||
meeting = {
|
||||
"meeting_id": 1,
|
||||
"uuid": "test-uuid-123",
|
||||
"meeting_number": "M-20251211-01",
|
||||
"subject": "專案進度討論會議",
|
||||
"meeting_time": datetime(2025, 12, 11, 14, 30),
|
||||
"location": "會議室A",
|
||||
"chairperson": "王經理",
|
||||
"recorder": "李小明",
|
||||
"attendees": "張三, 李四, 王五",
|
||||
"created_by": "test@example.com",
|
||||
}
|
||||
|
||||
# Mock conclusions
|
||||
conclusions = [
|
||||
{"conclusion_id": 1, "content": "確認專案時程延後兩週", "system_code": "C-20251211-01"},
|
||||
{"conclusion_id": 2, "content": "同意增加測試人力", "system_code": "C-20251211-02"},
|
||||
{"conclusion_id": 3, "content": "下次會議改為線上進行", "system_code": "C-20251211-03"},
|
||||
]
|
||||
|
||||
# Mock action items
|
||||
actions = [
|
||||
{
|
||||
"action_id": 1,
|
||||
"content": "更新專案時程表",
|
||||
"owner": "張三",
|
||||
"due_date": date(2025, 12, 15),
|
||||
"status": "Open",
|
||||
"system_code": "A-20251211-01",
|
||||
},
|
||||
{
|
||||
"action_id": 2,
|
||||
"content": "聯繫人資部門增聘測試人員",
|
||||
"owner": "李四",
|
||||
"due_date": date(2025, 12, 20),
|
||||
"status": "In Progress",
|
||||
"system_code": "A-20251211-02",
|
||||
},
|
||||
{
|
||||
"action_id": 3,
|
||||
"content": "準備線上會議設備",
|
||||
"owner": "王五",
|
||||
"due_date": None,
|
||||
"status": "Open",
|
||||
"system_code": "A-20251211-03",
|
||||
},
|
||||
]
|
||||
|
||||
# Check paths
|
||||
template_path = os.path.join(TEMPLATE_DIR, "meeting_template.xlsx")
|
||||
print(f"Template directory: {TEMPLATE_DIR}")
|
||||
print(f"Record directory: {RECORD_DIR}")
|
||||
print(f"Template exists: {os.path.exists(template_path)}")
|
||||
|
||||
# Ensure record directory exists
|
||||
os.makedirs(RECORD_DIR, exist_ok=True)
|
||||
|
||||
# Generate filename with meeting number
|
||||
meeting_number = meeting.get("meeting_number", "")
|
||||
filename = f"{meeting_number}.xlsx" if meeting_number else f"meeting_{meeting['uuid']}.xlsx"
|
||||
output_path = os.path.join(RECORD_DIR, filename)
|
||||
|
||||
# Test with template if exists
|
||||
if os.path.exists(template_path):
|
||||
print("\n=== Testing with template ===")
|
||||
wb = load_workbook(template_path)
|
||||
wb = fill_template_workbook(wb, meeting, conclusions, actions)
|
||||
wb.save(output_path)
|
||||
print(f"Saved to: {output_path}")
|
||||
|
||||
# Verify cell values
|
||||
ws = wb.active
|
||||
print("\n--- Verification ---")
|
||||
print(f"D3 (Subject): {ws['D3'].value}")
|
||||
print(f"D4 (Time): {ws['D4'].value}")
|
||||
print(f"D5 (Chair): {ws['D5'].value}")
|
||||
print(f"F4 (Location): {ws['F4'].value}")
|
||||
print(f"F5 (Recorder): {ws['F5'].value}")
|
||||
print(f"D6 (Attendees): {ws['D6'].value}")
|
||||
print(f"C8 (Meeting Number): {ws['C8'].value}")
|
||||
print(f"D8 (Conclusions): {ws['D8'].value}")
|
||||
print(f"\nAction Items:")
|
||||
for i in range(3):
|
||||
row = 10 + i
|
||||
print(f" Row {row}: C={ws[f'C{row}'].value}, D={ws[f'D{row}'].value}, F={ws[f'F{row}'].value}, G={ws[f'G{row}'].value}, H={ws[f'H{row}'].value}")
|
||||
else:
|
||||
print("\n=== Template not found, using default generation ===")
|
||||
wb = create_default_workbook(meeting, conclusions, actions)
|
||||
wb.save(output_path)
|
||||
print(f"Saved to: {output_path}")
|
||||
|
||||
print(f"\n✅ Test completed! File saved to: {output_path}")
|
||||
|
||||
# List files in record directory
|
||||
print(f"\n--- Files in record directory ---")
|
||||
for f in os.listdir(RECORD_DIR):
|
||||
print(f" {f}")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_excel_export()
|
||||
Reference in New Issue
Block a user