from datetime import datetime from pydantic import BaseModel, ConfigDict, Field, field_validator class MarketplaceImportJobRequest(BaseModel): """Request schema for triggering marketplace import. Note: vendor_id is injected by middleware, not from request body. """ source_url: str = Field(..., description="URL to CSV file from marketplace") marketplace: str = Field(default="Letzshop", description="Marketplace name") batch_size: int | None = Field( 1000, description="Processing batch size", ge=100, le=10000 ) language: str = Field( default="en", description="Language code for product translations (e.g., 'en', 'fr', 'de')", ) @field_validator("source_url") @classmethod def validate_url(cls, v): # Basic URL security validation if not v.startswith(("http://", "https://")): raise ValueError("URL must start with http:// or https://") return v.strip() @field_validator("marketplace") @classmethod def validate_marketplace(cls, v): return v.strip() @field_validator("language") @classmethod def validate_language(cls, v): # Basic language code validation (2-5 chars) v = v.strip().lower() if not 2 <= len(v) <= 5: raise ValueError("Language code must be 2-5 characters (e.g., 'en', 'fr')") return v class AdminMarketplaceImportJobRequest(BaseModel): """Request schema for admin-triggered marketplace import. Includes vendor_id since admin can import for any vendor. """ vendor_id: int = Field(..., description="Vendor ID to import products for") source_url: str = Field(..., description="URL to CSV file from marketplace") marketplace: str = Field(default="Letzshop", description="Marketplace name") batch_size: int | None = Field( 1000, description="Processing batch size", ge=100, le=10000 ) language: str = Field( default="en", description="Language code for product translations (e.g., 'en', 'fr', 'de')", ) @field_validator("source_url") @classmethod def validate_url(cls, v): # Basic URL security validation if not v.startswith(("http://", "https://")): raise ValueError("URL must start with http:// or https://") return v.strip() @field_validator("marketplace") @classmethod def validate_marketplace(cls, v): return v.strip() @field_validator("language") @classmethod def validate_language(cls, v): v = v.strip().lower() if not 2 <= len(v) <= 5: raise ValueError("Language code must be 2-5 characters (e.g., 'en', 'fr')") return v class MarketplaceImportErrorResponse(BaseModel): """Response schema for individual import error.""" model_config = ConfigDict(from_attributes=True) id: int row_number: int identifier: str | None = None error_type: str error_message: str row_data: dict | None = None created_at: datetime class MarketplaceImportErrorListResponse(BaseModel): """Response schema for list of import errors.""" errors: list[MarketplaceImportErrorResponse] total: int import_job_id: int class MarketplaceImportJobResponse(BaseModel): """Response schema for marketplace import job.""" model_config = ConfigDict(from_attributes=True) job_id: int vendor_id: int vendor_code: str | None = None # Populated from vendor relationship vendor_name: str | None = None # Populated from vendor relationship marketplace: str source_url: str status: str language: str | None = None # Language used for translations # Counts imported: int = 0 updated: int = 0 total_processed: int = 0 error_count: int = 0 # Error details error_message: str | None = None # Timestamps created_at: datetime started_at: datetime | None = None completed_at: datetime | None = None class MarketplaceImportJobListResponse(BaseModel): """Response schema for list of import jobs.""" jobs: list[MarketplaceImportJobResponse] total: int skip: int limit: int class AdminMarketplaceImportJobResponse(MarketplaceImportJobResponse): """Extended response schema for admin with additional fields.""" id: int # Alias for job_id (frontend compatibility) error_details: list = [] # Placeholder for future error details created_by_name: str | None = None # Username of who created the job class AdminMarketplaceImportJobListResponse(BaseModel): """Response schema for paginated list of import jobs (admin).""" items: list[AdminMarketplaceImportJobResponse] total: int page: int limit: int class MarketplaceImportJobStatusUpdate(BaseModel): """Schema for updating import job status (internal use).""" status: str imported_count: int | None = None updated_count: int | None = None error_count: int | None = None total_processed: int | None = None error_message: str | None = None