feat: add analytics
This commit is contained in:
191
src/plesna_gerance/api/routes/documents.py
Normal file
191
src/plesna_gerance/api/routes/documents.py
Normal file
@@ -0,0 +1,191 @@
|
||||
"""Documents routes - CRUD operations for documents."""
|
||||
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import func, select
|
||||
|
||||
from ...database import get_session, DatabaseService
|
||||
from ...database.models import Document, Immeuble, Lot, Locataire, Revenu, Depense
|
||||
from ...database.service import DuplicateDocumentError
|
||||
from ..schemas import SaveRequest, SaveResponse, DocumentSummary
|
||||
|
||||
router = APIRouter(prefix="/api", tags=["documents"])
|
||||
|
||||
|
||||
@router.post("/save", response_model=SaveResponse)
|
||||
async def save_document(
|
||||
request: SaveRequest,
|
||||
session: Session = Depends(get_session),
|
||||
) -> SaveResponse:
|
||||
"""Sauvegarde les donnees extraites en base de donnees.
|
||||
|
||||
- **source_file**: Nom du fichier PDF original (optionnel)
|
||||
- **data**: Donnees extraites (format identique a la reponse de /api/extract)
|
||||
- **depenses_tags**: Liste des tags par index de depense (optionnel)
|
||||
- **overwrite**: Si True, ecrase le document existant (optionnel)
|
||||
|
||||
Retourne un message de succes avec l'ID du document cree,
|
||||
ou une erreur si le document existe deja (doublon).
|
||||
"""
|
||||
try:
|
||||
db_service = DatabaseService(session)
|
||||
document = db_service.save_document(
|
||||
data=request.data,
|
||||
source_file=request.source_file,
|
||||
depenses_tags=request.depenses_tags,
|
||||
overwrite=request.overwrite,
|
||||
)
|
||||
|
||||
return SaveResponse(
|
||||
success=True,
|
||||
message="Document sauvegarde avec succes",
|
||||
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),
|
||||
) -> dict:
|
||||
"""Retourne les statistiques globales de la base de donnees.
|
||||
|
||||
Compteurs pour chaque table principale.
|
||||
"""
|
||||
return {
|
||||
"documents": session.execute(select(func.count(Document.id))).scalar() or 0,
|
||||
"immeubles": session.execute(select(func.count(Immeuble.id))).scalar() or 0,
|
||||
"lots": session.execute(select(func.count(Lot.id))).scalar() or 0,
|
||||
"locataires": session.execute(select(func.count(Locataire.id))).scalar() or 0,
|
||||
"revenus": session.execute(select(func.count(Revenu.id))).scalar() or 0,
|
||||
"depenses": session.execute(select(func.count(Depense.id))).scalar() or 0,
|
||||
}
|
||||
|
||||
|
||||
@router.get("/documents", response_model=list[DocumentSummary])
|
||||
async def list_documents(
|
||||
limit: int = 100,
|
||||
offset: int = 0,
|
||||
session: Session = Depends(get_session),
|
||||
) -> list[DocumentSummary]:
|
||||
"""Liste les documents importes en base.
|
||||
|
||||
- **limit**: Nombre maximum de documents a retourner (defaut: 100)
|
||||
- **offset**: Decalage pour la pagination (defaut: 0)
|
||||
|
||||
Retourne une liste de documents avec leurs metadonnees principales.
|
||||
"""
|
||||
db_service = DatabaseService(session)
|
||||
documents = db_service.list_documents(limit=limit, offset=offset)
|
||||
|
||||
return [
|
||||
DocumentSummary(
|
||||
id=doc.id,
|
||||
reference=doc.reference,
|
||||
date=str(doc.date),
|
||||
type=doc.type,
|
||||
source_file=doc.source_file,
|
||||
immeuble_code=doc.immeuble.code if doc.immeuble else None,
|
||||
immeuble_adresse=doc.immeuble.adresse if doc.immeuble else None,
|
||||
solde_montant=doc.solde_montant,
|
||||
solde_type=doc.solde_type,
|
||||
created_at=doc.created_at.isoformat() if doc.created_at else None,
|
||||
)
|
||||
for doc in documents
|
||||
]
|
||||
|
||||
|
||||
@router.get("/documents/{document_id}")
|
||||
async def get_document(
|
||||
document_id: int,
|
||||
session: Session = Depends(get_session),
|
||||
) -> dict:
|
||||
"""Recupere un document par son ID avec toutes ses donnees.
|
||||
|
||||
Retourne le document complet incluant le JSON original.
|
||||
"""
|
||||
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")
|
||||
|
||||
return {
|
||||
"id": document.id,
|
||||
"reference": document.reference,
|
||||
"date": str(document.date),
|
||||
"type": document.type,
|
||||
"source_file": document.source_file,
|
||||
"immeuble": {
|
||||
"code": document.immeuble.code,
|
||||
"adresse": document.immeuble.adresse,
|
||||
"ville": document.immeuble.ville,
|
||||
"code_postal": document.immeuble.code_postal,
|
||||
}
|
||||
if document.immeuble
|
||||
else None,
|
||||
"solde": {
|
||||
"montant": document.solde_montant,
|
||||
"type": document.solde_type,
|
||||
"date_arrete": str(document.solde_date_arrete)
|
||||
if document.solde_date_arrete
|
||||
else None,
|
||||
},
|
||||
"editeur": {
|
||||
"nom": document.editeur_nom,
|
||||
"siret": document.editeur_siret,
|
||||
},
|
||||
"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,
|
||||
}
|
||||
|
||||
|
||||
@router.get("/check-duplicate")
|
||||
async def check_duplicate(
|
||||
reference: str,
|
||||
date: str,
|
||||
session: Session = Depends(get_session),
|
||||
) -> dict:
|
||||
"""Verifie si un document existe deja en base.
|
||||
|
||||
- **reference**: Reference du document
|
||||
- **date**: Date du document (format YYYY-MM-DD)
|
||||
|
||||
Retourne {exists: true/false, document_id: ...}
|
||||
"""
|
||||
try:
|
||||
doc_date = datetime.strptime(date, "%Y-%m-%d").date()
|
||||
except ValueError:
|
||||
raise HTTPException(
|
||||
status_code=400, detail="Format de date invalide. Utiliser YYYY-MM-DD"
|
||||
)
|
||||
|
||||
db_service = DatabaseService(session)
|
||||
existing = db_service.check_duplicate(reference, doc_date)
|
||||
|
||||
return {
|
||||
"exists": existing is not None,
|
||||
"document_id": existing.id if existing else None,
|
||||
"reference": reference,
|
||||
"date": date,
|
||||
}
|
||||
Reference in New Issue
Block a user