fix: parser PDF formato Mercadona sin espacio qty-nombre + productos por peso
This commit is contained in:
parent
2e1b35553a
commit
02b055a96b
47
app.py
47
app.py
|
|
@ -71,14 +71,37 @@ _OCR_EXCLUDE = [
|
||||||
def parsear_texto_ticket(texto):
|
def parsear_texto_ticket(texto):
|
||||||
"""Extrae productos de texto OCR con el mismo formato que los tickets Mercadona."""
|
"""Extrae productos de texto OCR con el mismo formato que los tickets Mercadona."""
|
||||||
productos = []
|
productos = []
|
||||||
for line in texto.splitlines():
|
lines = texto.splitlines()
|
||||||
linea = line.strip()
|
i = 0
|
||||||
if not linea:
|
while i < len(lines):
|
||||||
|
linea = lines[i].strip()
|
||||||
|
if not linea or any(kw in linea.upper() for kw in _OCR_EXCLUDE):
|
||||||
|
i += 1
|
||||||
continue
|
continue
|
||||||
if any(kw in linea.upper() for kw in _OCR_EXCLUDE):
|
|
||||||
continue
|
# Producto por peso (2 líneas): "1BROCOLI" + "1,048 kg 2,60 €/kg 2,72"
|
||||||
# "2 ROLLO HOGAR DOBLE 2,35 4,70"
|
if i + 1 < len(lines):
|
||||||
m = re.match(r"^(\d+)\s+(.+?)\s+(\d+[,\.]\d{2})\s+(\d+[,\.]\d{2})$", linea)
|
next_line = lines[i + 1].strip()
|
||||||
|
weight_m = re.search(r"kg.*?(\d+[,.]\d{2})\s*$", next_line)
|
||||||
|
if weight_m and re.match(r"^\d+[A-ZÁÉÍÓÚÑa-záéíóúñ+]", linea):
|
||||||
|
name_m = re.match(r"^(\d+)(.+)$", linea)
|
||||||
|
if name_m:
|
||||||
|
try:
|
||||||
|
cantidad = int(name_m.group(1))
|
||||||
|
precio_total = round(float(weight_m.group(1).replace(",", ".")), 2)
|
||||||
|
productos.append({
|
||||||
|
"cantidad": cantidad,
|
||||||
|
"producto": name_m.group(2).strip().upper(),
|
||||||
|
"precio_unitario": round(precio_total / cantidad, 2),
|
||||||
|
"precio_total": precio_total,
|
||||||
|
})
|
||||||
|
i += 2
|
||||||
|
continue
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# "2ARROZ SOS 1,88 3,76"
|
||||||
|
m = re.match(r"^(\d+)(.+?)\s+(\d+[,\.]\d{2})\s+(\d+[,\.]\d{2})$", linea)
|
||||||
if m:
|
if m:
|
||||||
try:
|
try:
|
||||||
productos.append({
|
productos.append({
|
||||||
|
|
@ -87,11 +110,13 @@ def parsear_texto_ticket(texto):
|
||||||
"precio_unitario": round(float(m.group(3).replace(',', '.')), 2),
|
"precio_unitario": round(float(m.group(3).replace(',', '.')), 2),
|
||||||
"precio_total": round(float(m.group(4).replace(',', '.')), 2),
|
"precio_total": round(float(m.group(4).replace(',', '.')), 2),
|
||||||
})
|
})
|
||||||
|
i += 1
|
||||||
continue
|
continue
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
# "1 CROISSANT RELL CACAO 1,90"
|
|
||||||
m = re.match(r"^(\d+)\s+(.+?)\s+(\d+[,\.]\d{2})$", linea)
|
# "1CROISSANT RELL CACAO 1,90"
|
||||||
|
m = re.match(r"^(\d+)(.+?)\s+(\d+[,\.]\d{2})$", linea)
|
||||||
if m:
|
if m:
|
||||||
try:
|
try:
|
||||||
cantidad = int(m.group(1))
|
cantidad = int(m.group(1))
|
||||||
|
|
@ -102,8 +127,12 @@ def parsear_texto_ticket(texto):
|
||||||
"precio_unitario": round(precio / cantidad, 2),
|
"precio_unitario": round(precio / cantidad, 2),
|
||||||
"precio_total": precio,
|
"precio_total": precio,
|
||||||
})
|
})
|
||||||
|
i += 1
|
||||||
|
continue
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
i += 1
|
||||||
return productos
|
return productos
|
||||||
|
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -36,30 +36,61 @@ def extract_data_from_pdf(file_path):
|
||||||
fecha = datetime.strptime(date_match.group(1), "%d/%m/%Y") if date_match else None
|
fecha = datetime.strptime(date_match.group(1), "%d/%m/%Y") if date_match else None
|
||||||
|
|
||||||
products = []
|
products = []
|
||||||
|
lines = text.splitlines()
|
||||||
|
i = 0
|
||||||
|
while i < len(lines):
|
||||||
|
line = lines[i].strip()
|
||||||
|
|
||||||
for line in text.splitlines():
|
if not line or any(kw in line for kw in exclude_keywords):
|
||||||
if any(keyword in line for keyword in exclude_keywords):
|
i += 1
|
||||||
continue
|
|
||||||
|
|
||||||
# Coincide con líneas tipo: "2 ROLLO HOGAR DOBLE 2,35 4,70"
|
|
||||||
match = re.match(r"(\d+)\s+(.*?)\s+(\d+,\d{2})\s+(\d+,\d{2})$", line)
|
|
||||||
if match:
|
|
||||||
cantidad = int(match.group(1))
|
|
||||||
producto = match.group(2).strip().upper()
|
|
||||||
precio_unitario = float(match.group(3).replace(",", "."))
|
|
||||||
precio_total = float(match.group(4).replace(",", "."))
|
|
||||||
products.append((fecha, cantidad, producto, precio_unitario, precio_total))
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Coincide con líneas tipo: "1 CROISSANT RELL CACAO 1,90"
|
# Producto por peso (2 líneas):
|
||||||
match_simple = re.match(r"(\d+)\s+(.*?)\s+(\d+,\d{2})$", line)
|
# "1BROCOLI"
|
||||||
if match_simple:
|
# "1,048 kg 2,60 €/kg 2,72"
|
||||||
cantidad = int(match_simple.group(1))
|
if i + 1 < len(lines):
|
||||||
producto = match_simple.group(2).strip().upper()
|
next_line = lines[i + 1].strip()
|
||||||
precio_total = float(match_simple.group(3).replace(",", "."))
|
weight_m = re.search(r"kg.*?(\d+[,.]\d{2})\s*$", next_line)
|
||||||
precio_unitario = precio_total / cantidad
|
if weight_m and re.match(r"^\d+[A-ZÁÉÍÓÚÑa-záéíóúñ+]", line):
|
||||||
products.append((fecha, cantidad, producto, round(precio_unitario, 2), precio_total))
|
name_m = re.match(r"^(\d+)(.+)$", line)
|
||||||
continue
|
if name_m:
|
||||||
|
cantidad = int(name_m.group(1))
|
||||||
|
producto = name_m.group(2).strip().upper()
|
||||||
|
precio_total = float(weight_m.group(1).replace(",", "."))
|
||||||
|
products.append((fecha, cantidad, producto,
|
||||||
|
round(precio_total / cantidad, 2), precio_total))
|
||||||
|
i += 2
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Formato: "2ARROZ SOS 1,88 3,76" → cantidad / nombre / p.unit / total
|
||||||
|
m = re.match(r"^(\d+)(.+?)\s+(\d+,\d{2})\s+(\d+,\d{2})$", line)
|
||||||
|
if m:
|
||||||
|
try:
|
||||||
|
cantidad = int(m.group(1))
|
||||||
|
producto = m.group(2).strip().upper()
|
||||||
|
precio_unitario = float(m.group(3).replace(",", "."))
|
||||||
|
precio_total = float(m.group(4).replace(",", "."))
|
||||||
|
products.append((fecha, cantidad, producto, precio_unitario, precio_total))
|
||||||
|
i += 1
|
||||||
|
continue
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Formato: "1CALABAZA TROZOS 2,50" → cantidad / nombre / total
|
||||||
|
m = re.match(r"^(\d+)(.+?)\s+(\d+,\d{2})$", line)
|
||||||
|
if m:
|
||||||
|
try:
|
||||||
|
cantidad = int(m.group(1))
|
||||||
|
producto = m.group(2).strip().upper()
|
||||||
|
precio_total = float(m.group(3).replace(",", "."))
|
||||||
|
products.append((fecha, cantidad, producto,
|
||||||
|
round(precio_total / cantidad, 2), precio_total))
|
||||||
|
i += 1
|
||||||
|
continue
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
|
||||||
return products
|
return products
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue