- Ajout venv Python (.venv) avec pip bootstrap (python3-venv absent) - Correction OCR Linux : marqueur TTC/TVA tolère la confusion T↔I (Tesseract 5.3.4 Linux lit parfois "TIc" au lieu de "TTC") - test_leclerc.py : skipif si Tesseract absent, xfail pour test de somme (précision OCR variable entre plateformes, solution LLM vision prévue) - Résultat : 77 passent, 1 xfail, 0 échec (vs 78 sur Windows) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
73 lines
1.9 KiB
Python
73 lines
1.9 KiB
Python
"""
|
||
Modèle de données commun pour les tickets de courses.
|
||
|
||
Toutes les enseignes (Picnic, Leclerc, etc.) produisent
|
||
une instance de Receipt après parsing. C'est le format
|
||
JSON normalisé en sortie.
|
||
"""
|
||
|
||
import json
|
||
from dataclasses import dataclass, field, asdict
|
||
from datetime import date
|
||
from typing import Optional
|
||
|
||
|
||
@dataclass
|
||
class Item:
|
||
"""Un article sur le ticket de courses."""
|
||
|
||
name: str
|
||
"""Nom du produit tel qu'il apparaît sur le ticket."""
|
||
|
||
quantity: float
|
||
"""Quantité achetée (ex: 2.0, 0.5)."""
|
||
|
||
unit: str
|
||
"""Unité de mesure : 'pièce', 'kg', 'L', 'g', etc."""
|
||
|
||
unit_price: float
|
||
"""Prix unitaire en euros."""
|
||
|
||
total_price: float
|
||
"""Prix total pour cet article (quantity × unit_price)."""
|
||
|
||
category: Optional[str] = None
|
||
"""Catégorie du produit, si disponible (ex: 'Fruits & Légumes')."""
|
||
|
||
|
||
@dataclass
|
||
class Receipt:
|
||
"""Ticket de courses normalisé, toutes enseignes confondues."""
|
||
|
||
store: str
|
||
"""Nom de l'enseigne : 'picnic' ou 'leclerc'."""
|
||
|
||
date: date
|
||
"""Date de la commande ou de l'achat."""
|
||
|
||
total: float
|
||
"""Montant total payé en euros."""
|
||
|
||
items: list[Item] = field(default_factory=list)
|
||
"""Liste des articles achetés."""
|
||
|
||
currency: str = "EUR"
|
||
"""Devise (EUR par défaut)."""
|
||
|
||
order_id: Optional[str] = None
|
||
"""Identifiant de commande, si disponible."""
|
||
|
||
delivery_fee: Optional[float] = None
|
||
"""Frais de livraison en euros, si applicable (None pour Leclerc)."""
|
||
|
||
def to_dict(self) -> dict:
|
||
"""Convertit le ticket en dictionnaire JSON-sérialisable."""
|
||
d = asdict(self)
|
||
# La date n'est pas JSON-sérialisable nativement, on la convertit en string ISO
|
||
d["date"] = self.date.isoformat()
|
||
return d
|
||
|
||
def to_json(self) -> str:
|
||
"""Sérialise le ticket en JSON formaté."""
|
||
return json.dumps(self.to_dict(), ensure_ascii=False, indent=2)
|