- Convert legacy [DEF🆔Type] anchors to #region/#endregion across 329 files
- Reinstate _normalize_timestamp_value in sql_generator.py
- Fix MarkerLogger→logger migration in events.py (molecular CoT markers)
- Fix dataset_review orchestrator dependencies (_build_execution_snapshot)
- Fix config_manager stale-record deletion (moved to save path only)
- Add 77 missing [/DEF:] closers in 5 unbalanced test files
- Update assistant_chat.integration.test.js for #region format
- Apply molecular-cot-logging markers (REASON/REFLECT/EXPLORE) via logger.* methods
234 lines
9.8 KiB
Python
234 lines
9.8 KiB
Python
# #region TranslateScheduleRoutesModule [C:3] [TYPE Module] [SEMANTICS api, routes, translate, schedule]
|
|
# @BRIEF Translation Schedule management routes.
|
|
# @LAYER: API
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, status, Query
|
|
from typing import Any, Dict, List, Optional
|
|
from sqlalchemy.orm import Session
|
|
|
|
from ....core.database import get_db
|
|
from ....core.logger import logger, belief_scope
|
|
from ....schemas.auth import User
|
|
from ....dependencies import get_current_user, has_permission, get_config_manager
|
|
from ....core.config_manager import ConfigManager
|
|
from ....plugins.translate.scheduler import TranslationScheduler
|
|
from ....schemas.translate import ScheduleConfig, ScheduleResponse
|
|
|
|
from ._router import router
|
|
|
|
|
|
# ============================================================
|
|
# Schedule
|
|
# ============================================================
|
|
|
|
# #region get_schedule [TYPE Function]
|
|
# @BRIEF Get the schedule for a translation job.
|
|
# @PRE: User has translate.schedule.view permission.
|
|
# @POST: Returns the schedule configuration.
|
|
@router.get("/jobs/{job_id}/schedule")
|
|
async def get_schedule(
|
|
job_id: str,
|
|
current_user: User = Depends(get_current_user),
|
|
_ = Depends(has_permission("translate.schedule", "VIEW")),
|
|
db: Session = Depends(get_db),
|
|
config_manager: ConfigManager = Depends(get_config_manager),
|
|
):
|
|
"""Get schedule for a translation job."""
|
|
logger.info(f"[translate_routes][get_schedule] Job: {job_id}, User: {current_user.username}")
|
|
try:
|
|
scheduler = TranslationScheduler(db, config_manager, current_user.username)
|
|
schedule = scheduler.get_schedule(job_id)
|
|
return {
|
|
"id": schedule.id,
|
|
"job_id": schedule.job_id,
|
|
"cron_expression": schedule.cron_expression,
|
|
"timezone": schedule.timezone,
|
|
"is_active": schedule.is_active,
|
|
"last_run_at": schedule.last_run_at.isoformat() if schedule.last_run_at else None,
|
|
"next_run_at": schedule.next_run_at.isoformat() if schedule.next_run_at else None,
|
|
"created_by": schedule.created_by,
|
|
"created_at": schedule.created_at.isoformat() if schedule.created_at else None,
|
|
"updated_at": schedule.updated_at.isoformat() if schedule.updated_at else None,
|
|
}
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
|
|
# #endregion get_schedule
|
|
|
|
|
|
# #region set_schedule [TYPE Function]
|
|
# @BRIEF Set or update the schedule for a translation job.
|
|
# @PRE: User has translate.schedule.manage permission.
|
|
# @POST: Schedule is created or updated.
|
|
@router.put("/jobs/{job_id}/schedule")
|
|
async def set_schedule(
|
|
job_id: str,
|
|
payload: ScheduleConfig,
|
|
current_user: User = Depends(get_current_user),
|
|
_ = Depends(has_permission("translate.schedule", "MANAGE")),
|
|
db: Session = Depends(get_db),
|
|
config_manager: ConfigManager = Depends(get_config_manager),
|
|
):
|
|
"""Set or update schedule for a translation job."""
|
|
logger.info(f"[translate_routes][set_schedule] Job: {job_id}, User: {current_user.username}")
|
|
try:
|
|
scheduler = TranslationScheduler(db, config_manager, current_user.username)
|
|
# Check if schedule already exists
|
|
from ...models.translate import TranslationSchedule
|
|
existing = db.query(TranslationSchedule).filter(
|
|
TranslationSchedule.job_id == job_id
|
|
).first()
|
|
if existing:
|
|
schedule = scheduler.update_schedule(
|
|
job_id,
|
|
cron_expression=payload.cron_expression,
|
|
timezone=payload.timezone,
|
|
is_active=payload.is_active,
|
|
)
|
|
else:
|
|
schedule = scheduler.create_schedule(
|
|
job_id,
|
|
cron_expression=payload.cron_expression,
|
|
timezone=payload.timezone,
|
|
is_active=payload.is_active,
|
|
)
|
|
# Register with APScheduler via SchedulerService
|
|
from ...dependencies import get_scheduler_service
|
|
sched_svc = get_scheduler_service()
|
|
sched_svc.add_translation_job(
|
|
schedule_id=schedule.id,
|
|
job_id=job_id,
|
|
cron_expression=schedule.cron_expression,
|
|
timezone=schedule.timezone,
|
|
)
|
|
return {
|
|
"id": schedule.id,
|
|
"job_id": schedule.job_id,
|
|
"cron_expression": schedule.cron_expression,
|
|
"timezone": schedule.timezone,
|
|
"is_active": schedule.is_active,
|
|
"last_run_at": schedule.last_run_at.isoformat() if schedule.last_run_at else None,
|
|
"created_by": schedule.created_by,
|
|
"created_at": schedule.created_at.isoformat() if schedule.created_at else None,
|
|
"updated_at": schedule.updated_at.isoformat() if schedule.updated_at else None,
|
|
}
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
|
# #endregion set_schedule
|
|
|
|
|
|
# #region enable_schedule [TYPE Function]
|
|
# @BRIEF Enable a schedule for a translation job.
|
|
# @PRE: User has translate.schedule.manage permission.
|
|
# @POST: Schedule is enabled.
|
|
@router.post("/jobs/{job_id}/schedule/enable")
|
|
async def enable_schedule(
|
|
job_id: str,
|
|
current_user: User = Depends(get_current_user),
|
|
_ = Depends(has_permission("translate.schedule", "MANAGE")),
|
|
db: Session = Depends(get_db),
|
|
config_manager: ConfigManager = Depends(get_config_manager),
|
|
):
|
|
"""Enable a schedule for a translation job."""
|
|
logger.info(f"[translate_routes][enable_schedule] Job: {job_id}, User: {current_user.username}")
|
|
try:
|
|
scheduler = TranslationScheduler(db, config_manager, current_user.username)
|
|
schedule = scheduler.set_schedule_active(job_id, is_active=True)
|
|
from ...dependencies import get_scheduler_service
|
|
sched_svc = get_scheduler_service()
|
|
sched_svc.add_translation_job(
|
|
schedule_id=schedule.id,
|
|
job_id=job_id,
|
|
cron_expression=schedule.cron_expression,
|
|
timezone=schedule.timezone,
|
|
)
|
|
return {"status": "enabled", "job_id": job_id}
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
|
|
# #endregion enable_schedule
|
|
|
|
|
|
# #region disable_schedule [TYPE Function]
|
|
# @BRIEF Disable a schedule for a translation job.
|
|
# @PRE: User has translate.schedule.manage permission.
|
|
# @POST: Schedule is disabled.
|
|
@router.post("/jobs/{job_id}/schedule/disable")
|
|
async def disable_schedule(
|
|
job_id: str,
|
|
current_user: User = Depends(get_current_user),
|
|
_ = Depends(has_permission("translate.schedule", "MANAGE")),
|
|
db: Session = Depends(get_db),
|
|
config_manager: ConfigManager = Depends(get_config_manager),
|
|
):
|
|
"""Disable a schedule for a translation job."""
|
|
logger.info(f"[translate_routes][disable_schedule] Job: {job_id}, User: {current_user.username}")
|
|
try:
|
|
scheduler = TranslationScheduler(db, config_manager, current_user.username)
|
|
scheduler.set_schedule_active(job_id, is_active=False)
|
|
from ...dependencies import get_scheduler_service
|
|
sched_svc = get_scheduler_service()
|
|
sched_svc.remove_translation_job(schedule_id=job_id)
|
|
return {"status": "disabled", "job_id": job_id}
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
|
|
# #endregion disable_schedule
|
|
|
|
|
|
# #region delete_schedule [TYPE Function]
|
|
# @BRIEF Delete the schedule for a translation job.
|
|
# @PRE: User has translate.schedule.manage permission.
|
|
# @POST: Schedule is removed.
|
|
@router.delete("/jobs/{job_id}/schedule", status_code=status.HTTP_204_NO_CONTENT)
|
|
async def delete_schedule(
|
|
job_id: str,
|
|
current_user: User = Depends(get_current_user),
|
|
_ = Depends(has_permission("translate.schedule", "MANAGE")),
|
|
db: Session = Depends(get_db),
|
|
config_manager: ConfigManager = Depends(get_config_manager),
|
|
):
|
|
"""Delete schedule for a translation job."""
|
|
logger.info(f"[translate_routes][delete_schedule] Job: {job_id}, User: {current_user.username}")
|
|
try:
|
|
scheduler = TranslationScheduler(db, config_manager, current_user.username)
|
|
scheduler.delete_schedule(job_id)
|
|
from ...dependencies import get_scheduler_service
|
|
sched_svc = get_scheduler_service()
|
|
sched_svc.remove_translation_job(schedule_id=job_id)
|
|
return None
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
|
|
# #endregion delete_schedule
|
|
|
|
|
|
# #region get_next_executions [TYPE Function]
|
|
# @BRIEF Preview next N executions for a job's schedule.
|
|
# @PRE: User has translate.schedule.view permission.
|
|
# @POST: Returns next execution times.
|
|
@router.get("/jobs/{job_id}/schedule/next-executions")
|
|
async def get_next_executions(
|
|
job_id: str,
|
|
n: int = Query(3, ge=1, le=10),
|
|
current_user: User = Depends(get_current_user),
|
|
_ = Depends(has_permission("translate.schedule", "VIEW")),
|
|
db: Session = Depends(get_db),
|
|
config_manager: ConfigManager = Depends(get_config_manager),
|
|
):
|
|
"""Preview next N executions for a job's schedule."""
|
|
logger.info(f"[translate_routes][get_next_executions] Job: {job_id}, User: {current_user.username}")
|
|
try:
|
|
scheduler = TranslationScheduler(db, config_manager, current_user.username)
|
|
schedule = scheduler.get_schedule(job_id)
|
|
next_times = TranslationScheduler.get_next_executions(
|
|
schedule.cron_expression, schedule.timezone, n=n
|
|
)
|
|
return {
|
|
"job_id": job_id,
|
|
"cron_expression": schedule.cron_expression,
|
|
"timezone": schedule.timezone,
|
|
"next_executions": next_times,
|
|
}
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
|
|
# #endregion get_next_executions
|
|
|
|
# #endregion TranslateScheduleRoutesModule
|