INVOICE / extractor.py
Anh1211's picture
Upload 4 files
3dd7866 verified
Raw
History Blame Contribute Delete
2.89 kB
import re
import io
import pytesseract
from PIL import Image
from pdf2image import convert_from_bytes
from typing import Dict, Any
# --- Configuration des Regex (Extraction Étape 2) ---
RE_DATE = re.compile(r"(\d{1,2}[\/\.-]\d{1,2}[\/\.-]\d{2,4})") # [cite: 48]
RE_TOTAL = re.compile(r"(?:total|montant|ttc|à payer).*?(\d+[\.,]\d{2})", re.IGNORECASE) # [cite: 66, 73]
RE_TVA = re.compile(r"(\d{1,2}(?:[\.,]\d)?)\s*%", re.IGNORECASE) # [cite: 49, 72]
RE_PAYMENT = re.compile(r"(?:payé par|moyen de paiement|paiement)\s*[:\-]?\s*(.*)", re.IGNORECASE) # [cite: 67]
RE_VENDEUR_LABEL = re.compile(r"(?:vendeur|fournisseur|vendor)\s*[:\-]?\s*(.*)", re.IGNORECASE)
def ocr_document(file_bytes: bytes, is_pdf: bool):
"""Étape 1 : Extraction du texte brut via Tesseract [cite: 19, 21]"""
if is_pdf:
# Nécessite poppler-utils dans packages.txt
images = convert_from_bytes(file_bytes)
else:
images = [Image.open(io.BytesIO(file_bytes))]
full_text = ""
for img in images:
full_text += pytesseract.image_to_string(img, lang="fra+eng") + "\n"
return full_text, images
def extract_fields(text: str) -> Dict[str, Any]:
"""Étape 2 : Parsing des informations clés [cite: 22, 24, 26]"""
lines = [l.strip() for l in text.split('\n') if len(l.strip()) > 2]
# 1. Nom de la marchandise (Enseigne/Marchand)
supplier = None
label_match = RE_VENDEUR_LABEL.search(text)
if label_match and label_match.group(1).strip():
supplier = label_match.group(1).strip()
if not supplier:
# Fallback : évite les lignes de pagination (ex: IKEA) [cite: 44]
for line in lines:
if "page" not in line.lower() and not any(char.isdigit() for char in line[:3]):
supplier = line
break
# 2. Prix Total [cite: 66]
total_matches = RE_TOTAL.findall(text)
total_amount = float(total_matches[-1].replace(",", ".")) if total_matches else None
# 3. % TVA (On prend le premier taux trouvé)
tva_match = RE_TVA.search(text)
tva_rate = tva_match.group(1).replace(",", ".") + "%" if tva_match else None
# 4. Moyen de paiement (Nettoyage des chiffres et étoiles) [cite: 67, 70]
payment_match = RE_PAYMENT.search(text)
payment_method = None
if payment_match:
raw_p = payment_match.group(1).strip()
# Supprime chiffres, étoiles et points pour ne garder que le texte
payment_method = re.sub(r'[\d\*\.]+', '', raw_p).strip()
# 5. Date de facture [cite: 48]
date_match = RE_DATE.search(text)
invoice_date = date_match.group(1) if date_match else None
return {
"nom_marchand": supplier, # [cite: 9]
"prix_total": total_amount, # [cite: 9]
"tva_pourcentage": tva_rate,
"moyen_paiement": payment_method,
"invoice_date": invoice_date # [cite: 9]
}