eg_convert_file / app.py
titipata's picture
Upload app.py
356905a verified
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Excel Column Remover and Signature Adder
A simple Gradio app that removes first 2 columns and adds signature lines to Excel files.
"""
import gradio as gr
import pandas as pd
import tempfile
import os
from datetime import datetime
def process_excel_file(input_file):
"""
Process Excel file by removing first 2 columns and adding signature lines
Args:
input_file: Gradio file input object
Returns:
tuple: (output_file_path, success_message, preview_data)
"""
try:
# Create output filename
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_filename = f"processed_report_{timestamp}.xlsx"
output_path = os.path.join(tempfile.gettempdir(), output_filename)
# Read Excel file
df = pd.read_excel(input_file.name, header=None)
print(f"Original shape: {df.shape}")
# Extract first 4 header rows and clean them (remove extra spaces)
header_rows = []
for i in range(min(4, len(df))):
row_data = df.iloc[i].dropna().astype(str).tolist()
if row_data:
# Take first non-empty cell and clean it
header_text = str(row_data[0]).strip()
header_rows.append([header_text])
else:
header_rows.append([''])
# Remove first 2 columns from all data (including headers)
df_processed = df.iloc[:, 2:] # Keep from column 2 onwards
print(f"After removing first 2 columns: {df_processed.shape}")
# Reset column indices
df_processed = df_processed.reset_index(drop=True)
df_processed.columns = range(len(df_processed.columns))
# Create the final structure:
# 1. First 4 header rows (cleaned)
# 2. Original data from column 3 onwards
# 3. Signature lines
final_data = []
# Add cleaned header rows
for header in header_rows:
final_data.append(header)
# Add empty separator row
final_data.append([])
# Add the rest of the data (starting from row 5 in original, which is row 0 after removing first 4)
data_start_row = 4 # Skip first 4 header rows we already added
for i in range(data_start_row, len(df_processed)):
row_data = df_processed.iloc[i].tolist()
final_data.append(row_data)
# Add signature lines at the end
signature_lines = [
[], # Empty row
[], # Empty row
[], # Empty row
["กรรมการ", "", "กรรมการ", "", "กรรมการ"],
[], # Empty row
[], # Empty row
["ผู้ได้รับมอบหมายให้รับเงินและเอกสารแทนตัวเงินตามรายละเอียดข้างต้น ไปแล้ว"],
[], # Empty row
[], # Empty row
[], # Empty row
["ลงชื่อ.................................................(ผู้รับเงิน)"],
[], # Empty row
[], # Empty row
["ลงชื่อ.................................................(หัวหน้าหน่วยงานย่อยผู้มอบหมาย)"]
]
# Add signature lines
for signature_line in signature_lines:
final_data.append(signature_line)
# Create final dataframe
final_df = pd.DataFrame(final_data)
# Make sure all columns are filled
max_cols = max([len(row) for row in final_data if row])
final_df = final_df.reindex(columns=range(max_cols))
# Fill NaN values with empty strings
final_df = final_df.fillna('')
print(f"Final shape: {final_df.shape}")
# Save to Excel
final_df.to_excel(output_path, index=False, header=False, engine='openpyxl')
# Create preview (first 20 rows)
preview_df = final_df.head(20).copy()
# Add string column names for Gradio
preview_df.columns = [f'คอลัมน์_{i+1}' for i in range(len(preview_df.columns))]
# Convert to strings for display
for col in preview_df.columns:
preview_df[col] = preview_df[col].astype(str).replace('nan', '').replace('', '-')
success_message = f"""
✅ **ประมวลผลเสร็จสิ้น!**
📊 **สรุป:**
- รูปแบบต้นฉบับ: {df.shape[0]} แถว × {df.shape[1]} คอลัมน์
- หลังตัดคอลัมน์: {df_processed.shape[0]} แถว × {df_processed.shape[1]} คอลัมน์
- เพิ่มบรรทัดลงนาม: {len(signature_lines)} บรรทัด
- รูปแบบสุดท้าย: {final_df.shape[0]} แถว × {final_df.shape[1]} คอลัมน์
📥 **ดาวน์โหลดไฟล์ที่ประมวลผลแล้ว**
"""
return output_path, success_message, preview_df
except Exception as e:
error_message = f"""
❌ **เกิดข้อผิดพลาด:**
{str(e)}
**กรุณาตรวจสอบ:**
- ไฟล์ Excel (.xls หรือ .xlsx) ที่ถูกต้อง
- มีข้อมูลในไฟล์
- ไฟล์ไม่เสียหาย
"""
return None, error_message, pd.DataFrame({'ข้อผิดพลาด': ['ไม่สามารถประมวลผลไฟล์ได้']})
def create_gradio_interface():
"""Create the Gradio interface"""
with gr.Blocks(title="Excel Column Processor", theme=gr.themes.Soft()) as iface:
gr.Markdown("""
# 📊 เครื่องมือประมวลผลไฟล์ Excel
ตัดคอลัมน์แรก 2 คอลัมน์ และเพิ่มบรรทัดลงนามท้ายไฟล์
**วิธีใช้:**
1. อัปโหลดไฟล์ Excel
2. กดปุ่ม "ประมวลผลไฟล์"
3. ดาวน์โหลดไฟล์ผลลัพธ์
""")
with gr.Row():
with gr.Column():
gr.Markdown("""
**การทำงาน:**
- ตัดคอลัมน์ที่ 1 และ 2 ออก
- เลื่อนข้อมูลมาเริ่มที่คอลัมน์ A
- เพิ่มบรรทัดลงนาม (กรรมการ, ผู้รับเงิน, หัวหน้า)
- ส่งออกเป็น Excel ใหม่
""")
with gr.Row():
with gr.Column():
file_input = gr.File(
label="📁 อัปโหลดไฟล์ Excel",
file_types=[".xls", ".xlsx"],
file_count="single"
)
process_btn = gr.Button(
"🔄 ประมวลผลไฟล์",
variant="primary",
size="lg"
)
with gr.Row():
with gr.Column():
status_output = gr.Markdown(
label="📊 สถานะ",
value="👆 อัปโหลดไฟล์ Excel และกดประมวลผล"
)
with gr.Row():
with gr.Column():
file_output = gr.File(
label="💾 ดาวน์โหลดไฟล์ผลลัพธ์",
visible=False
)
with gr.Row():
with gr.Column():
preview_output = gr.Dataframe(
label="👀 ตัวอย่าง (20 แถวแรก)",
visible=False,
wrap=True
)
def handle_processing(file):
if file is None:
return (
gr.update(visible=False),
"⚠️ **กรุณาอัปโหลดไฟล์ Excel ก่อน**",
gr.update(visible=False)
)
result_file, message, preview = process_excel_file(file)
if result_file:
return (
gr.update(value=result_file, visible=True),
message,
gr.update(value=preview, visible=True)
)
else:
return (
gr.update(visible=False),
message,
gr.update(value=preview, visible=True)
)
process_btn.click(
fn=handle_processing,
inputs=[file_input],
outputs=[file_output, status_output, preview_output]
)
gr.Markdown("""
**รายละเอียด:**
- รองรับ Excel (.xls, .xlsx)
- ตัดคอลัมน์แรก 2 คอลัมน์อัตโนมัติ
- เพิ่มช่องลงนามสำหรับกรรมการ ผู้รับเงิน และหัวหน้า
- เหมาะสำหรับรายงานการเงินและเอกสารทางการ
💡 **เคล็ดลับ:** เครื่องมือนี้จะลบคอลัมน์ว่างด้านซ้ายและจัดรูปแบบให้พร้อมสำหรับการลงนาม
""")
return iface
# Create and launch the app
if __name__ == "__main__":
app = create_gradio_interface()
app.launch(
server_name="0.0.0.0",
server_port=7860,
share=False
)