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.
132 lines
4.6 KiB
Python
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
|