autocompra/generar_lista.py

120 lines
4.2 KiB
Python

"""
generar_lista.py
Genera datos.js con predicciones de compra limpias a partir de lista_compra_estimado.csv
Uso: python generar_lista.py
"""
import pandas as pd
import json
import re
from datetime import datetime
# ---------------------------------------------------------------------------
# Filtros de basura (líneas del ticket que no son productos)
# ---------------------------------------------------------------------------
BAD_PATTERNS = [
r'^\s*%',
r'tarjeta',
r'^total(\s|$|\s*\()',
r'€/kg',
r'\d+,\d+\s*kg',
r'^n\.c',
r'^aut[\s\d]',
r'verificado',
r'visa',
r'bancaria',
r'devoluciones',
r'mastercard',
r'importe',
r'^iva\s',
r'^cuota\s',
r'^\d+,\d+\s*€', # líneas de precio suelto
r'^\d{1,3}\s*€', # líneas tipo "15 €"
]
def es_basura(nombre):
n = nombre.lower().strip()
if not n or len(n) < 3:
return True
return any(re.search(p, n) for p in BAD_PATTERNS)
def limpiar_nombre(nombre):
nombre = nombre.strip()
# Quitar el "1" pegado al inicio del nombre que viene de la cantidad en el ticket
# "1Yogur Limon" → "Yogur Limon" | "1+Prot Pud" → "+Prot Pud"
# Pero "16 Huevos Camperos" → "16 Huevos Camperos" (es un pack de 16 huevos)
nombre = re.sub(r'^1([A-ZÁÉÍÓÚÑ+a-záéíóúñ])', r'\1', nombre)
return nombre.strip()
# ---------------------------------------------------------------------------
# Cargar datos
# ---------------------------------------------------------------------------
try:
# Intentar UTF-8 primero, si falla usar cp1252 (Windows)
try:
df = pd.read_csv('lista_compra_estimado.csv', encoding='utf-8')
except UnicodeDecodeError:
df = pd.read_csv('lista_compra_estimado.csv', encoding='cp1252')
except FileNotFoundError:
print("❌ No se encontró lista_compra_estimado.csv")
print(" Ejecuta primero: python autocompra5.py (o autocompra7.py)")
exit(1)
print(f" Productos en CSV: {len(df)}")
# Filtrar basura
df = df[~df['producto'].apply(es_basura)].copy()
print(f" Después de filtrar basura: {len(df)}")
# Limpiar nombres
df['nombre'] = df['producto'].apply(limpiar_nombre)
# Parsear fechas estimadas
df['fecha'] = pd.to_datetime(df['fecha_estimada_proxima_compra'], format='%d/%m/%Y', errors='coerce')
df = df.dropna(subset=['fecha', 'diferencia_dias'])
# Excluir productos sin frecuencia real (frecuencia = 0 significa comprado una sola vez)
df = df[df['diferencia_dias'] > 0]
# Eliminar duplicados (mismo nombre → quedarse con el de menor frecuencia, más fiable)
df = df.sort_values('diferencia_dias')
df = df.drop_duplicates(subset='nombre', keep='first')
# Calcular días hasta la próxima compra desde HOY
hoy = pd.Timestamp(datetime.now().date())
df['dias_hasta'] = (df['fecha'] - hoy).dt.days
# Ordenar: primero los más frecuentes (más urgentes a recoger), luego por fecha estimada
df = df.sort_values(['diferencia_dias', 'dias_hasta'])
# ---------------------------------------------------------------------------
# Construir JSON de salida
# ---------------------------------------------------------------------------
resultado = []
for _, row in df.iterrows():
resultado.append({
'producto': row['nombre'],
'fecha_estimada': row['fecha_estimada_proxima_compra'],
'dias_hasta': int(row['dias_hasta']),
'frecuencia_dias': round(float(row['diferencia_dias']), 1),
})
# Escribir datos.js (cargable como <script src> sin servidor HTTP)
ts = datetime.now().strftime('%d/%m/%Y %H:%M')
with open('datos.js', 'w', encoding='utf-8') as f:
f.write('// Generado el ' + ts + ' - ' + str(len(resultado)) + ' productos\n')
f.write('const GENERADO = "' + ts + '";\n')
f.write('const predicciones = ')
json.dump(resultado, f, ensure_ascii=False, indent=2)
f.write(';\n')
# Escribir datos.json (usado por la app Flask)
datos_json = {
'generado': ts,
'total': len(resultado),
'predicciones': resultado,
}
with open('datos.json', 'w', encoding='utf-8') as f:
json.dump(datos_json, f, ensure_ascii=False, indent=2)
print(f"{len(resultado)} predicciones escritas en datos.js y datos.json")
print(f" Abre index.html en el navegador o arranca app.py para ver la lista.")