# #region TranslateSchemas [C:2] [TYPE Module] # @BRIEF Pydantic v2 schemas for translation API request/response serialization. # @LAYER Domain from datetime import datetime from typing import Any, Dict, List, Optional from pydantic import BaseModel, Field import json # #region TranslateJobCreate [C:1] [TYPE Class] class TranslateJobCreate(BaseModel): name: str description: Optional[str] = None source_dialect: str = Field(..., description="Source database dialect (e.g. postgresql, clickhouse)") target_dialect: str = Field(..., description="Target database dialect (e.g. postgresql, clickhouse)") database_dialect: Optional[str] = Field(None, description="Detected dialect from Superset connection at save time") source_datasource_id: Optional[str] = Field(None, description="Superset datasource ID") source_table: Optional[str] = Field(None, description="Source table name") target_schema: Optional[str] = Field(None, description="Target table schema") target_table: Optional[str] = Field(None, description="Target table name") source_key_cols: Optional[List[str]] = Field(default_factory=list, description="Source key column names") target_key_cols: Optional[List[str]] = Field(default_factory=list, description="Target key column names") translation_column: Optional[str] = Field(None, description="Column to translate") context_columns: Optional[List[str]] = Field(default_factory=list, description="Context column names") target_language: Optional[str] = Field(None, description="Target language code") provider_id: Optional[str] = Field(None, description="LLM provider ID") batch_size: int = Field(50, description="Records per batch") upsert_strategy: str = Field("MERGE", description="UPSERT strategy: MERGE, INSERT, UPDATE") dictionary_ids: Optional[List[str]] = Field(default_factory=list, description="Associated terminology dictionary IDs") environment_id: Optional[str] = Field(None, description="Superset environment ID") target_database_id: Optional[str] = Field(None, description="Superset database ID for INSERT via SQL Lab") # #endregion TranslateJobCreate # #region TranslateJobUpdate [C:1] [TYPE Class] class TranslateJobUpdate(BaseModel): name: Optional[str] = None description: Optional[str] = None source_dialect: Optional[str] = None target_dialect: Optional[str] = None database_dialect: Optional[str] = None source_datasource_id: Optional[str] = None source_table: Optional[str] = None target_schema: Optional[str] = None target_table: Optional[str] = None source_key_cols: Optional[List[str]] = None target_key_cols: Optional[List[str]] = None translation_column: Optional[str] = None context_columns: Optional[List[str]] = None target_language: Optional[str] = None provider_id: Optional[str] = None batch_size: Optional[int] = None upsert_strategy: Optional[str] = None status: Optional[str] = None dictionary_ids: Optional[List[str]] = None environment_id: Optional[str] = None target_database_id: Optional[str] = None # #endregion TranslateJobUpdate # #region TranslateJobResponse [C:1] [TYPE Class] class TranslateJobResponse(BaseModel): id: str name: str description: Optional[str] = None source_dialect: str target_dialect: str database_dialect: Optional[str] = None source_datasource_id: Optional[str] = None source_table: Optional[str] = None target_schema: Optional[str] = None target_table: Optional[str] = None source_key_cols: Optional[List[str]] = None target_key_cols: Optional[List[str]] = None translation_column: Optional[str] = None context_columns: Optional[List[str]] = None target_language: Optional[str] = None provider_id: Optional[str] = None batch_size: int = 50 upsert_strategy: str = "MERGE" status: str created_by: Optional[str] = None created_at: datetime updated_at: Optional[datetime] = None dictionary_ids: Optional[List[str]] = None environment_id: Optional[str] = None target_database_id: Optional[str] = None class Config: from_attributes = True # #endregion TranslateJobResponse # #region DatasourceColumnResponse [C:1] [TYPE Class] class DatasourceColumnResponse(BaseModel): name: str type: Optional[str] = None is_physical: bool = True is_dttm: bool = False description: Optional[str] = None # #endregion DatasourceColumnResponse # #region DatasourceColumnsResponse [C:1] [TYPE Class] class DatasourceColumnsResponse(BaseModel): datasource_id: int datasource_name: Optional[str] = None schema_name: Optional[str] = None database_dialect: str columns: List[DatasourceColumnResponse] = [] class Config: from_attributes = True # #endregion DatasourceColumnsResponse # #region DuplicateJobResponse [C:1] [TYPE Class] class DuplicateJobResponse(BaseModel): id: str name: str message: str = "Job duplicated successfully" class Config: from_attributes = True # #endregion DuplicateJobResponse # #region DictionaryCreate [C:1] [TYPE Class] class DictionaryCreate(BaseModel): name: str description: Optional[str] = None source_dialect: str target_dialect: str is_active: bool = True # #endregion DictionaryCreate # #region DictionaryImport [C:1] [TYPE Class] class DictionaryImport(BaseModel): content: str = Field(..., description="CSV or TSV content as raw string") delimiter: Optional[str] = Field(None, description="Detected or forced delimiter: ',' or '\\t'. Auto-detect if omitted.") on_conflict: str = Field("overwrite", description="'overwrite' or 'keep_existing' or 'cancel'") preview_only: bool = Field(False, description="If true, return preview without applying") # #endregion DictionaryImport # #region DictionaryResponse [C:1] [TYPE Class] class DictionaryResponse(BaseModel): id: str name: str description: Optional[str] = None source_dialect: str target_dialect: str is_active: bool created_by: Optional[str] = None created_at: datetime updated_at: Optional[datetime] = None entry_count: Optional[int] = None class Config: from_attributes = True # #endregion DictionaryResponse # #region DictionaryEntryCreate [C:1] [TYPE Class] class DictionaryEntryCreate(BaseModel): source_term: str = Field(..., description="Source term to translate") target_term: str = Field(..., description="Target/translated term") context_notes: Optional[str] = Field(None, description="Optional context notes") class Config: from_attributes = True # #endregion DictionaryEntryCreate # #region DictionaryEntryResponse [C:1] [TYPE Class] class DictionaryEntryResponse(BaseModel): id: str dictionary_id: str source_term: str source_term_normalized: str target_term: str context_notes: Optional[str] = None created_at: datetime updated_at: Optional[datetime] = None class Config: from_attributes = True # #endregion DictionaryEntryResponse # #region DictionaryImportResult [C:1] [TYPE Class] class DictionaryImportResult(BaseModel): total: int = 0 created: int = 0 updated: int = 0 skipped: int = 0 errors: List[Dict[str, Any]] = Field(default_factory=list) preview: List[Dict[str, Any]] = Field(default_factory=list, description="Preview rows with conflict flags") # #endregion DictionaryImportResult # #region PreviewRequest [C:1] [TYPE Class] class PreviewRequest(BaseModel): sample_size: int = Field(10, ge=1, le=100, description="Number of sample rows to preview") prompt_template: Optional[str] = Field(None, description="Optional custom prompt template") env_id: Optional[str] = Field(None, description="Superset environment ID for preview data fetch") # #endregion PreviewRequest # #region PreviewRowUpdate [C:1] [TYPE Class] class PreviewRowUpdate(BaseModel): action: str = Field(..., description="'approve', 'reject', or 'edit'") translation: Optional[str] = Field(None, description="Edited translation (required for 'edit' action)") feedback: Optional[str] = Field(None, description="Optional feedback/comment") # #endregion PreviewRowUpdate # #region PreviewAcceptResponse [C:1] [TYPE Class] class PreviewAcceptResponse(BaseModel): id: str job_id: str status: str created_by: Optional[str] = None created_at: datetime expires_at: Optional[datetime] = None records: List['PreviewRow'] = [] class Config: from_attributes = True # #endregion PreviewAcceptResponse # #region CostEstimate [C:1] [TYPE Class] class CostEstimate(BaseModel): sample_size: int = 0 sample_prompt_tokens: int = 0 sample_output_tokens: int = 0 sample_total_tokens: int = 0 sample_cost: float = 0.0 estimated_total_rows: int = 0 estimated_tokens: int = 0 estimated_cost: float = 0.0 # #endregion CostEstimate # #region TermCorrectionSubmit [C:1] [TYPE Class] class TermCorrectionSubmit(BaseModel): source_term: str incorrect_target_term: str corrected_target_term: str dictionary_id: Optional[str] = Field(None, description="Target dictionary ID (language-filtered)") origin_run_id: Optional[str] = Field(None, description="Run ID from which this correction originated") origin_row_key: Optional[str] = Field(None, description="Row key within the run") # #endregion TermCorrectionSubmit # #region TermCorrectionBulkSubmit [C:1] [TYPE Class] class TermCorrectionBulkSubmit(BaseModel): corrections: List[TermCorrectionSubmit] dictionary_id: str = Field(..., description="Target dictionary ID for all corrections") # #endregion TermCorrectionBulkSubmit # #region CorrectionConflictResult [C:1] [TYPE Class] class CorrectionConflictResult(BaseModel): source_term: str existing_target_term: str submitted_target_term: str action: str = "keep_existing" # #endregion CorrectionConflictResult # #region CorrectionSubmitResponse [C:1] [TYPE Class] class CorrectionSubmitResponse(BaseModel): entry_id: Optional[str] = None action: str # "created", "updated", "conflict_detected", "skipped" source_term: str target_term: str conflict: Optional[CorrectionConflictResult] = None message: Optional[str] = None # #endregion CorrectionSubmitResponse # #region ScheduleConfig [C:1] [TYPE Class] class ScheduleConfig(BaseModel): cron_expression: str = Field(..., description="Cron expression for scheduling (e.g. '0 2 * * *')") timezone: str = Field("UTC", description="Timezone for the cron schedule (e.g. 'UTC', 'Europe/Moscow')") is_active: bool = True # #endregion ScheduleConfig # #region ScheduleResponse [C:1] [TYPE Class] class ScheduleResponse(BaseModel): id: str job_id: str cron_expression: str timezone: str is_active: bool last_run_at: Optional[datetime] = None next_run_at: Optional[datetime] = None created_by: Optional[str] = None created_at: datetime updated_at: Optional[datetime] = None class Config: from_attributes = True # #endregion ScheduleResponse # #region NextExecutionResponse [C:1] [TYPE Class] class NextExecutionResponse(BaseModel): job_id: str cron_expression: str timezone: str next_executions: List[str] = [] # #endregion NextExecutionResponse # #region TranslationRunResponse [C:1] [TYPE Class] class TranslationRunResponse(BaseModel): id: str job_id: str status: str trigger_type: Optional[str] = None started_at: Optional[datetime] = None completed_at: Optional[datetime] = None error_message: Optional[str] = None total_records: int = 0 successful_records: int = 0 failed_records: int = 0 skipped_records: int = 0 insert_status: Optional[str] = None superset_execution_id: Optional[str] = None config_snapshot: Optional[Dict[str, Any]] = None key_hash: Optional[str] = None config_hash: Optional[str] = None dict_snapshot_hash: Optional[str] = None created_by: Optional[str] = None created_at: datetime class Config: from_attributes = True # #endregion TranslationRunResponse # #region RunDetailResponse [C:1] [TYPE Class] class RunDetailResponse(BaseModel): run: TranslationRunResponse records: List[Dict[str, Any]] = Field(default_factory=list, description="Paginated translation records") events: List[Dict[str, Any]] = Field(default_factory=list, description="Run events") event_invariants: Optional[Dict[str, Any]] = None batch_count: int = 0 # #endregion RunDetailResponse # #region RunHistoryFilter [C:1] [TYPE Class] class RunHistoryFilter(BaseModel): job_id: Optional[str] = None status: Optional[str] = None trigger_type: Optional[str] = None created_by: Optional[str] = None date_from: Optional[datetime] = None date_to: Optional[datetime] = None page: int = 1 page_size: int = 20 # #endregion RunHistoryFilter # #region RunListResponse [C:1] [TYPE Class] class RunListResponse(BaseModel): items: List[TranslationRunResponse] = [] total: int = 0 page: int = 1 page_size: int = 20 # #endregion RunListResponse # #region AggregatedMetricsResponse [C:1] [TYPE Class] class AggregatedMetricsResponse(BaseModel): job_id: str total_runs: int = 0 successful_runs: int = 0 failed_runs: int = 0 cancelled_runs: int = 0 total_records: int = 0 successful_records: int = 0 failed_records: int = 0 skipped_records: int = 0 cumulative_tokens: Optional[int] = None cumulative_cost: Optional[float] = None avg_duration_ms: Optional[float] = None last_run_at: Optional[datetime] = None next_scheduled_run: Optional[datetime] = None # #endregion AggregatedMetricsResponse # #region PreviewRow [C:1] [TYPE Class] class PreviewRow(BaseModel): id: str source_sql: Optional[str] = None target_sql: Optional[str] = None source_object_type: Optional[str] = None source_object_id: Optional[str] = None source_object_name: Optional[str] = None status: str = "PENDING" feedback: Optional[str] = None # #endregion PreviewRow # #region TranslationPreviewResponse [C:1] [TYPE Class] class TranslationPreviewResponse(BaseModel): id: str job_id: str run_id: Optional[str] = None status: str created_by: Optional[str] = None created_at: datetime expires_at: Optional[datetime] = None records: List[PreviewRow] = [] class Config: from_attributes = True # #endregion TranslationPreviewResponse # #region TranslationBatchResponse [C:1] [TYPE Class] class TranslationBatchResponse(BaseModel): id: str run_id: str batch_index: int status: str total_records: int = 0 successful_records: int = 0 failed_records: int = 0 started_at: Optional[datetime] = None completed_at: Optional[datetime] = None created_at: datetime class Config: from_attributes = True # #endregion TranslationBatchResponse # #region MetricsResponse [C:1] [TYPE Class] class MetricsResponse(BaseModel): job_id: str snapshot_date: datetime total_jobs: int = 0 total_runs: int = 0 total_records: int = 0 successful_records: int = 0 failed_records: int = 0 skipped_records: int = 0 avg_duration_ms: Optional[int] = None p50_duration_ms: Optional[int] = None p95_duration_ms: Optional[int] = None p99_duration_ms: Optional[int] = None class Config: from_attributes = True # #endregion MetricsResponse # #endregion TranslateSchemas