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")