Modelo Cash Flow
Volver a Documentación general
Documentación de la Integración OCR en el Backend (Python + FastAPI)
Este documento describe cómo se integra un módulo OCR (Python) dentro de un backend desarrollado en Python usando FastAPI y siguiendo principios de arquitectura hexagonal. El objetivo es detectar de manera automática la categoría, el precio, la fecha y la prioridad de compras en facturas o recibos.
Tabla de Contenidos
- Introducción
- Objetivos del Módulo OCR
- Requisitos y Tecnologías
- Arquitectura e Integración
- Variables de Entorno
- Detección de Datos (Categoría, Precio, Fecha, Prioridad)
- Limitaciones y Posibles Mejoras
- Conclusiones
Introducción
En Cashflow, el backend en Python se encarga de procesar, registrar y gestionar los movimientos financieros. Para agilizar la creación de movimientos provenientes de facturas o recibos físicos, se ha desarrollado/integrado un módulo de OCR que extrae automáticamente la información relevante (precio, categoría, fecha, prioridad).
El backend usa FastAPI para exponer endpoints REST y sigue una arquitectura hexagonal (puertos y adaptadores), lo que facilita su mantenibilidad y escalabilidad. El OCR se integra como un adaptador de salida que consume la lógica principal del dominio para almacenar sus resultados.
Objetivos del Módulo OCR
- Automatizar la lectura de datos clave en facturas (precio, fecha, categoría, prioridad).
- Reducir errores de transcripción y ahorrar tiempo al usuario.
- Mantener una arquitectura limpia: El OCR se implementa como un servicio externo o un submódulo que cumple un puerto (interfaz) definido en el dominio.
- Mejorar la analítica posterior de movimientos financieros gracias a la detección de prioridad y categoría.
Requisitos y Tecnologías
- Python 3.8+: Lenguaje principal.
- FastAPI: Framework para crear y gestionar las rutas y lógica de negocio.
- Arquitectura Hexagonal: Separación de la lógica de negocio (dominio) de los adaptadores de entrada (endpoints) y de salida (OCR, base de datos, etc.).
- OCR:
- Puede ser Tesseract, Pytesseract, OpenCV o un modelo personalizado (PyTorch, TensorFlow, etc.).
- Base de Datos: PostgreSQL (u otra) para almacenar la información extraída.
Arquitectura e Integración
Arquitectura Hexagonal en FastAPI
1
2
3
4
5
6
7
8
Domino (Reglas de negocio, casos de uso)
┌───────────┐
│ Puertos │ <--- Contratos de entrada/salida
└───────────┘
↑ ↓
Adaptadores:
- Entrada (FastAPI Controllers, CLI, etc.)
- Salida (BD, OCR, servicios externos)
- Dominio: Contiene las entidades y lógica asociada a la creación de movimientos.
- Puerto de Entrada: Interfaz definida para registrar un nuevo movimiento (p.ej.,
CreateInvoiceUseCase
). - Puerto de Salida: Interfaz que el dominio invoca para ejecutar la detección OCR (
OCRPort
). - Adaptadores: Concretan la conexión con FastAPI y la herramienta OCR real.
Flujo de Subida de Imágenes
sequenceDiagram
actor U as Usuario
participant F as Frontend
participant API as FastAPI (Adaptador de Entrada)
participant OCR as Módulo OCR (Adaptador de Salida)
participant D as Dominio
participant DB as Base de Datos
U->>F: Sube imagen de la factura
F->>API: POST /invoices/upload (multipart/form-data)
API->>D: Ejecuta caso de uso CreateInvoice (pasa ruta de imagen)
D->>OCR: Invoca método de puerto de salida (detectData(image))
OCR->>OCR: Procesa imagen (fecha, precio, categoría, prioridad)
OCR-->>D: Retorna resultados de OCR
D->>DB: Guarda datos en la entidad Invoice/Movement
DB-->>D: Confirma registro
D-->>API: Devuelve datos de la factura registrada
API-->>F: Respuesta 201 Created (con datos extraídos)
- El usuario sube una imagen desde el frontend.
- FastAPI recibe el archivo y llama a la lógica del dominio.
- Dominio invoca un puerto de salida para solicitar la detección OCR.
- OCR procesa la imagen y retorna la información extraída.
- El dominio guarda la información en la base de datos a través de su repositorio.
- El backend responde con un código
201
y los datos registrados.
Endpoints y Puertos de Entrada/Salida
Ejemplo de endpoint en FastAPI (adaptador de entrada)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# app/adapters/entrypoints/fastapi_routes.py
from fastapi import APIRouter, File, UploadFile, Depends
from app.domain.usecases.create_invoice import CreateInvoiceUseCase
router = APIRouter()
@router.post("/invoices/upload")
async def upload_invoice(
file: UploadFile = File(...),
use_case: CreateInvoiceUseCase = Depends()
):
# Se guarda el archivo en una carpeta temporal, luego se llama al caso de uso
temp_path = f"/tmp/{file.filename}"
with open(temp_path, "wb") as f:
f.write(await file.read())
invoice_data = use_case.execute(temp_path)
return invoice_data
Puerto de Salida (OCRPort)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# app/domain/ports/ocr_port.py
from abc import ABC, abstractmethod
class OCRPort(ABC):
@abstractmethod
def detect_data(self, image_path: str) -> dict:
"""
Retorna un dict con campos:
{
'category': str,
'price': float,
'date': datetime/date,
'priority': str
}
"""
pass
Adaptador OCR
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
# app/adapters/out/ocr_adapter.py
import pytesseract
from app.domain.ports.ocr_port import OCRPort
from PIL import Image
import re
class PyTesseractOCRAdapter(OCRPort):
def detect_data(self, image_path: str) -> dict:
# Conversión a texto
text = pytesseract.image_to_string(Image.open(image_path))
# Ejemplo simplificado de extracción
price = self._extract_price(text)
date = self._extract_date(text)
category = self._extract_category(text)
priority = self._infer_priority(category, price)
return {
'category': category,
'price': price,
'date': date,
'priority': priority
}
def _extract_price(self, text: str) -> float:
# Regex básico de ejemplo
match = re.search(r"(\d+(\.\d{1,2}))", text)
return float(match.group(1)) if match else 0.0
def _extract_date(self, text: str):
# Extrae fecha con regex sencillo
# ...
return "2025-01-19"
def _extract_category(self, text: str) -> str:
# Busca palabras clave para deducir categoría
# ...
return "Comida"
def _infer_priority(self, category: str, price: float) -> str:
# Ejemplo básico
if price > 100:
return "Alta"
return "Media"
Variables de Entorno
En el proyecto se pueden definir variables de entorno para personalizar la integración OCR:
1
2
3
4
5
# .env
OCR_ENGINE=pytesseract
OCR_LANG=spa
OCR_CONFIDENCE_THRESHOLD=0.7
DB_URL=postgresql://user:password@host:5432/cashflow_db
- OCR_ENGINE: Selecciona el motor OCR (pytesseract, un contenedor con Tesseract, etc.).
- OCR_LANG: Configura el idioma principal (ej.
spa
para español). - OCR_CONFIDENCE_THRESHOLD: Umbral mínimo de confianza para la detección.
- DB_URL: URL de conexión a la base de datos.
Detección de Datos (Categoría, Precio, Fecha, Prioridad)
- Categoría:
- Heurísticas basadas en palabras clave en la factura (p.e. “grocery”, “food”, “restaurant” →
Comida
). - O un modelo ML entrenado para clasificar categorías (ej. BERT, TF-IDF, etc.).
- Heurísticas basadas en palabras clave en la factura (p.e. “grocery”, “food”, “restaurant” →
- Precio:
- Regex o heurística para capturar valores monetarios con formato
XX.XX
. - Manejo de símbolos monetarios o separadores de miles.
- Regex o heurística para capturar valores monetarios con formato
- Fecha:
- Patrones de fecha (
DD/MM/YYYY
,YYYY-MM-DD
). - Ajustes regionales (día/mes invertidos).
- Patrones de fecha (
- Prioridad:
- Reglas de negocio: monto elevado => “Alta”; ciertos tipos de gastos => “Esencial”.
- Opcionalmente, la categoría determina la prioridad (p.e., “Salud” => “Alta”, “Entretenimiento” => “Baja”).
Limitaciones y Posibles Mejoras
- Calidad de las Imágenes: OCR depende de la nitidez y el formato de la factura. Imágenes borrosas reducen la exactitud.
- Formato y Legibilidad: Facturas con diseños complicados pueden requerir segmentación previa o un modelo de IA más robusto.
- Clasificación de Categoría: Un sistema de palabras clave sencillo puede errar con nuevas categorías. Incluir un método de aprendizaje continuo mejoraría la precisión.
- Internacionalización: Para distintos idiomas o monedas, se requiere mayor configuración (ej. regex flexibles o un modelo entrenado multilenguaje).
- Procesamiento en Paralelo: Para grandes volúmenes, considerar workers asíncronos (celery, RQ, etc.) que procesen en segundo plano.
Conclusiones
La integración de OCR en el backend Python + FastAPI (con arquitectura hexagonal) permite:
- Automatizar la creación de movimientos financieros a partir de imágenes.
- Mantener un diseño escalable y limpio, donde el OCR es un adaptador de salida.
- Centralizar la lógica de negocio en el dominio, agnóstico de la implementación específica del OCR.
Para un sistema más completo, conviene:
- Añadir un pipeline de preprocesamiento de la imagen (desenfoque, rotación, etc.).
- Entrenar o mejorar los algoritmos de clasificación de categoría y detección de fecha.
- Implementar un sistema de colas para procesar las imágenes de manera asíncrona.
Con esta arquitectura y definición se sientan las bases para un módulo OCR robusto, facilitando el registro y clasificación de sus facturas de manera rápida y efectiva.
Volver a Documentación general