You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

102 lines
3.6 KiB
Python

"""In-process daily scheduler for proactive search (CLAUDE.md-compatible, no daemon)."""
from __future__ import annotations
import time
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo
from loguru import logger
from gimme_job.proactive.engine import ProactiveEngine
def next_run_at(
now: datetime,
daily_time: str,
weekly_day: int,
weekly_time: str,
) -> tuple[datetime, str]:
"""Compute the next (datetime, mode) to run, from a timezone-aware now.
daily_time: "HH:MM" — daily pass
weekly_day: weekday int (0=Mon..6=Sun) — weekly deep scan
weekly_time: "HH:MM" — weekly deep scan time (same day)
"""
hh, mm = (int(x) for x in daily_time.split(":"))
daily_base = now.replace(hour=hh, minute=mm, second=0, microsecond=0)
whh, wmm = (int(x) for x in weekly_time.split(":"))
days_ahead = (weekly_day - now.weekday()) % 7
weekly_base = (now + timedelta(days=days_ahead)).replace(
hour=whh, minute=wmm, second=0, microsecond=0
)
candidates = []
if daily_base >= now:
candidates.append((daily_base, "daily"))
if weekly_base >= now:
candidates.append((weekly_base, "weekly"))
# fallbacks: tomorrow
candidates.append((daily_base + timedelta(days=1), "daily"))
candidates.append((weekly_base + timedelta(days=7), "weekly"))
return min(candidates, key=lambda c: c[0])
def run_forever(
global_config,
daily_time: str = "06:00",
weekly_day: int = 6,
weekly_time: str = "08:00",
timezone: str = "America/Phoenix",
dry_run: bool = False,
limit_queries: int | None = None,
) -> None:
"""Loop: sleep until next scheduled time, then run the engine. Ctrl+C to stop."""
tz = ZoneInfo(timezone)
engine = ProactiveEngine(global_config=global_config)
run_count = 0
logger.info(
f"Proactive scheduler: daily {daily_time} ({timezone}), "
f"weekly day={weekly_day} at {weekly_time} — Ctrl+C to stop"
)
try:
while True:
now = datetime.now(tz)
next_dt, mode = next_run_at(now, daily_time, weekly_day, weekly_time)
wait_secs = (next_dt - now).total_seconds()
logger.info(
f"Next {mode} run at {next_dt.strftime('%Y-%m-%d %H:%M %Z')} "
f"(in {wait_secs / 3600:.1f}h)"
)
time.sleep(max(1, wait_secs))
run_count += 1
logger.info(f"── Proactive run #{run_count} ({mode}) ──")
try:
result = engine.run(
mode=mode, dry_run=dry_run, limit_queries=limit_queries
)
logger.info(
f"Run done: NEW={result.new_count} REOPENED={result.reopened_count} "
f"STILL_OPEN={result.still_open_count} VERIFY={result.verify_count}"
)
if result.report_path:
from gimme_job.proactive.html_report import terminal_link
logger.info(
f"Report: {terminal_link(result.report_path, result.report_path)}"
)
if result.role_audit_path:
from gimme_job.proactive.html_report import terminal_link
logger.info(
f"Role audit: "
f"{terminal_link(result.role_audit_path, result.role_audit_path)}"
)
except Exception as e:
logger.error(f"Run #{run_count} failed: {e}")
except KeyboardInterrupt:
logger.info("Proactive scheduler stopped.")