Pantry tracker app with: - FastAPI backend + Vue 3 SPA frontend - SQLite via circuitforge-core (migrations 001-005) - Inventory CRUD, barcode scan, receipt OCR pipeline - Expiry prediction (deterministic + LLM fallback) - CF-core tier system integration - Cloud session support (menagerie)
138 lines
4.1 KiB
Python
138 lines
4.1 KiB
Python
#!/usr/bin/env python
|
|
"""
|
|
Pydantic schemas for OCR data models.
|
|
"""
|
|
|
|
from datetime import datetime, date, time
|
|
from typing import Optional, List, Dict, Any
|
|
from uuid import UUID
|
|
from pydantic import BaseModel, Field, validator
|
|
|
|
|
|
class MerchantInfo(BaseModel):
|
|
"""Merchant/store information from receipt."""
|
|
name: Optional[str] = None
|
|
address: Optional[str] = None
|
|
phone: Optional[str] = None
|
|
email: Optional[str] = None
|
|
website: Optional[str] = None
|
|
tax_id: Optional[str] = None
|
|
|
|
|
|
class TransactionInfo(BaseModel):
|
|
"""Transaction details from receipt."""
|
|
date: Optional[date] = None
|
|
time: Optional[time] = None
|
|
receipt_number: Optional[str] = None
|
|
register: Optional[str] = None
|
|
cashier: Optional[str] = None
|
|
transaction_id: Optional[str] = None
|
|
|
|
|
|
class ReceiptItem(BaseModel):
|
|
"""Individual line item from receipt."""
|
|
name: str
|
|
quantity: float = 1.0
|
|
unit_price: Optional[float] = None
|
|
total_price: float
|
|
category: Optional[str] = None
|
|
tax_code: Optional[str] = None
|
|
discount: Optional[float] = 0.0
|
|
barcode: Optional[str] = None
|
|
notes: Optional[str] = None
|
|
|
|
|
|
class ReceiptTotals(BaseModel):
|
|
"""Financial totals from receipt."""
|
|
subtotal: float
|
|
tax: Optional[float] = 0.0
|
|
discount: Optional[float] = 0.0
|
|
tip: Optional[float] = 0.0
|
|
total: float
|
|
payment_method: Optional[str] = None
|
|
amount_paid: Optional[float] = None
|
|
change: Optional[float] = 0.0
|
|
calculated_subtotal: Optional[float] = None # For validation
|
|
|
|
|
|
class ConfidenceScores(BaseModel):
|
|
"""Confidence scores for extracted data."""
|
|
overall: float = Field(ge=0.0, le=1.0)
|
|
merchant: Optional[float] = Field(default=0.5, ge=0.0, le=1.0)
|
|
items: Optional[float] = Field(default=0.5, ge=0.0, le=1.0)
|
|
totals: Optional[float] = Field(default=0.5, ge=0.0, le=1.0)
|
|
transaction: Optional[float] = Field(default=0.5, ge=0.0, le=1.0)
|
|
|
|
|
|
class OCRResult(BaseModel):
|
|
"""Complete OCR extraction result."""
|
|
merchant: MerchantInfo
|
|
transaction: TransactionInfo
|
|
items: List[ReceiptItem]
|
|
totals: ReceiptTotals
|
|
confidence: ConfidenceScores
|
|
raw_text: Optional[str] = None
|
|
warnings: List[str] = Field(default_factory=list)
|
|
processing_time: Optional[float] = None # seconds
|
|
|
|
|
|
class ReceiptDataCreate(BaseModel):
|
|
"""Schema for creating receipt data."""
|
|
receipt_id: UUID
|
|
merchant_name: Optional[str] = None
|
|
merchant_address: Optional[str] = None
|
|
merchant_phone: Optional[str] = None
|
|
transaction_date: Optional[date] = None
|
|
transaction_time: Optional[time] = None
|
|
receipt_number: Optional[str] = None
|
|
items: List[Dict[str, Any]] = Field(default_factory=list)
|
|
subtotal: Optional[float] = None
|
|
tax: Optional[float] = None
|
|
tip: Optional[float] = None
|
|
total: Optional[float] = None
|
|
payment_method: Optional[str] = None
|
|
raw_text: Optional[str] = None
|
|
confidence_scores: Optional[Dict[str, float]] = None
|
|
warnings: List[str] = Field(default_factory=list)
|
|
|
|
|
|
class ReceiptDataResponse(BaseModel):
|
|
"""Schema for receipt data response."""
|
|
id: UUID
|
|
receipt_id: UUID
|
|
merchant_name: Optional[str]
|
|
merchant_address: Optional[str]
|
|
merchant_phone: Optional[str]
|
|
transaction_date: Optional[date]
|
|
transaction_time: Optional[time]
|
|
receipt_number: Optional[str]
|
|
items: List[Dict[str, Any]]
|
|
subtotal: Optional[float]
|
|
tax: Optional[float]
|
|
tip: Optional[float]
|
|
total: Optional[float]
|
|
payment_method: Optional[str]
|
|
raw_text: Optional[str]
|
|
confidence_scores: Optional[Dict[str, float]]
|
|
warnings: List[str]
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class OCRStatusResponse(BaseModel):
|
|
"""OCR processing status response."""
|
|
receipt_id: UUID
|
|
ocr_completed: bool
|
|
has_data: bool
|
|
confidence: Optional[float] = None
|
|
item_count: Optional[int] = None
|
|
warnings: List[str] = Field(default_factory=list)
|
|
|
|
|
|
class OCRTriggerRequest(BaseModel):
|
|
"""Request to trigger OCR processing."""
|
|
force_reprocess: bool = False
|
|
use_quantization: bool = False
|