Spaces:
Running
Running
File size: 9,032 Bytes
4604cbe 751bbae 5ac2c89 6133ae1 751bbae 4604cbe 5ac2c89 536f43e 5ac2c89 b8f1c3f 6133ae1 431a9d7 b8f1c3f 4604cbe 431a9d7 6133ae1 4604cbe b8f1c3f 5ac2c89 66cd178 751bbae 6133ae1 4604cbe b8f1c3f 4604cbe b8f1c3f 4604cbe b8f1c3f 399fd9f 4604cbe 399fd9f 4604cbe 399fd9f 6133ae1 399fd9f 4604cbe 399fd9f b8f1c3f 4604cbe 751bbae 431a9d7 4604cbe 09a0354 4604cbe 431a9d7 4604cbe 5ac2c89 4604cbe b8f1c3f 4604cbe 09a0354 4604cbe 09a0354 4604cbe 5ac2c89 399fd9f 5ac2c89 751bbae 4604cbe 751bbae 4604cbe 431a9d7 4604cbe 431a9d7 4604cbe 431a9d7 4604cbe 5ac2c89 4604cbe 751bbae 4604cbe 536f43e 5ac2c89 4604cbe 536f43e 4604cbe 431a9d7 5ac2c89 4604cbe 09a0354 431a9d7 4604cbe 536f43e 4604cbe b8f1c3f 4604cbe b8f1c3f 4604cbe b8f1c3f 399fd9f 4604cbe 399fd9f b8f1c3f 4604cbe b8f1c3f 399fd9f b8f1c3f |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
from fastapi import FastAPI, UploadFile, File, HTTPException
from fastapi.responses import HTMLResponse, Response
from fastapi.staticfiles import StaticFiles
from pathlib import Path
import os
from backend.utils import extract_text_from_file, translate_text
import io
from docx import Document
import asyncio
from typing import Optional
import logging
from datetime import datetime
app = FastAPI(
title="DocTranslator Pro",
description="Service avancé de traduction de documents avec gestion améliorée des erreurs",
version="2.0.0"
)
# Configuration améliorée
MAX_FILE_SIZE = 15 * 1024 * 1024 # 15 Mo (augmenté de 10 Mo)
MAX_TEXT_LENGTH = 75000 # 75k caractères (augmenté de 50k)
PROCESSING_TIMEOUT = 600 # 10 minutes (augmenté de 5 minutes)
SUPPORTED_FORMATS = ('.pdf', '.docx', '.xlsx', '.pptx') # Ajout de PPTX
LOG_FILE = "translation_logs.log"
# Configuration des fichiers statiques
app.mount("/static", StaticFiles(directory="frontend"), name="static")
# Configuration du logging avancé
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(LOG_FILE),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
class TranslationLogger:
@staticmethod
def log_transaction(filename: str, char_count: int, status: str):
with open(LOG_FILE, "a") as f:
f.write(f"{datetime.now().isoformat()}|{filename}|{char_count}|{status}\n")
@app.on_event("startup")
async def startup_event():
"""Vérification améliorée du modèle au démarrage"""
from backend.utils import translator
if translator is None:
logger.error("❌ ERREUR CRITIQUE: Le modèle de traduction n'a pas pu être chargé!")
raise RuntimeError("Échec du chargement du modèle")
else:
logger.info("✅ Modèle de traduction initialisé avec succès")
logger.info(f"⚙️ Configuration - Taille max fichier: {MAX_FILE_SIZE/1024/1024}MB")
logger.info(f"⚙️ Configuration - Texte max: {MAX_TEXT_LENGTH} caractères")
@app.get("/", response_class=HTMLResponse)
async def serve_frontend():
"""Endpoint amélioré pour servir l'interface frontend"""
try:
with open(os.path.join("frontend", "index.html"), "r", encoding="utf-8") as f:
return HTMLResponse(content=f.read(), status_code=200)
except Exception as e:
logger.error(f"Erreur de chargement du frontend: {str(e)}")
raise HTTPException(
status_code=500,
detail="Erreur de chargement de l'interface. Contactez l'administrateur."
)
async def process_file(file: UploadFile, target_lang: str) -> dict:
"""
Fonction de traitement améliorée avec gestion d'erreur granulaire
"""
# Vérification de la taille
if file.size > MAX_FILE_SIZE:
error_msg = f"Fichier trop volumineux (> {MAX_FILE_SIZE/1024/1024:.1f} Mo)"
logger.warning(error_msg)
raise HTTPException(status_code=413, detail=error_msg)
# Vérification du type de fichier
if not file.filename.lower().endswith(SUPPORTED_FORMATS):
error_msg = f"Format non supporté. Formats acceptés: {', '.join(SUPPORTED_FORMATS)}"
logger.warning(error_msg)
raise HTTPException(status_code=415, detail=error_msg)
# Lecture du contenu avec gestion d'erreur
try:
contents = await file.read()
except Exception as e:
error_msg = f"Erreur de lecture du fichier: {str(e)}"
logger.error(error_msg)
raise HTTPException(status_code=422, detail=error_msg)
# Extraction du texte
extracted_text = extract_text_from_file(contents, file.filename)
if extracted_text.startswith("Erreur:"):
error_msg = extracted_text.replace("Erreur:", "").strip()
logger.error(f"Erreur extraction: {error_msg}")
raise HTTPException(status_code=422, detail=error_msg)
if len(extracted_text) > MAX_TEXT_LENGTH:
error_msg = f"Texte trop long ({len(extracted_text)} > {MAX_TEXT_LENGTH} caractères)"
logger.warning(error_msg)
raise HTTPException(status_code=413, detail=error_msg)
# Traduction avec suivi
logger.info(f"Début traduction - {len(extracted_text)} caractères")
translated_text = translate_text(extracted_text, target_lang)
if translated_text.startswith("Erreur:"):
error_detail = translated_text.replace("Erreur:", "").strip()
TranslationLogger.log_transaction(file.filename, len(extracted_text), "failed")
logger.error(f"Échec traduction: {error_detail}")
raise HTTPException(status_code=500, detail=error_detail)
# Succès
TranslationLogger.log_transaction(file.filename, len(extracted_text), "success")
logger.info(f"Traduction réussie - {len(extracted_text)}→{len(translated_text)} caractères")
return {
"filename": file.filename,
"original_text": extracted_text,
"translated_text": translated_text,
"char_count": len(extracted_text),
"translation_ratio": f"{len(translated_text)/len(extracted_text):.2f}" if extracted_text else "0"
}
@app.post("/api/translate")
async def translate_endpoint(
file: UploadFile = File(...),
target_lang: str = "en"
):
"""
Endpoint amélioré avec gestion complète des erreurs
"""
try:
# Traitement avec timeout configurable
result = await asyncio.wait_for(
process_file(file, target_lang),
timeout=PROCESSING_TIMEOUT
)
return {
"status": "success",
"message": "Traduction terminée avec succès",
"processing_time": f"{PROCESSING_TIMEOUT}s max",
**result
}
except asyncio.TimeoutError:
error_msg = "Temps de traitement dépassé. Veuillez réduire la taille du document."
logger.error(error_msg)
raise HTTPException(status_code=408, detail=error_msg)
except HTTPException:
raise # Already properly formatted
except Exception as e:
error_msg = f"Erreur inattendue: {str(e)}"
logger.critical(error_msg)
raise HTTPException(status_code=500, detail=error_msg)
@app.post("/api/download_translated")
async def download_translated(
file: UploadFile = File(...),
target_lang: str = "en"
):
"""
Endpoint de téléchargement amélioré avec gestion d'erreur
"""
try:
# Traitement avec timeout
result = await asyncio.wait_for(
process_file(file, target_lang),
timeout=PROCESSING_TIMEOUT
)
# Génération du DOCX avec gestion d'erreur
try:
doc = Document()
doc.add_paragraph(result["translated_text"])
file_stream = io.BytesIO()
doc.save(file_stream)
file_stream.seek(0)
return Response(
content=file_stream.getvalue(),
media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
headers={
"Content-Disposition": f"attachment; filename=TRAD_{result['filename']}",
"X-File-Size": str(len(file_stream.getvalue())),
"X-Char-Count": str(result["char_count"]),
"X-Translation-Ratio": result["translation_ratio"]
}
)
except Exception as e:
error_msg = f"Erreur de génération DOCX: {str(e)}"
logger.error(error_msg)
raise HTTPException(status_code=500, detail=error_msg)
except asyncio.TimeoutError:
error_msg = "Temps de génération dépassé. Document trop volumineux."
logger.error(error_msg)
raise HTTPException(status_code=408, detail=error_msg)
except HTTPException:
raise
except Exception as e:
error_msg = f"Erreur de téléchargement: {str(e)}"
logger.error(error_msg)
raise HTTPException(status_code=500, detail=error_msg)
@app.get("/api/health")
async def health_check():
"""Endpoint de vérification de santé amélioré"""
from backend.utils import translator
return {
"status": "OK" if translator else "ERROR",
"model_loaded": bool(translator),
"max_file_size": f"{MAX_FILE_SIZE/1024/1024:.1f} MB",
"max_text_length": f"{MAX_TEXT_LENGTH} caractères",
"supported_formats": SUPPORTED_FORMATS,
"log_file": LOG_FILE
}
@app.exception_handler(404)
async def not_found_handler(request, exc):
"""Gestionnaire d'erreur 404 amélioré"""
logger.warning(f"404 - {request.url}")
return HTMLResponse(
content="""
<div style='text-align:center; padding:2rem'>
<h1>404 - Ressource non trouvée</h1>
<p>La page que vous cherchez n'existe pas ou a été déplacée</p>
<a href="/">Retour à l'accueil</a>
</div>
""",
status_code=404
) |