feat: expose la re-extraction verifiable d'un document
POST /api/documents/{id}/re-extract retourne desormais, en plus des donnees
re-extraites, les donnees actuelles et les tags des depenses : un seul appel
suffit pour comparer avant/apres et reporter les tags. L'endpoint devient
synchrone pour que FastAPI l'execute dans un thread, l'extraction bloquant
plusieurs secondes par PDF.
PUT /api/documents/{id} enregistre la nouvelle extraction en visant le
document par son ID (save_document(replace_document_id=...)) : une extraction
qui corrige la reference ou la date met a jour le bon document au lieu d'en
creer un second, et refuse la collision avec un document voisin. Le PDF
stocke est conserve.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -242,6 +242,7 @@ async def get_document(
|
||||
"siret": document.editeur_siret,
|
||||
},
|
||||
"json_data": json.loads(document.json_data) if document.json_data else None,
|
||||
"depenses_tags": db_service.get_depenses_tags(document_id),
|
||||
"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,
|
||||
@@ -358,18 +359,78 @@ async def download_document_json(
|
||||
)
|
||||
|
||||
|
||||
@router.put("/documents/{document_id}", response_model=SaveResponse)
|
||||
async def update_document(
|
||||
document_id: int,
|
||||
request: SaveRequest,
|
||||
session: Session = Depends(get_session),
|
||||
) -> SaveResponse:
|
||||
"""Remplace les donnees d'un document existant, identifie par son ID.
|
||||
|
||||
A la difference de `POST /api/save` avec `overwrite`, qui retrouve le
|
||||
document par (reference, date), le document vise est ici designe par son ID :
|
||||
une nouvelle extraction qui corrige la reference ou la date met a jour le bon
|
||||
document au lieu d'en creer un second. Le PDF stocke est conserve.
|
||||
|
||||
- **data**: Nouvelles donnees extraites
|
||||
- **depenses_tags**: Tags a appliquer aux depenses (par index d'operation)
|
||||
- **source_file**: Nom du fichier source (optionnel, conserve si absent)
|
||||
"""
|
||||
db_service = DatabaseService(session)
|
||||
existing = db_service.get_document_by_id(document_id)
|
||||
if not existing:
|
||||
raise HTTPException(status_code=404, detail="Document non trouve")
|
||||
|
||||
try:
|
||||
document = db_service.save_document(
|
||||
data=request.data,
|
||||
source_file=request.source_file or existing.source_file,
|
||||
depenses_tags=request.depenses_tags,
|
||||
replace_document_id=document_id,
|
||||
)
|
||||
except DuplicateDocumentError as e:
|
||||
return SaveResponse(
|
||||
success=False,
|
||||
message=(
|
||||
f"Un autre document porte deja 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:
|
||||
logger.exception("Erreur lors de la mise a jour du document %s", document_id)
|
||||
raise HTTPException(
|
||||
status_code=500, detail="Erreur lors de la mise a jour du document."
|
||||
)
|
||||
|
||||
return SaveResponse(
|
||||
success=True,
|
||||
message="Document mis a jour avec succes",
|
||||
document_id=document.id,
|
||||
reference=document.reference,
|
||||
date=str(document.date),
|
||||
)
|
||||
|
||||
|
||||
@router.post("/documents/{document_id}/re-extract")
|
||||
async def re_extract_document(
|
||||
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.
|
||||
Utile pour corriger l'extraction apres amelioration des parsers, document par
|
||||
document ou lors d'un balayage de toute la base. Ne modifie pas la base :
|
||||
retourne cote a cote les donnees actuelles et les donnees re-extraites, pour
|
||||
que l'appelant compare et decide s'il enregistre (PUT /api/documents/{id}).
|
||||
|
||||
Retourne les donnees re-extraites du PDF.
|
||||
`depenses_tags` porte les tags actuels du document, pour pouvoir etre
|
||||
reportes sur la nouvelle extraction plutot que perdus au reenregistrement.
|
||||
|
||||
Endpoint synchrone (`def`) : l'extraction est bloquante et prend plusieurs
|
||||
secondes, FastAPI l'execute donc dans un thread pour ne pas figer le serveur.
|
||||
"""
|
||||
db_service = DatabaseService(session)
|
||||
document = db_service.get_document_by_id(document_id)
|
||||
@@ -405,7 +466,10 @@ async def re_extract_document(
|
||||
"document_id": document_id,
|
||||
"reference": document.reference,
|
||||
"date": str(document.date),
|
||||
"source_file": document.source_file,
|
||||
"original_json_path": document.json_path,
|
||||
"previous_data": json.loads(document.json_data) if document.json_data else None,
|
||||
"re_extracted_data": new_data,
|
||||
"depenses_tags": db_service.get_depenses_tags(document_id),
|
||||
"message": "Donnees re-extraites. Utilisez PUT /api/documents/{id} pour mettre a jour.",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user