api-rmarkdown / app.py
manuelcaccone's picture
Upload folder using huggingface_hub
978295b verified
Raw
History Blame Contribute Delete
8.89 kB
from fastapi import FastAPI, HTTPException, BackgroundTasks
from fastapi.responses import HTMLResponse, FileResponse
from pydantic import BaseModel
from pathlib import Path
import uuid
import shutil
import subprocess
from validator import validate_rmd, render_rmd_format
app = FastAPI(
title="RMD Validator API",
description="API per renderizzare file R Markdown in HTML",
version="2.0.0"
)
class RmdContent(BaseModel):
content: str
filename: str = "document.rmd"
@app.get("/")
async def root():
return {
"status": "online",
"service": "RMD Validator API",
"version": "2.0.0"
}
@app.get("/health")
async def health():
return {
"status": "healthy",
"r_available": True,
"packages_loaded": True
}
@app.post("/validate")
async def validate_rmd_endpoint(rmd_data: RmdContent):
request_id = str(uuid.uuid4())
temp_dir = Path(f"/app/temp/{request_id}")
temp_dir.mkdir(parents=True, exist_ok=True)
try:
if not rmd_data.filename.endswith('.rmd'):
rmd_data.filename += '.rmd'
rmd_file = temp_dir / rmd_data.filename
with open(rmd_file, 'w', encoding='utf-8') as f:
f.write(rmd_data.content)
status, message = validate_rmd(str(rmd_file))
response = {
"status": status,
"message": message,
"filename": rmd_data.filename,
"request_id": request_id
}
if status == 200:
base_name = rmd_file.stem
generated_files = []
for ext in (".html", ".tex"):
out = temp_dir / f"{base_name}{ext}"
if out.exists():
generated_files.append(f"{base_name}{ext}")
response["files"] = generated_files
response["output_directory"] = str(temp_dir)
else:
error_file = temp_dir / "404"
if error_file.exists():
with open(error_file, 'r', encoding='utf-8') as f:
response["error_details"] = f.read()
return response
except FileNotFoundError as e:
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
raise HTTPException(status_code=500, detail=f"Errore interno: {str(e)}")
@app.post("/render", response_class=HTMLResponse)
async def render_rmd_endpoint(rmd_data: RmdContent):
"""Renderizza Rmd e restituisce direttamente l'HTML."""
request_id = str(uuid.uuid4())
temp_dir = Path(f"/app/temp/{request_id}")
temp_dir.mkdir(parents=True, exist_ok=True)
try:
if not rmd_data.filename.endswith('.rmd'):
rmd_data.filename += '.rmd'
rmd_file = temp_dir / rmd_data.filename
with open(rmd_file, 'w', encoding='utf-8') as f:
f.write(rmd_data.content)
status, message = validate_rmd(str(rmd_file))
if status != 200:
raise HTTPException(status_code=422, detail=message)
html_file = temp_dir / f"{rmd_file.stem}.html"
if not html_file.exists():
raise HTTPException(status_code=500, detail="HTML non generato")
html_content = html_file.read_text(encoding='utf-8')
return HTMLResponse(content=html_content)
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=f"Errore interno: {str(e)}")
finally:
shutil.rmtree(temp_dir, ignore_errors=True)
@app.post("/validate-and-cleanup")
async def validate_rmd_and_cleanup(rmd_data: RmdContent):
request_id = str(uuid.uuid4())
temp_dir = Path(f"/app/temp/{request_id}")
temp_dir.mkdir(parents=True, exist_ok=True)
try:
if not rmd_data.filename.endswith('.rmd'):
rmd_data.filename += '.rmd'
rmd_file = temp_dir / rmd_data.filename
with open(rmd_file, 'w', encoding='utf-8') as f:
f.write(rmd_data.content)
status, message = validate_rmd(str(rmd_file))
response = {
"status": status,
"message": message,
"filename": rmd_data.filename,
"request_id": request_id
}
if status != 200:
error_file = temp_dir / "404"
if error_file.exists():
with open(error_file, 'r', encoding='utf-8') as f:
response["error_details"] = f.read()
return response
except FileNotFoundError as e:
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
raise HTTPException(status_code=500, detail=f"Errore interno: {str(e)}")
finally:
shutil.rmtree(temp_dir, ignore_errors=True)
def _prepare_rmd(rmd_data, temp_dir):
if not rmd_data.filename.endswith('.rmd'):
rmd_data.filename += '.rmd'
rmd_file = temp_dir / rmd_data.filename
rmd_file.write_text(rmd_data.content, encoding='utf-8')
return rmd_file
@app.post("/download/html")
async def download_html(rmd_data: RmdContent, background_tasks: BackgroundTasks):
request_id = str(uuid.uuid4())
temp_dir = Path(f"/app/temp/{request_id}")
temp_dir.mkdir(parents=True, exist_ok=True)
try:
rmd_file = _prepare_rmd(rmd_data, temp_dir)
render_rmd_format(str(rmd_file), 'html_document')
html_file = temp_dir / f"{rmd_file.stem}.html"
if not html_file.exists():
raise HTTPException(status_code=500, detail="HTML non generato")
background_tasks.add_task(shutil.rmtree, temp_dir, True)
return FileResponse(
path=str(html_file),
filename=f"{rmd_file.stem}.html",
media_type="text/html",
)
except subprocess.CalledProcessError as e:
shutil.rmtree(temp_dir, ignore_errors=True)
raise HTTPException(status_code=422, detail=f"Errore rendering: {e.stderr}")
except HTTPException:
raise
except Exception as e:
shutil.rmtree(temp_dir, ignore_errors=True)
raise HTTPException(status_code=500, detail=str(e))
@app.post("/download/pdf")
async def download_pdf(rmd_data: RmdContent, background_tasks: BackgroundTasks):
request_id = str(uuid.uuid4())
temp_dir = Path(f"/app/temp/{request_id}")
temp_dir.mkdir(parents=True, exist_ok=True)
try:
rmd_file = _prepare_rmd(rmd_data, temp_dir)
render_rmd_format(str(rmd_file), 'pdf_document')
pdf_file = temp_dir / f"{rmd_file.stem}.pdf"
if not pdf_file.exists():
raise HTTPException(status_code=500, detail="PDF non generato")
background_tasks.add_task(shutil.rmtree, temp_dir, True)
return FileResponse(
path=str(pdf_file),
filename=f"{rmd_file.stem}.pdf",
media_type="application/pdf",
)
except subprocess.CalledProcessError as e:
shutil.rmtree(temp_dir, ignore_errors=True)
raise HTTPException(status_code=422, detail=f"Errore rendering: {e.stderr}")
except HTTPException:
raise
except Exception as e:
shutil.rmtree(temp_dir, ignore_errors=True)
raise HTTPException(status_code=500, detail=str(e))
@app.post("/download/tex")
async def download_tex(rmd_data: RmdContent, background_tasks: BackgroundTasks):
request_id = str(uuid.uuid4())
temp_dir = Path(f"/app/temp/{request_id}")
temp_dir.mkdir(parents=True, exist_ok=True)
try:
rmd_file = _prepare_rmd(rmd_data, temp_dir)
render_rmd_format(str(rmd_file), 'latex_document')
tex_file = temp_dir / f"{rmd_file.stem}.tex"
if not tex_file.exists():
raise HTTPException(status_code=500, detail="LaTeX non generato")
background_tasks.add_task(shutil.rmtree, temp_dir, True)
return FileResponse(
path=str(tex_file),
filename=f"{rmd_file.stem}.tex",
media_type="application/x-tex",
)
except subprocess.CalledProcessError as e:
shutil.rmtree(temp_dir, ignore_errors=True)
raise HTTPException(status_code=422, detail=f"Errore rendering: {e.stderr}")
except HTTPException:
raise
except Exception as e:
shutil.rmtree(temp_dir, ignore_errors=True)
raise HTTPException(status_code=500, detail=str(e))
@app.delete("/cleanup/{request_id}")
async def cleanup_request(request_id: str):
temp_dir = Path(f"/app/temp/{request_id}")
if not temp_dir.exists():
raise HTTPException(status_code=404, detail="Request ID non trovato")
try:
shutil.rmtree(temp_dir)
return {"status": "success", "message": f"File per request {request_id} eliminati"}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Errore durante la pulizia: {str(e)}")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=7860)