feat: add analytics

This commit is contained in:
2026-01-19 21:40:10 +01:00
parent 0fd6bdaeb3
commit aac4d194e3
19 changed files with 1940 additions and 347 deletions

View File

@@ -0,0 +1,178 @@
"""Pydantic models for API requests and responses."""
from datetime import date
from typing import Any
from pydantic import BaseModel
# ============================================================
# Requests
# ============================================================
class SaveRequest(BaseModel):
"""Request body for saving extracted data."""
source_file: str | None = None
data: dict[str, Any]
depenses_tags: list[dict] | None = None
overwrite: bool = False
class PredictTagsRequest(BaseModel):
"""Request body for predicting tags."""
depenses: list[dict]
# ============================================================
# Responses - Documents
# ============================================================
class SaveResponse(BaseModel):
"""Response for save operation."""
success: bool
message: str
document_id: int | None = None
reference: str | None = None
date: str | None = None
class DocumentSummary(BaseModel):
"""Summary of a saved document."""
id: int
reference: str
date: str
type: str | None
source_file: str | None
immeuble_code: str | None
immeuble_adresse: str | None
solde_montant: float | None
solde_type: str | None
created_at: str
# ============================================================
# Responses - Analytics
# ============================================================
class ImmeubleResponse(BaseModel):
"""Response for immeuble listing."""
id: int
code: str
adresse: str | None
ville: str | None
code_postal: str | None
nb_lots: int = 0
nb_depenses: int = 0
class LotResponse(BaseModel):
"""Response for lot listing."""
id: int
numero: str
type: str | None
immeuble_id: int
immeuble_code: str | None
class TagResponse(BaseModel):
"""Response for tag listing."""
id: int
nom: str
nb_depenses: int = 0
class FournisseurResponse(BaseModel):
"""Response for fournisseur listing."""
nom: str
nb_depenses: int
total_debit: float
class DepenseDetail(BaseModel):
"""Detailed depense for analytics."""
id: int
document_id: int
document_date: date | None
document_reference: str | None
immeuble_id: int
immeuble_code: str | None
immeuble_adresse: str | None
lot_id: int | None
lot_numero: str | None
tag_id: int | None
tag_nom: str | None
categorie: str | None
sous_categorie: str | None
fournisseur: str | None
description: str | None
debit: float
credit: float
tva: float
locatif: float
deductible: float
class CategorySummary(BaseModel):
"""Summary for a category."""
categorie: str
count: int
total_debit: float
total_credit: float
total_tva: float
total_locatif: float
total_deductible: float
class TagSummary(BaseModel):
"""Summary for a tag."""
tag_id: int | None
tag_nom: str | None
count: int
total_debit: float
class MonthlySummary(BaseModel):
"""Summary for a month."""
year: int
month: int
count: int
total_debit: float
total_credit: float
class FournisseurSummary(BaseModel):
"""Summary for a fournisseur."""
fournisseur: str | None
count: int
total_debit: float
class DepensesSummary(BaseModel):
"""Aggregated summary of depenses for analytics."""
total_count: int
total_debit: float
total_credit: float
total_tva: float
total_locatif: float
total_deductible: float
by_category: list[CategorySummary]
by_tag: list[TagSummary]
by_month: list[MonthlySummary]
by_fournisseur: list[FournisseurSummary]