- Removes source_dialect and target_dialect from TerminologyDictionary model, schemas, routes, helper serialization, and all tests - Adds allowed_languages config field (BCP-47 language codes) to GlobalSettings with validation, consolidated settings response, and API endpoint - Adds alembic migration f1a2b3c4d5e6 to drop the two columns
711 lines
28 KiB
Python
711 lines
28 KiB
Python
# #region TranslateSchemas [C:3] [TYPE Module] [SEMANTICS pydantic, translate, schema, translate-job-create]
|
|
# @BRIEF Pydantic v2 schemas for translation API request/response serialization.
|
|
# @LAYER API
|
|
# @RELATION DEPENDS_ON -> [EXT:Library:pydantic]
|
|
|
|
from datetime import datetime
|
|
import re
|
|
from typing import Any
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
|
|
|
|
def _validate_bcp47_list(v: list[str] | None) -> list[str] | None:
|
|
"""Validate that each item in target_languages is a valid BCP-47 tag."""
|
|
if not v:
|
|
return v
|
|
for tag in v:
|
|
if not tag or not tag.strip():
|
|
raise ValueError(f"Invalid BCP-47 tag: '{tag}' — must be a non-empty string")
|
|
if not re.match(r'^[a-zA-Z]{2,8}(-[a-zA-Z0-9]{1,8})*$', tag.strip()):
|
|
raise ValueError(
|
|
f"Invalid BCP-47 tag: '{tag}'. "
|
|
"Expected format like 'en', 'ru', 'zh-CN', 'pt-BR'."
|
|
)
|
|
return v
|
|
|
|
|
|
# #region TranslateJobCreate [TYPE Class]
|
|
# @BRIEF Schema for creating a new translation job.
|
|
class TranslateJobCreate(BaseModel):
|
|
name: str
|
|
description: str | None = 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: str | None = Field(None, description="Detected dialect from Superset connection at save time")
|
|
source_datasource_id: str | None = Field(None, description="Superset datasource ID")
|
|
source_table: str | None = Field(None, description="Source table name")
|
|
target_schema: str | None = Field(None, description="Target table schema")
|
|
target_table: str | None = Field(None, description="Target table name")
|
|
source_key_cols: list[str] | None = Field(default_factory=list, description="Source key column names")
|
|
target_key_cols: list[str] | None = Field(default_factory=list, description="Target key column names")
|
|
translation_column: str | None = Field(None, description="Source column to translate")
|
|
target_column: str | None = Field(None, description="Target column for translated output (defaults to translation_column)")
|
|
target_language_column: str | None = Field(None, description="Target column for language code (e.g. 'ru', 'en')")
|
|
target_source_column: str | None = Field(None, description="Target column for source/original text")
|
|
target_source_language_column: str | None = Field(None, description="Target column for detected source language (BCP-47)")
|
|
context_columns: list[str] | None = Field(default_factory=list, description="Context column names")
|
|
target_language: str | None = Field(None, description="Target language code [DEPRECATED: use target_languages]")
|
|
target_languages: list[str] | None = Field(default_factory=list, description="List of BCP-47 target language codes")
|
|
provider_id: str | None = Field(None, description="LLM provider ID")
|
|
|
|
_validate_target_languages = field_validator("target_languages")(_validate_bcp47_list)
|
|
batch_size: int = Field(50, description="Records per batch")
|
|
disable_reasoning: bool = Field(False, description="If true, pass reasoning_effort:none to LLM to save output tokens")
|
|
upsert_strategy: str = Field("MERGE", description="UPSERT strategy: MERGE, INSERT, UPDATE")
|
|
dictionary_ids: list[str] | None = Field(default_factory=list, description="Associated terminology dictionary IDs")
|
|
environment_id: str | None = Field(None, description="Superset environment ID")
|
|
target_database_id: str | None = Field(None, description="Superset database ID for SQL Lab insert target")
|
|
# #endregion TranslateJobCreate
|
|
|
|
|
|
# #region TranslateJobUpdate [TYPE Class]
|
|
# @BRIEF Schema for updating an existing translation job.
|
|
class TranslateJobUpdate(BaseModel):
|
|
name: str | None = None
|
|
description: str | None = None
|
|
source_dialect: str | None = None
|
|
target_dialect: str | None = None
|
|
database_dialect: str | None = None
|
|
source_datasource_id: str | None = None
|
|
source_table: str | None = None
|
|
target_schema: str | None = None
|
|
target_table: str | None = None
|
|
source_key_cols: list[str] | None = None
|
|
target_key_cols: list[str] | None = None
|
|
translation_column: str | None = None
|
|
target_column: str | None = None
|
|
target_language_column: str | None = None
|
|
target_source_column: str | None = None
|
|
target_source_language_column: str | None = None
|
|
context_columns: list[str] | None = None
|
|
target_language: str | None = None
|
|
target_languages: list[str] | None = None
|
|
provider_id: str | None = None
|
|
|
|
_validate_target_languages = field_validator("target_languages")(_validate_bcp47_list)
|
|
batch_size: int | None = None
|
|
disable_reasoning: bool | None = None
|
|
upsert_strategy: str | None = None
|
|
status: str | None = None
|
|
dictionary_ids: list[str] | None = None
|
|
environment_id: str | None = None
|
|
target_database_id: str | None = None
|
|
# #endregion TranslateJobUpdate
|
|
|
|
|
|
# #region TranslateJobResponse [TYPE Class]
|
|
# @BRIEF Schema for translation job API responses.
|
|
class TranslateJobResponse(BaseModel):
|
|
id: str
|
|
name: str
|
|
description: str | None = None
|
|
source_dialect: str
|
|
target_dialect: str
|
|
database_dialect: str | None = None
|
|
source_datasource_id: str | None = None
|
|
source_table: str | None = None
|
|
target_schema: str | None = None
|
|
target_table: str | None = None
|
|
source_key_cols: list[str] | None = None
|
|
target_key_cols: list[str] | None = None
|
|
translation_column: str | None = None
|
|
target_column: str | None = None
|
|
target_language_column: str | None = None
|
|
target_source_column: str | None = None
|
|
target_source_language_column: str | None = None
|
|
context_columns: list[str] | None = None
|
|
# source_language removed — deprecated, per-row auto-detected
|
|
target_languages: list[str] | None = None
|
|
provider_id: str | None = None
|
|
batch_size: int = 50
|
|
disable_reasoning: bool = False
|
|
upsert_strategy: str = "MERGE"
|
|
status: str
|
|
created_by: str | None = None
|
|
created_at: datetime
|
|
updated_at: datetime | None = None
|
|
dictionary_ids: list[str] | None = None
|
|
environment_id: str | None = None
|
|
target_database_id: str | None = None
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
# #endregion TranslateJobResponse
|
|
|
|
|
|
# #region DatasourceColumnResponse [TYPE Class]
|
|
# @BRIEF Schema for datasource column metadata response.
|
|
class DatasourceColumnResponse(BaseModel):
|
|
name: str
|
|
type: str | None = None
|
|
is_physical: bool = True
|
|
is_dttm: bool = False
|
|
description: str | None = None
|
|
|
|
# #endregion DatasourceColumnResponse
|
|
|
|
# #region DatasourceColumnsResponse [TYPE Class]
|
|
# @BRIEF Schema for datasource columns endpoint response.
|
|
class DatasourceColumnsResponse(BaseModel):
|
|
datasource_id: int
|
|
datasource_name: str | None = None
|
|
schema_name: str | None = None
|
|
database_dialect: str
|
|
columns: list[DatasourceColumnResponse] = []
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
# #endregion DatasourceColumnsResponse
|
|
|
|
|
|
# #region DuplicateJobResponse [TYPE Class]
|
|
# @BRIEF Schema for duplicate job response.
|
|
class DuplicateJobResponse(BaseModel):
|
|
id: str
|
|
name: str
|
|
message: str = "Job duplicated successfully"
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
# #endregion DuplicateJobResponse
|
|
|
|
|
|
# #region DictionaryCreate [TYPE Class]
|
|
# @BRIEF Schema for creating a new terminology dictionary.
|
|
class DictionaryCreate(BaseModel):
|
|
name: str
|
|
description: str | None = None
|
|
is_active: bool = True
|
|
# #endregion DictionaryCreate
|
|
|
|
|
|
# #region DictionaryImport [TYPE Class]
|
|
# @BRIEF Schema for importing entries into a terminology dictionary.
|
|
class DictionaryImport(BaseModel):
|
|
content: str = Field(..., description="CSV or TSV content as raw string")
|
|
delimiter: str | None = 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")
|
|
default_source_language: str | None = Field(None, description="Default BCP-47 source language when column missing in data")
|
|
default_target_language: str | None = Field(None, description="Default BCP-47 target language when column missing in data")
|
|
# #endregion DictionaryImport
|
|
|
|
|
|
# #region DictionaryResponse [TYPE Class]
|
|
# @BRIEF Schema for terminology dictionary API responses.
|
|
class DictionaryResponse(BaseModel):
|
|
id: str
|
|
name: str
|
|
description: str | None = None
|
|
is_active: bool
|
|
created_by: str | None = None
|
|
created_at: datetime
|
|
updated_at: datetime | None = None
|
|
entry_count: int | None = None
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
# #endregion DictionaryResponse
|
|
|
|
|
|
# #region DictionaryEntryCreate [TYPE Class]
|
|
# @BRIEF Schema for adding/editing a dictionary entry with language pair support.
|
|
class DictionaryEntryCreate(BaseModel):
|
|
source_term: str = Field(..., description="Source term to translate")
|
|
target_term: str = Field(..., description="Target/translated term")
|
|
context_notes: str | None = Field(None, description="Optional context notes")
|
|
source_language: str = Field("und", description="BCP-47 source language code (default: und)")
|
|
target_language: str = Field("und", description="BCP-47 target language code (default: und)")
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
# #endregion DictionaryEntryCreate
|
|
|
|
|
|
# #region DictionaryEntryResponse [TYPE Class]
|
|
# @BRIEF Schema for dictionary entry API responses.
|
|
class DictionaryEntryResponse(BaseModel):
|
|
id: str
|
|
dictionary_id: str
|
|
source_term: str
|
|
source_term_normalized: str
|
|
target_term: str
|
|
source_language: str | None = None
|
|
target_language: str | None = None
|
|
context_notes: str | None = None
|
|
context_data: dict[str, Any] | None = None
|
|
usage_notes: str | None = None
|
|
has_context: bool = False
|
|
context_source: str | None = None
|
|
origin_source_language: str | None = None
|
|
created_at: datetime
|
|
updated_at: datetime | None = None
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
# #endregion DictionaryEntryResponse
|
|
|
|
|
|
# #region DictionaryImportResult [TYPE Class]
|
|
# @BRIEF Schema for dictionary import result.
|
|
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 [TYPE Class]
|
|
# @BRIEF Schema for triggering a translation preview.
|
|
class PreviewRequest(BaseModel):
|
|
sample_size: int = Field(10, ge=1, le=100, description="Number of sample rows to preview")
|
|
prompt_template: str | None = Field(None, description="Optional custom prompt template")
|
|
env_id: str | None = Field(None, description="Superset environment ID for preview data fetch")
|
|
# #endregion PreviewRequest
|
|
|
|
|
|
# #region PreviewRowUpdate [TYPE Class]
|
|
# @BRIEF Schema for approving/editing/rejecting a preview row.
|
|
class PreviewRowUpdate(BaseModel):
|
|
action: str = Field(..., description="'approve', 'reject', or 'edit'")
|
|
translation: str | None = Field(None, description="Edited translation (required for 'edit' action)")
|
|
feedback: str | None = Field(None, description="Optional feedback/comment")
|
|
language_code: str | None = Field(None, description="BCP-47 language code for per-language actions (optional, applies to all if omitted)")
|
|
# #endregion PreviewRowUpdate
|
|
|
|
|
|
# #region PreviewAcceptResponse [TYPE Class]
|
|
# @BRIEF Schema for preview accept response.
|
|
class PreviewAcceptResponse(BaseModel):
|
|
id: str
|
|
job_id: str
|
|
status: str
|
|
created_by: str | None = None
|
|
created_at: datetime
|
|
expires_at: datetime | None = None
|
|
records: list['PreviewRow'] = []
|
|
target_languages: list[str] = Field(default_factory=list, description="Target languages for this preview")
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
# #endregion PreviewAcceptResponse
|
|
|
|
|
|
# #region CostEstimate [TYPE Class]
|
|
# @BRIEF Schema for cost estimation in preview response.
|
|
class CostEstimate(BaseModel):
|
|
sample_size: int = 0
|
|
num_languages: int = 1
|
|
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
|
|
warning: str | None = None
|
|
# #endregion CostEstimate
|
|
|
|
|
|
# #region TermCorrectionSubmit [TYPE Class]
|
|
# @BRIEF Schema for submitting a term correction in a translation preview.
|
|
class TermCorrectionSubmit(BaseModel):
|
|
source_term: str
|
|
incorrect_target_term: str
|
|
corrected_target_term: str
|
|
dictionary_id: str | None = Field(None, description="Target dictionary ID (language-filtered)")
|
|
origin_run_id: str | None = Field(None, description="Run ID from which this correction originated")
|
|
origin_row_key: str | None = Field(None, description="Row key within the run")
|
|
context_data: dict[str, Any] | None = Field(None, description="Context data for the dictionary entry")
|
|
usage_notes: str | None = Field(None, description="Usage notes for the dictionary entry")
|
|
keep_context: bool = Field(True, description="If false, clear context_data and set has_context=False on dictionary entry")
|
|
# #endregion TermCorrectionSubmit
|
|
|
|
|
|
# #region TermCorrectionBulkSubmit [TYPE Class]
|
|
# @BRIEF Schema for submitting multiple term corrections atomically.
|
|
class TermCorrectionBulkSubmit(BaseModel):
|
|
corrections: list[TermCorrectionSubmit]
|
|
dictionary_id: str = Field(..., description="Target dictionary ID for all corrections")
|
|
# #endregion TermCorrectionBulkSubmit
|
|
|
|
|
|
# #region CorrectionConflictResult [TYPE Class]
|
|
# @BRIEF Schema for reporting conflicts in correction submission.
|
|
class CorrectionConflictResult(BaseModel):
|
|
source_term: str
|
|
existing_target_term: str
|
|
submitted_target_term: str
|
|
action: str = "keep_existing"
|
|
# #endregion CorrectionConflictResult
|
|
|
|
|
|
# #region CorrectionSubmitResponse [TYPE Class]
|
|
# @BRIEF Schema for correction submission response.
|
|
class CorrectionSubmitResponse(BaseModel):
|
|
entry_id: str | None = None
|
|
action: str # "created", "updated", "conflict_detected", "skipped"
|
|
source_term: str
|
|
target_term: str
|
|
conflict: CorrectionConflictResult | None = None
|
|
message: str | None = None
|
|
# #endregion CorrectionSubmitResponse
|
|
|
|
|
|
# #region ScheduleConfig [TYPE Class]
|
|
# @BRIEF Schema for configuring a recurring translation schedule.
|
|
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
|
|
execution_mode: str = Field("full", description="Translation execution mode: 'full' or 'new_key_only'")
|
|
# #endregion ScheduleConfig
|
|
|
|
|
|
# #region ScheduleResponse [TYPE Class]
|
|
# @BRIEF Schema for schedule API responses.
|
|
class ScheduleResponse(BaseModel):
|
|
id: str
|
|
job_id: str
|
|
cron_expression: str
|
|
timezone: str
|
|
is_active: bool
|
|
execution_mode: str = "full"
|
|
last_run_at: datetime | None = None
|
|
next_run_at: datetime | None = None
|
|
created_by: str | None = None
|
|
created_at: datetime
|
|
updated_at: datetime | None = None
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
# #endregion ScheduleResponse
|
|
|
|
|
|
# #region NextExecutionResponse [TYPE Class]
|
|
# @BRIEF Schema for next execution preview.
|
|
class NextExecutionResponse(BaseModel):
|
|
job_id: str
|
|
cron_expression: str
|
|
timezone: str
|
|
next_executions: list[str] = []
|
|
# #endregion NextExecutionResponse
|
|
|
|
|
|
# #region TranslationRunResponse [TYPE Class]
|
|
# @BRIEF Schema for translation run API responses.
|
|
class TranslationRunResponse(BaseModel):
|
|
id: str
|
|
job_id: str
|
|
status: str
|
|
trigger_type: str | None = None
|
|
started_at: datetime | None = None
|
|
completed_at: datetime | None = None
|
|
error_message: str | None = None
|
|
total_records: int = 0
|
|
successful_records: int = 0
|
|
failed_records: int = 0
|
|
skipped_records: int = 0
|
|
cache_hits: int = 0
|
|
insert_status: str | None = None
|
|
superset_execution_id: str | None = None
|
|
config_snapshot: dict[str, Any] | None = None
|
|
key_hash: str | None = None
|
|
config_hash: str | None = None
|
|
dict_snapshot_hash: str | None = None
|
|
created_by: str | None = None
|
|
created_at: datetime
|
|
language_stats: list['TranslationRunLanguageStatsResponse'] | None = None
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
# #endregion TranslationRunResponse
|
|
|
|
|
|
# #region RunDetailResponse [TYPE Class]
|
|
# @BRIEF Schema for detailed run response including records, events, and config snapshot.
|
|
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: dict[str, Any] | None = None
|
|
batch_count: int = 0
|
|
# #endregion RunDetailResponse
|
|
|
|
|
|
# #region RunHistoryFilter [TYPE Class]
|
|
# @BRIEF Schema for filtering run history.
|
|
class RunHistoryFilter(BaseModel):
|
|
job_id: str | None = None
|
|
status: str | None = None
|
|
trigger_type: str | None = None
|
|
created_by: str | None = None
|
|
date_from: datetime | None = None
|
|
date_to: datetime | None = None
|
|
page: int = 1
|
|
page_size: int = 20
|
|
# #endregion RunHistoryFilter
|
|
|
|
|
|
# #region RunListResponse [TYPE Class]
|
|
# @BRIEF Schema for paginated run list response.
|
|
class RunListResponse(BaseModel):
|
|
items: list[TranslationRunResponse] = []
|
|
total: int = 0
|
|
page: int = 1
|
|
page_size: int = 20
|
|
# #endregion RunListResponse
|
|
|
|
|
|
# #region AggregatedMetricsResponse [TYPE Class]
|
|
# @BRIEF Schema for aggregated per-job metrics.
|
|
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: int | None = None
|
|
cumulative_cost: float | None = None
|
|
avg_duration_ms: float | None = None
|
|
last_run_at: datetime | None = None
|
|
next_scheduled_run: datetime | None = None
|
|
# #endregion AggregatedMetricsResponse
|
|
|
|
|
|
# #region TranslationPreviewLanguageResponse [C:1] [TYPE Class]
|
|
# @BRIEF Schema for per-language preview data in API responses.
|
|
class TranslationPreviewLanguageResponse(BaseModel):
|
|
language_code: str
|
|
source_language_detected: str | None = None
|
|
translated_value: str | None = None
|
|
user_edit: str | None = None
|
|
final_value: str | None = None
|
|
status: str = "pending"
|
|
needs_review: bool = False
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
# #endregion TranslationPreviewLanguageResponse
|
|
|
|
|
|
# #region PreviewRow [TYPE Class]
|
|
# @BRIEF A single row in a translation preview showing original and translated content side by side.
|
|
class PreviewRow(BaseModel):
|
|
id: str
|
|
source_sql: str | None = None
|
|
target_sql: str | None = None
|
|
source_object_type: str | None = None
|
|
source_object_id: str | None = None
|
|
source_object_name: str | None = None
|
|
status: str = "PENDING"
|
|
feedback: str | None = None
|
|
source_language_detected: str | None = None
|
|
needs_review: bool = False
|
|
languages: list[TranslationPreviewLanguageResponse] = Field(default_factory=list, description="Per-language translation data")
|
|
# #endregion PreviewRow
|
|
|
|
|
|
# #region TranslationPreviewResponse [TYPE Class]
|
|
# @BRIEF Schema for translation preview session API responses.
|
|
class TranslationPreviewResponse(BaseModel):
|
|
id: str
|
|
job_id: str
|
|
run_id: str | None = None
|
|
status: str
|
|
created_by: str | None = None
|
|
created_at: datetime
|
|
expires_at: datetime | None = None
|
|
records: list[PreviewRow] = []
|
|
target_languages: list[str] = Field(default_factory=list, description="Target languages for this preview")
|
|
cost_estimate: CostEstimate | None = None
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
# #endregion TranslationPreviewResponse
|
|
|
|
|
|
# #region TranslationBatchResponse [TYPE Class]
|
|
# @BRIEF Schema for translation batch API responses.
|
|
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: datetime | None = None
|
|
completed_at: datetime | None = None
|
|
created_at: datetime
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
# #endregion TranslationBatchResponse
|
|
|
|
|
|
# #region MetricsResponse [TYPE Class]
|
|
# @BRIEF Schema for translation metrics API responses.
|
|
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: int | None = None
|
|
p50_duration_ms: int | None = None
|
|
p95_duration_ms: int | None = None
|
|
p99_duration_ms: int | None = None
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
# #endregion MetricsResponse
|
|
|
|
|
|
# #region TranslationLanguageResponse [C:1] [TYPE Class]
|
|
# @BRIEF Schema for per-language translation data in API responses.
|
|
class TranslationLanguageResponse(BaseModel):
|
|
language_code: str
|
|
source_language_detected: str | None = None
|
|
translated_value: str | None = None
|
|
user_edit: str | None = None
|
|
final_value: str | None = None
|
|
status: str = "pending"
|
|
needs_review: bool = False
|
|
language_overridden: bool = False
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
# #endregion TranslationLanguageResponse
|
|
|
|
|
|
# #region TranslationRunLanguageStatsResponse [C:1] [TYPE Class]
|
|
# @BRIEF Schema for per-language statistics in run API responses.
|
|
class TranslationRunLanguageStatsResponse(BaseModel):
|
|
language_code: str
|
|
total_rows: int = 0
|
|
translated_rows: int = 0
|
|
failed_rows: int = 0
|
|
skipped_rows: int = 0
|
|
token_count: int = 0
|
|
estimated_cost: float = 0.0
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
# #endregion TranslationRunLanguageStatsResponse
|
|
|
|
|
|
# #region OverrideLanguageRequest [C:1] [TYPE Class]
|
|
# @BRIEF Schema for manually overriding the detected source language of a translation.
|
|
class OverrideLanguageRequest(BaseModel):
|
|
source_language: str = Field(..., description="BCP-47 language code to override with")
|
|
# #endregion OverrideLanguageRequest
|
|
|
|
|
|
# #region BulkFindReplaceRequest [C:1] [TYPE Class]
|
|
# @BRIEF Schema for bulk find-and-replace within translations for a target language.
|
|
class BulkFindReplaceRequest(BaseModel):
|
|
find_pattern: str = Field(..., description="Text or regex pattern to find", max_length=500)
|
|
is_regex: bool = Field(False, description="Whether find_pattern is a regular expression")
|
|
replacement_text: str = Field(..., description="Replacement text")
|
|
target_language: str = Field(..., description="BCP-47 language code to target")
|
|
preview: bool = Field(True, description="If true, return matches without applying replacements")
|
|
submit_to_dictionary: bool = Field(False, description="If true, also submit corrections to dictionary")
|
|
dictionary_id: str | None = Field(None, description="Target dictionary ID for submitting corrections")
|
|
usage_notes: str | None = Field(None, description="Usage notes for dictionary entries")
|
|
submit_to_dictionary_with_context: bool = Field(False, description="If true, auto-capture source row context when submitting to dictionary")
|
|
|
|
@field_validator("find_pattern")
|
|
@classmethod
|
|
def validate_pattern_length(cls, v: str) -> str:
|
|
"""Reject patterns longer than 500 characters to prevent ReDoS."""
|
|
if len(v) > 500:
|
|
raise ValueError("Pattern too long (max 500 characters)")
|
|
return v
|
|
# #endregion BulkFindReplaceRequest
|
|
|
|
|
|
# #region InlineEditRequest [C:1] [TYPE Class]
|
|
# @BRIEF Schema for inline editing a translated value on a completed run result.
|
|
class InlineEditRequest(BaseModel):
|
|
final_value: str = Field(..., description="Corrected/edited translation text")
|
|
submit_to_dictionary: bool = Field(False, description="If true, also save correction to dictionary")
|
|
dictionary_id: str | None = Field(None, description="Target dictionary ID for saving correction")
|
|
context_data_override: dict[str, Any] | None = Field(None, description="Override auto-captured context data for dictionary entry")
|
|
usage_notes: str | None = Field(None, description="Usage notes for the dictionary entry")
|
|
keep_context: bool = Field(True, description="If false, clear context_data and set has_context=False on dictionary entry")
|
|
# #endregion InlineEditRequest
|
|
|
|
|
|
# #region BulkReplacePreviewItem [C:1] [TYPE Class]
|
|
# @BRIEF A single item in a bulk replace preview list.
|
|
class BulkReplacePreviewItem(BaseModel):
|
|
record_id: str
|
|
language_code: str
|
|
source_term: str
|
|
current_value: str
|
|
new_value: str
|
|
source_language_detected: str | None = None
|
|
# #endregion BulkReplacePreviewItem
|
|
|
|
|
|
# #region BulkReplaceResponse [C:1] [TYPE Class]
|
|
# @BRIEF Response for bulk find-and-replace operation.
|
|
class BulkReplaceResponse(BaseModel):
|
|
rows_affected: int = 0
|
|
corrections_submitted: int = 0
|
|
preview: list[BulkReplacePreviewItem] = Field(default_factory=list)
|
|
# #endregion BulkReplaceResponse
|
|
|
|
|
|
# #region InlineCorrectionSubmit [C:1] [TYPE Class]
|
|
# @BRIEF Schema for submitting an inline correction for a specific translated record.
|
|
class InlineCorrectionSubmit(BaseModel):
|
|
record_id: str = Field(..., description="Translation record ID")
|
|
language_code: str = Field(..., description="BCP-47 language code of the translated value")
|
|
source_term: str = Field(..., description="Original source term that was incorrectly translated")
|
|
corrected_term: str = Field(..., description="Corrected translation")
|
|
dictionary_id: str = Field(..., description="Target dictionary ID to also save the correction")
|
|
source_language: str = Field(..., description="BCP-47 source language code")
|
|
# #endregion InlineCorrectionSubmit
|
|
|
|
|
|
# #region TargetSchemaValidationRequest [C:1] [TYPE Class]
|
|
# @BRIEF Schema for requesting target table schema validation.
|
|
class TargetSchemaValidationRequest(BaseModel):
|
|
environment_id: str = Field(..., description="Superset environment ID")
|
|
target_database_id: str = Field(..., description="Superset database ID for the target DB")
|
|
target_schema: str = Field("public", description="Target table schema (default: public)")
|
|
target_table: str = Field(..., description="Target table name")
|
|
# Column mapping from job config (everything that affects expected columns)
|
|
target_key_cols: list[str] | None = Field(default_factory=list, description="Target key column names")
|
|
target_column: str | None = Field(None, description="Target column for translated output")
|
|
translation_column: str | None = Field(None, description="Source column name (fallback for target_column)")
|
|
target_language_column: str | None = Field(None, description="Column for language code")
|
|
target_source_column: str | None = Field(None, description="Column for source text")
|
|
target_source_language_column: str | None = Field(None, description="Column for detected source language")
|
|
# #endregion TargetSchemaValidationRequest
|
|
|
|
|
|
# #region TargetSchemaColumnInfo [C:1] [TYPE Class]
|
|
# @BRIEF Schema for a single column in the schema validation response.
|
|
class TargetSchemaColumnInfo(BaseModel):
|
|
name: str
|
|
data_type: str | None = None
|
|
is_nullable: bool = True
|
|
# #endregion TargetSchemaColumnInfo
|
|
|
|
|
|
# #region TargetSchemaValidationResponse [C:1] [TYPE Class]
|
|
# @BRIEF Schema for target table schema validation response.
|
|
class TargetSchemaValidationResponse(BaseModel):
|
|
table_exists: bool = False
|
|
error: str | None = None
|
|
expected_columns: list[TargetSchemaColumnInfo] = Field(default_factory=list, description="Columns that the INSERT expects")
|
|
actual_columns: list[TargetSchemaColumnInfo] = Field(default_factory=list, description="Columns that exist in the target table")
|
|
missing_columns: list[TargetSchemaColumnInfo] = Field(default_factory=list, description="Expected columns NOT found in target table")
|
|
extra_columns: list[TargetSchemaColumnInfo] = Field(default_factory=list, description="Actual columns NOT in expected list")
|
|
all_present: bool = False
|
|
database_name: str | None = Field(default=None, description="Actual database name queried (from Superset)")
|
|
database_backend: str | None = Field(default=None, description="Actual database backend dialect (from Superset)")
|
|
# #endregion TargetSchemaValidationResponse
|
|
|
|
|
|
# #endregion TranslateSchemas
|