ordinary-qcm / app.py
TiH0's picture
Update app.py
6f2a3be verified
from robyn import Robyn, Request, Response, jsonify, ALLOW_CORS
import os
import tempfile
import asyncio
from concurrent.futures import ProcessPoolExecutor
import traceback
from ord import process_excel_to_word
# At the top, after imports
from functools import partial
# Increase worker pool
executor = ProcessPoolExecutor(max_workers=os.cpu_count() or 4)
# In convert_excel function, the process_file_sync is already good
# But you can add batching if multiple files come in
app = Robyn(__file__)
ALLOW_CORS(app, origins=["*"])
# Create process pool for CPU-intensive tasks
executor = ProcessPoolExecutor(max_workers=4)
def process_file_sync(input_path, output_path, display_name, use_two_columns,
add_separator, balance_method, theme_hex):
"""Synchronous wrapper for process_excel_to_word"""
process_excel_to_word(
excel_file_path=input_path,
output_word_path=output_path,
display_name=display_name,
use_two_columns=use_two_columns,
add_separator_line=add_separator,
balance_method=balance_method,
theme_hex=theme_hex
)
return output_path
@app.get("/")
def index(request: Request):
"""Health check endpoint - synchronous is fine for simple JSON"""
return jsonify({
"status": "online",
"message": "Excel to Word Converter API",
"version": "1.0.0",
"endpoints": {
"/convert": "POST - Convert Excel to Word",
"/health": "GET - Health check"
}
})
@app.get("/health")
def health(request: Request):
"""Health check for HuggingFace"""
return jsonify({"status": "healthy", "service": "excel-to-word-converter"})
@app.post("/convert")
async def convert_excel(request: Request):
"""Convert Excel to Word document - async for I/O operations"""
input_path = None
output_path = None
try:
# Get the uploaded file
files = request.files
if not files:
return Response(
status_code=400,
headers={"Content-Type": "application/json"},
description=jsonify({"error": "No file uploaded"})
)
# Get file
uploaded_file = files.get('file') or files[list(files.keys())[0]]
# Extract file content
if isinstance(uploaded_file, bytes):
file_content = uploaded_file
elif hasattr(uploaded_file, 'content'):
file_content = uploaded_file.content
elif hasattr(uploaded_file, 'read'):
file_content = uploaded_file.read()
elif hasattr(uploaded_file, 'file'):
file_content = uploaded_file.file.read()
else:
file_content = uploaded_file
# Get parameters
form_data = request.form_data or {}
display_name = form_data.get('display_name')
use_two_columns = form_data.get('use_two_columns', 'true').lower() == 'true'
add_separator = form_data.get('add_separator', 'true').lower() == 'true'
balance_method = form_data.get('balance_method', 'dynamic')
theme_hex = form_data.get('theme_hex', '5FFFDF')
# Create temp files using asyncio for non-blocking I/O
loop = asyncio.get_event_loop()
# Write input file asynchronously
input_path = tempfile.mktemp(suffix='.xlsx')
await loop.run_in_executor(None, lambda: open(input_path, 'wb').write(file_content))
output_path = tempfile.mktemp(suffix='.docx')
# Run the CPU-intensive conversion in a separate process
await loop.run_in_executor(
executor,
process_file_sync,
input_path,
output_path,
display_name,
use_two_columns,
add_separator,
balance_method,
theme_hex
)
# Read output file asynchronously
docx_content = await loop.run_in_executor(
None,
lambda: open(output_path, 'rb').read()
)
# Clean up temp files
await loop.run_in_executor(None, lambda: os.unlink(input_path))
await loop.run_in_executor(None, lambda: os.unlink(output_path))
return Response(
status_code=200,
headers={
"Content-Type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"Content-Disposition": "attachment; filename=converted_document.docx",
},
description=docx_content
)
except Exception as e:
error_trace = traceback.format_exc()
print(f"Error: {error_trace}")
# Clean up on error
if input_path and os.path.exists(input_path):
os.unlink(input_path)
if output_path and os.path.exists(output_path):
os.unlink(output_path)
return Response(
status_code=500,
headers={"Content-Type": "application/json"},
description=jsonify({
"error": str(e),
"trace": error_trace
})
)
if __name__ == "__main__":
port = int(os.getenv("PORT", 7860))
app.start(host="0.0.0.0", port=port)