cormort's picture
Update app.py
082cc06 verified
import gradio as gr
import openpyxl
import os
from openpyxl.utils import get_column_letter
from copy import copy
TEMPLATE_FILE = "template.xlsx"
def process_direct_merge(file_objs, template_height):
if not file_objs:
return None, "請先上傳檔案!"
try:
template_height = int(template_height)
except:
return None, "模板高度必須是數字!"
files = file_objs if isinstance(file_objs, list) else [file_objs]
# 檔案排序邏輯 (OF- > DF- > SF- > PF-)
prefix_priority = {"OF-": 1, "DF-": 2, "SF-": 3, "PF-": 4}
def sort_key(f):
name = os.path.basename(f.name).upper()
for prefix, weight in prefix_priority.items():
if name.startswith(prefix): return (weight, name)
return (99, name)
files.sort(key=sort_key)
try:
temp_wb = openpyxl.load_workbook(TEMPLATE_FILE)
temp_ws = temp_wb.worksheets[0]
out_wb = openpyxl.Workbook()
out_ws = out_wb.active
out_ws.title = "合併報表"
# --- 頁面設置:水平方向與邊界調整 ---
# 設定為水平 (Landscape)
out_ws.page_setup.orientation = out_ws.ORIENTATION_LANDSCAPE
# 設定邊界 (單位英吋): 2cm ≈ 0.787, 1cm ≈ 0.394
out_ws.page_margins.top = 0.787 # 上 2cm
out_ws.page_margins.bottom = 0.787 # 下 2cm
out_ws.page_margins.left = 0.394 # 左 1cm
out_ws.page_margins.right = 0.394 # 右 1cm
# 強制每一頁寬度符合一頁 (避免欄位溢出)
out_ws.page_setup.fitToWidth = 1
out_ws.page_setup.fitToHeight = 0
# 1. 預先複製欄寬
for i in range(1, 31):
col_l = get_column_letter(i)
if col_l in temp_ws.column_dimensions:
out_ws.column_dimensions[col_l].width = temp_ws.column_dimensions[col_l].width
current_base_row = 1
for file_obj in files:
wb = openpyxl.load_workbook(file_obj.name, data_only=True)
file_funds_data = []
for sheet_name in wb.sheetnames:
ws = wb[sheet_name]
for row in ws.iter_rows():
first_val = str(row[0].value) if row[0].value else ""
if "彙總表" in first_val or "近10年度" in first_val:
fund_name = first_val.replace("近10年度", "").replace("收支概況彙總表", "").strip()
records = []
start_row_idx = row[0].row + 4
for r_idx in range(start_row_idx, start_row_idx + 10):
# 跳過原始檔 B 欄,讀取 A + C~U 欄
year_val = ws.cell(row=r_idx, column=1).value
data_row = [ws.cell(row=r_idx, column=c).value for c in range(3, 22)]
records.append([year_val] + data_row)
file_funds_data.append({"name": fund_name, "data": records})
# 寫入資料與樣式
for fund in file_funds_data:
for r in range(1, template_height + 1):
target_row = current_base_row + r - 1
# 同步範本列高
if r in temp_ws.row_dimensions:
out_ws.row_dimensions[target_row].height = temp_ws.row_dimensions[r].height
for c in range(1, 26):
s_cell = temp_ws.cell(row=r, column=c)
t_cell = out_ws.cell(row=target_row, column=c)
# 非數據區複製模板文字
if not (5 <= r <= 14): t_cell.value = s_cell.value
if s_cell.has_style:
t_cell.font = copy(s_cell.font)
t_cell.border = copy(s_cell.border)
t_cell.fill = copy(s_cell.fill)
t_cell.alignment = copy(s_cell.alignment)
t_cell.number_format = copy(s_cell.number_format)
# 套用合併格
if temp_ws.merged_cells:
for m_range in temp_ws.merged_cells.ranges:
if m_range.min_row <= template_height:
min_col, min_row, max_col, max_row = m_range.bounds
out_ws.merge_cells(
start_row=current_base_row + min_row - 1,
start_column=min_col,
end_row=current_base_row + max_row - 1,
end_column=max_col
)
# 填入內容 (標題與 10 年數據)
out_ws.cell(row=current_base_row, column=1).value = f"{fund['name']}近10年度收支概況彙總表"
for r_idx, row_vals in enumerate(fund['data']):
for c_idx, val in enumerate(row_vals):
out_ws.cell(row=current_base_row + 4 + r_idx, column=c_idx + 1).value = val
current_base_row += template_height
# 補空白列時同步範本列高
if len(file_funds_data) % 2 != 0:
for r in range(1, template_height + 1):
target_row = current_base_row + r - 1
if r in temp_ws.row_dimensions:
out_ws.row_dimensions[target_row].height = temp_ws.row_dimensions[r].height
current_base_row += template_height
output_path = os.path.abspath("Final_Merged_Report.xlsx")
out_wb.save(output_path)
return output_path, f"合併成功!範本高度 {template_height} 列,水平列印且邊界已設定。"
except Exception as e:
return None, f"發生錯誤: {str(e)}"
# Gradio UI
with gr.Blocks() as app:
gr.Markdown("# 基金報表合併系統 (18 列範本版)")
with gr.Row():
f_in = gr.File(label="上傳 Excel", file_count="multiple")
h_in = gr.Number(label="設定模板高度 (已設為新範本 18)", value=18)
btn = gr.Button("🚀 開始一鍵生成", variant="primary")
f_out = gr.File(label="下載結果")
status = gr.Textbox(label="狀態")
btn.click(process_direct_merge, [f_in, h_in], [f_out, status])
if __name__ == "__main__":
app.launch(server_name="0.0.0.0", server_port=7860)