120 lines
4.2 KiB
Python
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.")
|