"""Pydantic schemas for bulk upload endpoints.""" from datetime import datetime from typing import List, Optional from pydantic import BaseModel, Field class BulkUploadResponse(BaseModel): """Response schema for bulk upload endpoint.""" batch_id: int = Field(..., description="Unique batch identifier for tracking") job_ids: List[str] = Field(..., description="List of OCR job UUIDs created") total_files: int = Field(..., description="Number of files in the batch") message: str = Field(..., description="Status message") class Config: json_schema_extra = { "example": { "batch_id": 1, "job_ids": [ "550e8400-e29b-41d4-a716-446655440001", "550e8400-e29b-41d4-a716-446655440002", ], "total_files": 2, "message": "2 files queued for processing" } } class BatchJobInfo(BaseModel): """Information about a single job in a batch.""" job_id: str = Field(..., description="OCR job UUID") filename: str = Field(..., description="Original filename") status: str = Field(..., description="Job status: pending, processing, completed, failed") receipt_id: Optional[int] = Field(None, description="Created receipt ID (if completed)") error_message: Optional[str] = Field(None, description="Error message (if failed)") class BatchStatusResponse(BaseModel): """Response schema for batch status endpoint.""" batch_id: int = Field(..., description="Batch identifier") status: str = Field(..., description="Overall batch status") total_files: int = Field(..., description="Total number of files in batch") pending_count: int = Field(..., description="Number of pending jobs") processing_count: int = Field(..., description="Number of processing jobs") completed_count: int = Field(..., description="Number of completed jobs") failed_count: int = Field(..., description="Number of failed jobs") jobs: List[BatchJobInfo] = Field(..., description="List of jobs with their status") total_amount: Optional[float] = Field(None, description="Sum of all receipt amounts") created_at: datetime = Field(..., description="Batch creation timestamp") class Config: json_schema_extra = { "example": { "batch_id": 1, "status": "processing", "total_files": 5, "pending_count": 2, "processing_count": 1, "completed_count": 2, "failed_count": 0, "jobs": [ {"job_id": "abc-123", "filename": "bon1.pdf", "status": "completed", "receipt_id": 15}, {"job_id": "def-456", "filename": "bon2.jpg", "status": "processing", "receipt_id": None}, ], "total_amount": 150.50, "created_at": "2025-01-09T10:30:00" } } class DuplicateFileInfo(BaseModel): """Information about a duplicate file detected during upload.""" filename: str = Field(..., description="Name of the duplicate file") error: str = Field(default="duplicate", description="Error type (always 'duplicate')") existing_receipt_id: int = Field(..., description="ID of the existing receipt with same file hash") message: str = Field(..., description="Human-readable error message") class Config: json_schema_extra = { "example": { "filename": "bon_lidl.pdf", "error": "duplicate", "existing_receipt_id": 123, "message": "Fișier duplicat - există deja ca bon #123" } } class BulkUploadResponseWithDuplicates(BaseModel): """Response schema for bulk upload with partial success (some duplicates).""" batch_id: Optional[int] = Field(None, description="Batch ID (None if all files were duplicates)") job_ids: List[str] = Field(default_factory=list, description="List of OCR job UUIDs created") total_files: int = Field(..., description="Total number of files submitted") processed_files: int = Field(..., description="Number of files successfully queued") duplicate_files: int = Field(..., description="Number of duplicate files rejected") duplicates: List[DuplicateFileInfo] = Field(default_factory=list, description="List of duplicate file details") message: str = Field(..., description="Status message") class Config: json_schema_extra = { "example": { "batch_id": 1, "job_ids": ["550e8400-e29b-41d4-a716-446655440001"], "total_files": 3, "processed_files": 1, "duplicate_files": 2, "duplicates": [ { "filename": "bon_lidl.pdf", "error": "duplicate", "existing_receipt_id": 123, "message": "Fișier duplicat - există deja ca bon #123" } ], "message": "1 fișier în procesare, 2 duplicate ignorate" } } class BulkUploadError(BaseModel): """Error response for bulk upload validation failures.""" detail: str = Field(..., description="Error message") invalid_files: Optional[List[str]] = Field(None, description="List of invalid filenames") class RetryResponse(BaseModel): """Response schema for retry endpoints.""" success: bool = Field(..., description="Whether the retry was successful") receipt_id: int = Field(..., description="Receipt ID that was retried") job_id: Optional[str] = Field(None, description="New OCR job ID created") message: str = Field(..., description="Status message") class Config: json_schema_extra = { "example": { "success": True, "receipt_id": 123, "job_id": "550e8400-e29b-41d4-a716-446655440001", "message": "Bon reîncarcat în procesare" } } class BatchRetryResponse(BaseModel): """Response schema for batch retry endpoint.""" success: bool = Field(..., description="Whether any retries were successful") batch_id: str = Field(..., description="Batch ID that was retried") retried_count: int = Field(..., description="Number of receipts successfully retried") failed_count: int = Field(..., description="Number of receipts that couldn't be retried") errors: List[str] = Field(default_factory=list, description="List of error messages") message: str = Field(..., description="Status message") class Config: json_schema_extra = { "example": { "success": True, "batch_id": "abc-123", "retried_count": 3, "failed_count": 0, "errors": [], "message": "3 bonuri reîncarcate în procesare" } } class CancelJobResponse(BaseModel): """Response schema for cancel job endpoint.""" success: bool = Field(..., description="Whether the cancellation was successful") job_id: str = Field(..., description="Job ID that was cancelled") cancelled_at: datetime = Field(..., description="Timestamp when the job was cancelled") message: str = Field(..., description="Status message") class Config: json_schema_extra = { "example": { "success": True, "job_id": "550e8400-e29b-41d4-a716-446655440001", "cancelled_at": "2025-01-11T15:30:00", "message": "Job anulat cu succes" } } class CancelBatchResponse(BaseModel): """Response schema for cancel batch endpoint.""" success: bool = Field(..., description="Whether any jobs were cancelled") batch_id: int = Field(..., description="Batch ID that was cancelled") cancelled_count: int = Field(..., description="Number of jobs successfully cancelled") skipped_count: int = Field(..., description="Number of jobs skipped (completed/failed)") message: str = Field(..., description="Status message") class Config: json_schema_extra = { "example": { "success": True, "batch_id": 1, "cancelled_count": 3, "skipped_count": 2, "message": "3 job-uri anulate, 2 ignorate (deja procesate)" } }