feat: reimport pdf
This commit is contained in:
@@ -3,13 +3,15 @@
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi import APIRouter, Depends, HTTPException, File, UploadFile, Form
|
||||
from fastapi.responses import Response, JSONResponse
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import func, select
|
||||
|
||||
from ...database import get_session, DatabaseService
|
||||
from ...database import get_session, DatabaseService, storage
|
||||
from ...database.models import Document, Immeuble, Lot, Locataire, Revenu, Depense
|
||||
from ...database.service import DuplicateDocumentError
|
||||
from ...extractor import extract_compte_rendu
|
||||
from ..schemas import SaveRequest, SaveResponse, DocumentSummary
|
||||
|
||||
router = APIRouter(prefix="/api", tags=["documents"])
|
||||
@@ -64,6 +66,80 @@ async def save_document(
|
||||
)
|
||||
|
||||
|
||||
@router.post("/save-with-pdf", response_model=SaveResponse)
|
||||
async def save_document_with_pdf(
|
||||
pdf_file: UploadFile = File(..., description="Fichier PDF original"),
|
||||
data: str = Form(..., description="Donnees JSON extraites"),
|
||||
depenses_tags: str | None = Form(None, description="Tags JSON pour les depenses"),
|
||||
overwrite: bool = Form(False, description="Ecraser si existant"),
|
||||
session: Session = Depends(get_session),
|
||||
) -> SaveResponse:
|
||||
"""Sauvegarde les donnees extraites avec le fichier PDF original.
|
||||
|
||||
Cette version stocke le PDF et le JSON sur le disque pour tracabilite.
|
||||
|
||||
- **pdf_file**: Fichier PDF original (multipart)
|
||||
- **data**: Donnees JSON extraites (string JSON)
|
||||
- **depenses_tags**: Tags JSON pour les depenses (optionnel)
|
||||
- **overwrite**: Si True, ecrase le document existant
|
||||
"""
|
||||
# Parse JSON data
|
||||
try:
|
||||
parsed_data = json.loads(data)
|
||||
except json.JSONDecodeError as e:
|
||||
raise HTTPException(status_code=400, detail=f"JSON invalide pour data: {e}")
|
||||
|
||||
# Parse depenses_tags if provided
|
||||
parsed_tags = None
|
||||
if depenses_tags:
|
||||
try:
|
||||
parsed_tags = json.loads(depenses_tags)
|
||||
except json.JSONDecodeError as e:
|
||||
raise HTTPException(
|
||||
status_code=400, detail=f"JSON invalide pour depenses_tags: {e}"
|
||||
)
|
||||
|
||||
# Read PDF content
|
||||
try:
|
||||
pdf_content = await pdf_file.read()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=f"Erreur lecture du PDF: {str(e)}")
|
||||
|
||||
try:
|
||||
db_service = DatabaseService(session)
|
||||
document = db_service.save_document(
|
||||
data=parsed_data,
|
||||
source_file=pdf_file.filename,
|
||||
pdf_content=pdf_content,
|
||||
depenses_tags=parsed_tags,
|
||||
overwrite=overwrite,
|
||||
)
|
||||
|
||||
return SaveResponse(
|
||||
success=True,
|
||||
message="Document sauvegarde avec succes (PDF et JSON stockes)",
|
||||
document_id=document.id,
|
||||
reference=document.reference,
|
||||
date=str(document.date),
|
||||
)
|
||||
|
||||
except DuplicateDocumentError as e:
|
||||
return SaveResponse(
|
||||
success=False,
|
||||
message=f"Document deja existant: reference={e.reference}, date={e.date}",
|
||||
reference=e.reference,
|
||||
date=str(e.date),
|
||||
)
|
||||
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=500, detail=f"Erreur lors de la sauvegarde: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/stats")
|
||||
async def get_stats(
|
||||
session: Session = Depends(get_session),
|
||||
@@ -110,6 +186,8 @@ async def list_documents(
|
||||
solde_montant=doc.solde_montant,
|
||||
solde_type=doc.solde_type,
|
||||
created_at=doc.created_at.isoformat() if doc.created_at else None,
|
||||
has_pdf=doc.pdf_path is not None,
|
||||
has_json=doc.json_path is not None,
|
||||
)
|
||||
for doc in documents
|
||||
]
|
||||
@@ -157,6 +235,8 @@ async def get_document(
|
||||
},
|
||||
"json_data": json.loads(document.json_data) if document.json_data else None,
|
||||
"created_at": document.created_at.isoformat() if document.created_at else None,
|
||||
"has_pdf": document.pdf_path is not None,
|
||||
"has_json": document.json_path is not None,
|
||||
}
|
||||
|
||||
|
||||
@@ -189,3 +269,135 @@ async def check_duplicate(
|
||||
"reference": reference,
|
||||
"date": date,
|
||||
}
|
||||
|
||||
|
||||
@router.get("/documents/{document_id}/pdf")
|
||||
async def download_document_pdf(
|
||||
document_id: int,
|
||||
session: Session = Depends(get_session),
|
||||
) -> Response:
|
||||
"""Telecharge le fichier PDF original d'un document.
|
||||
|
||||
Retourne le fichier PDF si disponible, sinon erreur 404.
|
||||
"""
|
||||
db_service = DatabaseService(session)
|
||||
document = db_service.get_document_by_id(document_id)
|
||||
|
||||
if not document:
|
||||
raise HTTPException(status_code=404, detail="Document non trouve")
|
||||
|
||||
if not document.pdf_path:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Fichier PDF non disponible pour ce document",
|
||||
)
|
||||
|
||||
try:
|
||||
pdf_content = storage.read_pdf(document.pdf_path)
|
||||
filename = document.source_file or f"{document.reference}.pdf"
|
||||
return Response(
|
||||
content=pdf_content,
|
||||
media_type="application/pdf",
|
||||
headers={"Content-Disposition": f'inline; filename="{filename}"'},
|
||||
)
|
||||
except FileNotFoundError:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Fichier PDF introuvable sur le disque",
|
||||
)
|
||||
|
||||
|
||||
@router.get("/documents/{document_id}/json")
|
||||
async def download_document_json(
|
||||
document_id: int,
|
||||
session: Session = Depends(get_session),
|
||||
) -> Response:
|
||||
"""Telecharge le fichier JSON extrait d'un document.
|
||||
|
||||
Retourne le fichier JSON si disponible sur disque,
|
||||
sinon retourne le json_data de la base de donnees.
|
||||
"""
|
||||
db_service = DatabaseService(session)
|
||||
document = db_service.get_document_by_id(document_id)
|
||||
|
||||
if not document:
|
||||
raise HTTPException(status_code=404, detail="Document non trouve")
|
||||
|
||||
filename = f"{document.reference}_{document.date}.json"
|
||||
|
||||
# Try to read from storage first
|
||||
if document.json_path:
|
||||
try:
|
||||
json_data = storage.read_json(document.json_path)
|
||||
return JSONResponse(
|
||||
content=json_data,
|
||||
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
|
||||
)
|
||||
except FileNotFoundError:
|
||||
pass # Fall back to database
|
||||
|
||||
# Fall back to json_data in database
|
||||
if document.json_data:
|
||||
json_data = json.loads(document.json_data)
|
||||
return JSONResponse(
|
||||
content=json_data,
|
||||
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
|
||||
)
|
||||
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Donnees JSON non disponibles pour ce document",
|
||||
)
|
||||
|
||||
|
||||
@router.post("/documents/{document_id}/re-extract")
|
||||
async def re_extract_document(
|
||||
document_id: int,
|
||||
session: Session = Depends(get_session),
|
||||
) -> dict:
|
||||
"""Re-extrait les donnees depuis le PDF stocke.
|
||||
|
||||
Utile pour corriger l'extraction apres amelioration des parsers.
|
||||
Ne modifie pas automatiquement la base - retourne les nouvelles donnees
|
||||
pour validation par l'utilisateur.
|
||||
|
||||
Retourne les donnees re-extraites du PDF.
|
||||
"""
|
||||
db_service = DatabaseService(session)
|
||||
document = db_service.get_document_by_id(document_id)
|
||||
|
||||
if not document:
|
||||
raise HTTPException(status_code=404, detail="Document non trouve")
|
||||
|
||||
if not document.pdf_path:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Fichier PDF non disponible pour ce document - re-extraction impossible",
|
||||
)
|
||||
|
||||
# Get absolute path
|
||||
pdf_full_path = storage.get_absolute_path(document.pdf_path)
|
||||
|
||||
if not pdf_full_path.exists():
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Fichier PDF introuvable sur le disque",
|
||||
)
|
||||
|
||||
# Re-extract
|
||||
try:
|
||||
new_data = extract_compte_rendu(str(pdf_full_path))
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=422,
|
||||
detail=f"Erreur lors de la re-extraction: {str(e)}",
|
||||
)
|
||||
|
||||
return {
|
||||
"document_id": document_id,
|
||||
"reference": document.reference,
|
||||
"date": str(document.date),
|
||||
"original_json_path": document.json_path,
|
||||
"re_extracted_data": new_data,
|
||||
"message": "Donnees re-extraites. Utilisez PUT /api/documents/{id} pour mettre a jour.",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user