afterhours-shift-manager/src/server/jobs.py

80 lines
2.3 KiB
Python
Raw Normal View History

"""In-process job dispatch. SQS when JOBS_QUEUE_URL is set; otherwise run inline."""
from __future__ import annotations
import importlib.util
import json
import logging
import os
import sys
from pathlib import Path
import boto3
from shared.holiday_flow import activate, deactivate
from shared.schedule import ShiftSchedule
logger = logging.getLogger(__name__)
_SRC = Path(__file__).resolve().parents[1]
_sqs = None
_handlers: dict[str, object] = {}
def _client():
global _sqs
if _sqs is None:
_sqs = boto3.client("sqs")
return _sqs
def _load_lambda_app(dirname: str):
if dirname in _handlers:
return _handlers[dirname]
path = _SRC / dirname / "app.py"
name = f"afterhours_{dirname.replace('-', '_')}"
spec = importlib.util.spec_from_file_location(name, path)
if spec is None or spec.loader is None:
raise ImportError(f"cannot load {path}")
mod = importlib.util.module_from_spec(spec)
sys.modules[name] = mod
spec.loader.exec_module(mod)
_handlers[dirname] = mod
return mod
def enqueue_job(payload: dict) -> None:
queue_url = os.environ.get("JOBS_QUEUE_URL", "").strip()
body = json.dumps(payload, default=str)
if not queue_url:
run_job(payload)
return
_client().send_message(QueueUrl=queue_url, MessageBody=body)
def _run_holiday(payload: dict) -> dict:
action = payload.get("action")
date = payload.get("date")
if not date:
return {"error": True, "reason": "missing_date"}
schedule = ShiftSchedule()
if action == "activate":
return activate(schedule, date)
if action == "deactivate":
return deactivate(schedule, date)
return {"error": True, "reason": "unknown_action", "action": action}
def run_job(payload: dict) -> dict:
event_type = payload.get("event", "")
if event_type == "holiday":
return _run_holiday(payload)
forced = {**payload, "force": True}
if event_type == "weekly_post":
return _load_lambda_app("weekly-post").handler(forced, None)
if event_type == "roster_sync":
return _load_lambda_app("roster-sync").handler(forced, None)
if event_type in {"ring_scheduler_daily", "ring_scheduler_weekend"}:
return _load_lambda_app("ring-scheduler").handler(forced, None)
raise ValueError(f"unknown job event {event_type!r}")