Files
ss-tools/backend/src/plugins/translate/orchestrator_run_completion.py
busya 4205618ee6 chore: eliminate all deprecation warnings from tests and linter
Warnings fixed:
- datetime.utcnow() → datetime.now(UTC) across 48+ files (src/ + tests/)
- datetime.utcnow (callback ref) → lambda: datetime.now(UTC) in model fields (18 files)
- Pydantic class Config → model_config = ConfigDict(...) (16 files)
- Pydantic .dict() → .model_dump() (8 files)
- ConfigDict(allow_population_by_field_name=True) → validate_by_name=True
- SQLAlchemy declarative_base() import path updated
- FastAPI on_event → lifespan context manager (app.py)
- Import sorting (ruff I001) auto-fixed across all files
- Fixed broken re-export chains that ruff F401 cleanup broke:
  _validate_bcp47: service.py now imports from dictionary_validation directly
  job_to_response: _job_routes.py and test imports from service_utils directly
  fetch_datasource_metadata: restored re-export in service.py
- Added missing TranslateJobService import in _job_routes.py (was deleted by F401)
- Added ConfigDict(protected_namespaces=()) for DashboardDatasetItem schema field
- pytest.ini: replaced deprecated importmode with asyncio_mode

All 440 tests pass with zero deprecation warnings.
2026-05-26 19:18:28 +03:00

132 lines
4.6 KiB
Python

# #region orchestrator_run_completion [C:3] [TYPE Module] [SEMANTICS translate, run, completion, failure]
# @BRIEF Post-execution run completion handlers: cancelled, success-with-insert, and failure paths.
# @RELATION DEPENDS_ON -> [TranslationRun]
# @RELATION DEPENDS_ON -> [TranslationJob]
# @RELATION DEPENDS_ON -> [TranslationResultAggregator]
# @RELATION DEPENDS_ON -> [SQLInsertService]
# @RELATION DEPENDS_ON -> [TranslationEventLog]
from datetime import UTC, datetime
from ...core.logger import logger
from ...models.translate import TranslationJob, TranslationRun, TranslationRunLanguageStats
# region handle_executor_failure [TYPE Function]
# @PURPOSE: Handle executor failure — rollback, mark run as FAILED, log event.
# @SIDE_EFFECT DB writes; event log.
def handle_executor_failure(
db,
event_log,
run: TranslationRun,
job: TranslationJob,
error: Exception,
current_user: str | None = None,
) -> TranslationRun:
"""Handle executor failure — rollback, mark as FAILED, log event."""
logger.explore("Translation execution failed", {"run_id": run.id, "error": str(error)})
try:
db.rollback()
except Exception:
pass
run = db.merge(run)
run.status = "FAILED"
run.error_message = f"Translation execution failed: {error}"
run.completed_at = datetime.now(UTC)
db.flush()
event_log.log_event(
job_id=job.id, run_id=run.id, event_type="RUN_FAILED",
payload={"error": str(error), "phase": "translation"},
created_by=current_user,
)
db.commit()
return run
# endregion handle_executor_failure
# region complete_cancelled [TYPE Function]
# @PURPOSE: Finalize a cancelled run — update language stats and log.
# @SIDE_EFFECT DB writes; event log.
def complete_cancelled(
db,
event_log,
aggregator,
run: TranslationRun,
language_stats_map: dict[str, TranslationRunLanguageStats],
current_user: str | None = None,
) -> TranslationRun:
"""Finalize a cancelled run."""
aggregator.update_language_stats(run.id, language_stats_map)
event_log.log_event(
job_id=run.job_id, run_id=run.id, event_type="RUN_CANCELLED",
payload={"reason": "cancellation_flag"}, created_by=current_user,
)
db.commit()
return run
# endregion complete_cancelled
# region complete_success [TYPE Function]
# @PURPOSE: Finalize a successful run — update stats, optionally insert SQL, commit.
# @SIDE_EFFECT DB writes; event log; Superset API call if skip_insert is False.
def complete_success(
db,
event_log,
aggregator,
sql_service,
run: TranslationRun,
job: TranslationJob,
skip_insert: bool,
language_stats_map: dict[str, TranslationRunLanguageStats],
current_user: str | None = None,
) -> TranslationRun:
"""Finalize a successful run with stats update and optional SQL insert."""
aggregator.update_language_stats(run.id, language_stats_map)
event_log.log_event(
job_id=job.id, run_id=run.id, event_type="TRANSLATION_PHASE_COMPLETED",
payload={
"total": run.total_records, "successful": run.successful_records,
"failed": run.failed_records, "skipped": run.skipped_records,
},
created_by=current_user,
)
if skip_insert:
run.status = "COMPLETED"
run.completed_at = datetime.now(UTC)
db.flush()
event_log.log_event(
job_id=job.id, run_id=run.id, event_type="RUN_COMPLETED",
payload={"skip_insert": True}, created_by=current_user,
)
db.commit()
return run
insert_result = sql_service.generate_and_insert_sql(job, run)
run.insert_status = insert_result.get("status")
run.superset_execution_id = str(insert_result.get("query_id") or "")
run.superset_execution_log = insert_result
if run.status != "FAILED":
run.status = "COMPLETED"
if insert_result.get("error_message"):
run.error_message = insert_result["error_message"]
run.completed_at = datetime.now(UTC)
db.flush()
event_log.log_event(
job_id=job.id, run_id=run.id, event_type="RUN_COMPLETED",
payload={
"insert_status": insert_result.get("status"),
"query_id": insert_result.get("query_id"),
"total_records": run.total_records,
"successful": run.successful_records,
"failed": run.failed_records,
},
created_by=current_user,
)
db.commit()
run = db.query(TranslationRun).filter(TranslationRun.id == run.id).first()
return run
# endregion complete_success
# #endregion orchestrator_run_completion