Spaces:
Running
Running
| from fastapi import FastAPI, File, UploadFile, Form, HTTPException | |
| from fastapi.responses import FileResponse, HTMLResponse | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi import BackgroundTasks | |
| from fastapi.responses import FileResponse | |
| import os | |
| import tempfile | |
| import re | |
| from pathlib import Path | |
| # Import your conversion function | |
| from statis import process_excel_to_word | |
| app = FastAPI(title="QCM Converter API") | |
| # Enable CORS for all origins (you can restrict this in production) | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| def validate_hex_color(color: str) -> bool: | |
| """Validate hex color format""" | |
| pattern = r'^[0-9A-Fa-f]{6}$' | |
| return bool(re.match(pattern, color)) | |
| async def root(): | |
| """Serve the HTML interface""" | |
| html_path = Path(__file__).parent / "index.html" | |
| if html_path.exists(): | |
| return html_path.read_text() | |
| return """ | |
| <html> | |
| <body> | |
| <h1>QCM Converter API</h1> | |
| <p>Upload your Excel files at <a href="/docs">/docs</a></p> | |
| </body> | |
| </html> | |
| """ | |
| async def convert_file( | |
| background_tasks: BackgroundTasks, | |
| file: UploadFile = File(...), | |
| use_two_columns: bool = Form(True), | |
| add_separator_line: bool = Form(True), | |
| theme_color: str = Form("5FFFDF") | |
| ): | |
| """ | |
| Convert Excel QCM file to Word document | |
| Parameters: | |
| - file: Excel file (.xlsx) | |
| - use_two_columns: Use two-column layout | |
| - add_separator_line: Add separator line between columns | |
| - theme_color: Hex color code (without #) e.g., "5FFFDF" | |
| """ | |
| # Validate file extension | |
| if not file.filename.endswith('.xlsx'): | |
| raise HTTPException(status_code=400, detail="Only .xlsx files are supported") | |
| # Validate color | |
| if not validate_hex_color(theme_color): | |
| raise HTTPException( | |
| status_code=400, | |
| detail="Invalid color format. Use 6-character hex code (e.g., '5FFFDF')" | |
| ) | |
| with tempfile.NamedTemporaryFile(delete=False, suffix='.xlsx') as temp_input: | |
| content = await file.read() | |
| temp_input.write(content) | |
| temp_input_path = temp_input.name | |
| output_filename = file.filename.replace('.xlsx', '_converted.docx') | |
| temp_output_path = tempfile.mktemp(suffix='.docx') | |
| try: | |
| process_excel_to_word( | |
| excel_file_path=temp_input_path, | |
| output_word_path=temp_output_path, | |
| theme_hex=theme_color | |
| ) | |
| # ✅ Schedule cleanup as a background task | |
| background_tasks.add_task(cleanup_files, temp_input_path, temp_output_path) | |
| return FileResponse( | |
| temp_output_path, | |
| media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document", | |
| filename=output_filename, | |
| background=None # <-- No lambda here | |
| ) | |
| except Exception as e: | |
| cleanup_files(temp_input_path, temp_output_path) | |
| raise HTTPException(status_code=500, detail=f"Conversion failed: {str(e)}") | |
| def cleanup_files(*file_paths): | |
| """Clean up temporary files""" | |
| for file_path in file_paths: | |
| try: | |
| if os.path.exists(file_path): | |
| os.unlink(file_path) | |
| except Exception as e: | |
| print(f"Error cleaning up {file_path}: {e}") | |
| async def health_check(): | |
| """Health check endpoint""" | |
| return {"status": "healthy", "message": "QCM Converter API is running"} | |
| if __name__ == "__main__": | |
| import uvicorn | |
| uvicorn.run(app, host="0.0.0.0", port=7860) |