129 lines
4.7 KiB
Python
129 lines
4.7 KiB
Python
import re
|
|
import pandas as pd
|
|
import os
|
|
from datetime import datetime, timedelta
|
|
from PyPDF2 import PdfReader
|
|
from collections import defaultdict
|
|
import numpy as np
|
|
|
|
# Carpeta con tus tickets PDF
|
|
ticket_folder = "tickets"
|
|
|
|
def extract_data_from_pdf(file_path):
|
|
reader = PdfReader(file_path)
|
|
text = ""
|
|
for page in reader.pages:
|
|
text += page.extract_text() + "\n"
|
|
|
|
date_match = re.search(r"(\d{2}/\d{2}/\d{4})", text)
|
|
fecha = datetime.strptime(date_match.group(1), "%d/%m/%Y") if date_match else None
|
|
|
|
products = []
|
|
for line in text.splitlines():
|
|
match = re.match(r"\d*\s?(.*?)\s+(\d+,\d{2})\s+(\d+,\d{2})", line)
|
|
if match:
|
|
nombre = match.group(1).strip().upper()
|
|
unit_price = float(match.group(2).replace(",", "."))
|
|
total_price = float(match.group(3).replace(",", "."))
|
|
products.append((fecha, nombre, unit_price, total_price))
|
|
else:
|
|
match_simple = re.match(r"(.*?)\s+(\d+,\d{2})$", line)
|
|
if match_simple:
|
|
nombre = match_simple.group(1).strip().upper()
|
|
total_price = float(match_simple.group(2).replace(",", "."))
|
|
products.append((fecha, nombre, None, total_price))
|
|
|
|
return products
|
|
|
|
# Recolectar todos los productos de todos los tickets
|
|
datos = []
|
|
for file in os.listdir(ticket_folder):
|
|
if file.endswith(".pdf"):
|
|
path = os.path.join(ticket_folder, file)
|
|
datos.extend(extract_data_from_pdf(path))
|
|
|
|
# Crear DataFrame
|
|
columnas = ["fecha", "producto", "precio_unitario", "precio_total"]
|
|
df = pd.DataFrame(datos, columns=columnas)
|
|
df.dropna(subset=["fecha"], inplace=True)
|
|
|
|
# Normalizar nombres de producto
|
|
df["producto"] = df["producto"].str.upper().str.strip()
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Cálculo de frecuencia con estacionalidad real por meses del año
|
|
# ---------------------------------------------------------------------------
|
|
hoy = datetime.now()
|
|
|
|
def _meses_activos(meses_compra):
|
|
activos = set()
|
|
for m in meses_compra:
|
|
activos.add(m)
|
|
activos.add((m % 12) + 1)
|
|
activos.add(((m - 2) % 12) + 1)
|
|
return activos
|
|
|
|
def _proximo_mes_activo(mes_hoy, meses_activos):
|
|
for i in range(1, 13):
|
|
if ((mes_hoy - 1 + i) % 12) + 1 in meses_activos:
|
|
return i
|
|
return 0
|
|
|
|
def calcular_frecuencia_estacional(grupo):
|
|
fechas = grupo['fecha'].sort_values().dropna()
|
|
if len(fechas) < 1:
|
|
return pd.Series({'diferencia_dias': float('nan'), 'proxima_compra_estimado': pd.NaT})
|
|
|
|
meses_compra = set(int(m) for m in fechas.dt.month.unique())
|
|
es_estacional = len(meses_compra) <= 5
|
|
|
|
gaps = fechas.diff().dt.days.dropna()
|
|
gaps_validos = gaps[(gaps > 0) & (gaps <= 90)]
|
|
if len(gaps_validos) >= 1:
|
|
freq = float(gaps_validos.mean())
|
|
elif len(gaps[gaps > 0]) >= 1:
|
|
freq = float(gaps[gaps > 0].mean())
|
|
else:
|
|
return pd.Series({'diferencia_dias': float('nan'), 'proxima_compra_estimado': pd.NaT})
|
|
|
|
ultima = fechas.max()
|
|
proxima = ultima + timedelta(days=freq)
|
|
|
|
if es_estacional:
|
|
activos = _meses_activos(meses_compra)
|
|
if hoy.month not in activos:
|
|
avance = _proximo_mes_activo(hoy.month, activos)
|
|
mes_inicio = ((hoy.month - 1 + avance) % 12) + 1
|
|
anio = hoy.year if mes_inicio > hoy.month else hoy.year + 1
|
|
dia = min(int(ultima.day), 28)
|
|
try:
|
|
proxima = datetime(anio, mes_inicio, dia)
|
|
except ValueError:
|
|
proxima = datetime(anio, mes_inicio, 1)
|
|
|
|
return pd.Series({'diferencia_dias': freq, 'proxima_compra_estimado': pd.Timestamp(proxima)})
|
|
|
|
frecuencia_compra = (
|
|
df.groupby("producto", group_keys=False)
|
|
.apply(calcular_frecuencia_estacional)
|
|
.reset_index()
|
|
)
|
|
|
|
# Ahora seleccionamos los productos que más frecuentemente compras
|
|
# y predecimos cuándo volverás a comprar basándonos en la frecuencia.
|
|
productos_estimados = frecuencia_compra.dropna(subset=["diferencia_dias"]).sort_values("diferencia_dias", ascending=True)
|
|
|
|
# Listar la compra estimada
|
|
productos_estimados["producto"] = productos_estimados["producto"].str.title() # Capitalizar el nombre del producto
|
|
productos_estimados["fecha_estimada_proxima_compra"] = productos_estimados["proxima_compra_estimado"].dt.strftime("%d/%m/%Y")
|
|
|
|
# Guardar la lista estimada de la compra
|
|
productos_estimados.to_csv("lista_compra_estimado.csv", index=False)
|
|
|
|
# Mostrar los primeros resultados
|
|
print("\n✅ Lista de la compra estimada para la próxima compra:")
|
|
print(productos_estimados[["producto", "diferencia_dias", "fecha_estimada_proxima_compra"]])
|
|
|
|
print("\n✅ Archivos generados:")
|
|
print("- lista_compra_estimado.csv")
|