kiwi/app/models/schemas/ocr.py
pyr0ball 8cbde774e5 chore: initial commit — kiwi Phase 2 complete
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)
2026-03-30 22:20:48 -07:00

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