from typing import Optional, Dict, Any, List from datetime import datetime from pydantic import BaseModel # Transaction Schemas class TransactionBase(BaseModel): transaction_id: str user_id: str account_id: str amount: float currency: str = "NGN" transaction_type: str # debit, credit, transfer merchant_id: Optional[str] = None merchant_category: Optional[str] = None channel: str # web, mobile, atm, pos location: Optional[str] = None ip_address: Optional[str] = None device_id: Optional[str] = None status: str = "pending" metadata: Optional[Dict[str, Any]] = None class TransactionCreate(TransactionBase): pass class TransactionUpdate(BaseModel): status: Optional[str] = None metadata: Optional[Dict[str, Any]] = None class Transaction(TransactionBase): id: int created_at: datetime updated_at: Optional[datetime] = None class Config: from_attributes = True # Rule Schemas class RuleCondition(BaseModel): field: str operator: str # eq, ne, gt, gte, lt, lte, in, not_in, contains, starts_with, ends_with value: Any aggregate_function: Optional[str] = None # sum, count, avg, max, min time_window: Optional[str] = None # "24h", "7d", "30d", etc. group_by: Optional[List[str]] = None # ["user_id", "account_id"] class RuleAction(BaseModel): action_type: str # flag, block, alert, score parameters: Dict[str, Any] = {} class RuleBase(BaseModel): name: str description: Optional[str] = None rule_type: str # velocity, amount_limit, blacklist, pattern, etc. conditions: List[RuleCondition] actions: List[RuleAction] priority: int = 1 is_active: bool = True class RuleCreate(RuleBase): created_by: Optional[str] = None class RuleUpdate(BaseModel): name: Optional[str] = None description: Optional[str] = None conditions: Optional[List[RuleCondition]] = None actions: Optional[List[RuleAction]] = None priority: Optional[int] = None is_active: Optional[bool] = None class Rule(RuleBase): id: int version: int created_by: Optional[str] = None created_at: datetime updated_at: Optional[datetime] = None class Config: from_attributes = True # Screening Schemas class ScreeningResultBase(BaseModel): transaction_id: str rule_id: int rule_name: str rule_version: int = 1 status: str # flagged, clean, error risk_score: float = 0.0 details: Optional[Dict[str, Any]] = None aggregated_data: Optional[Dict[str, Any]] = None screening_type: str = "real_time" class ScreeningResult(ScreeningResultBase): id: int created_at: datetime class Config: from_attributes = True class BatchScreeningRequest(BaseModel): name: Optional[str] = None description: Optional[str] = None transaction_filters: Optional[Dict[str, Any]] = None rule_ids: Optional[List[int]] = None # If None, apply all active rules date_from: Optional[datetime] = None date_to: Optional[datetime] = None class ScreeningBatch(BaseModel): id: int batch_id: str name: Optional[str] = None description: Optional[str] = None status: str total_transactions: int = 0 processed_transactions: int = 0 flagged_transactions: int = 0 rules_applied: Optional[List[int]] = None started_at: Optional[datetime] = None completed_at: Optional[datetime] = None created_at: datetime class Config: from_attributes = True # Response Schemas class PaginatedResponse(BaseModel): items: List[Any] total: int page: int page_size: int total_pages: int class ScreeningResponse(BaseModel): transaction_id: str results: List[ScreeningResult] overall_status: str # clean, flagged total_risk_score: float screening_duration_ms: float class AggregateRequest(BaseModel): aggregate_function: str # sum, count, avg, max, min field: str group_by: Optional[List[str]] = None filters: Optional[Dict[str, Any]] = None time_window: Optional[str] = None # "24h", "7d", "30d" date_from: Optional[datetime] = None date_to: Optional[datetime] = None class AggregateResponse(BaseModel): result: Dict[str, Any] cache_hit: bool = False computation_time_ms: float