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] }