eight-hourr/backend/cli.py

196 lines
6.2 KiB
Python
Raw Permalink Normal View History

2026-07-17 06:55:36 +00:00
from __future__ import annotations
import argparse
import asyncio
import json
import sys
from datetime import date
from pathlib import Path
from services.date_parser import parse_dates_file, parse_dates_text
from services.fill_service import FillService
def _print_json(data: object) -> None:
print(json.dumps(data, ensure_ascii=False, indent=2))
def _resolve_dates(args: argparse.Namespace) -> list[date]:
if args.file:
return parse_dates_file(args.file)
if args.dates:
return parse_dates_text(args.dates)
raise ValueError("Provide --file or --dates")
async def cmd_status(_: argparse.Namespace) -> int:
service = FillService()
_print_json(await service.get_status())
return 0
async def cmd_teams_test(args: argparse.Namespace) -> int:
"""Standalone Teams calendar fetch test (no PTS / GitLab write)."""
from services.teams_calendar_scraper import TeamsCalendarScraper
start = date.fromisoformat(args.start) if args.start else date.today()
end = date.fromisoformat(args.end) if args.end else start
scraper = TeamsCalendarScraper()
result = await scraper.list_meetings(
start,
end,
skip_holidays=not args.include_holidays,
debug=True,
)
_print_json(result)
if result.get("error"):
return 1
if (result.get("total_count") or 0) == 0:
print(
"\nHINT: 0 meetings. Login Teams via Web UI Settings (MFA), then retry.",
file=sys.stderr,
)
return 2
return 0
async def cmd_preview(args: argparse.Namespace) -> int:
service = FillService()
start = date.fromisoformat(args.start)
end = date.fromisoformat(args.end)
_print_json(await service.preview_plan(start, end, use_llm=not args.no_llm))
return 0
async def cmd_preview_dates(args: argparse.Namespace) -> int:
service = FillService()
days = _resolve_dates(args)
_print_json(await service.preview_plan_for_days(days, use_llm=not args.no_llm))
return 0
async def cmd_fill_today(args: argparse.Namespace) -> int:
service = FillService()
try:
service.ensure_ready()
except ValueError as exc:
print(f"ERROR: {exc}", file=sys.stderr)
return 1
result = await service.fill_today(dry_run=args.dry_run, use_llm=not args.no_llm)
_print_json(result)
return 0 if result.get("ok") else 1
async def cmd_fill_range(args: argparse.Namespace) -> int:
service = FillService()
try:
service.ensure_ready()
except ValueError as exc:
print(f"ERROR: {exc}", file=sys.stderr)
return 1
start = date.fromisoformat(args.start)
end = date.fromisoformat(args.end)
result = await service.fill_dates(
start,
end,
dry_run=args.dry_run,
skip_existing=not args.force,
use_llm=not args.no_llm,
)
_print_json(result)
return 0 if result.get("ok") else 1
async def cmd_fill_dates(args: argparse.Namespace) -> int:
service = FillService()
try:
service.ensure_ready()
except ValueError as exc:
print(f"ERROR: {exc}", file=sys.stderr)
return 1
days = _resolve_dates(args)
result = await service.fill_date_list(
days,
dry_run=args.dry_run,
skip_existing=not args.force,
use_llm=not args.no_llm,
)
_print_json(result)
return 0 if result.get("ok") else 1
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="PTS auto-fill CLI")
sub = parser.add_subparsers(dest="command", required=True)
sub.add_parser("status", help="Show system status")
preview = sub.add_parser("preview", help="Preview fill plan for date range")
preview.add_argument("--start", required=True, help="YYYY-MM-DD")
preview.add_argument("--end", required=True, help="YYYY-MM-DD")
preview.add_argument("--no-llm", action="store_true")
preview_dates = sub.add_parser("preview-dates", help="Preview fill plan for date list")
preview_dates.add_argument("--file", help="Path to dates.txt")
preview_dates.add_argument("--dates", help="Comma/newline separated dates")
preview_dates.add_argument("--no-llm", action="store_true")
fill_today = sub.add_parser("fill-today", help="Fill today's PTS entries")
fill_today.add_argument("--dry-run", action="store_true")
fill_today.add_argument("--no-llm", action="store_true")
fill_range = sub.add_parser("fill-range", help="Fill PTS entries for date range")
fill_range.add_argument("--start", required=True, help="YYYY-MM-DD")
fill_range.add_argument("--end", required=True, help="YYYY-MM-DD")
fill_range.add_argument("--dry-run", action="store_true")
fill_range.add_argument("--force", action="store_true")
fill_range.add_argument("--no-llm", action="store_true")
fill_dates = sub.add_parser("fill-dates", help="Fill PTS entries for explicit date list")
fill_dates.add_argument("--file", help="Path to dates.txt")
fill_dates.add_argument("--dates", help="Comma/newline separated dates")
fill_dates.add_argument("--dry-run", action="store_true")
fill_dates.add_argument("--force", action="store_true")
fill_dates.add_argument("--no-llm", action="store_true")
teams_test = sub.add_parser(
"teams-test",
help="Standalone test: fetch Teams/Outlook meetings only (no PTS write)",
)
teams_test.add_argument("--start", help="YYYY-MM-DD (default: today)")
teams_test.add_argument("--end", help="YYYY-MM-DD (default: start)")
teams_test.add_argument(
"--include-holidays",
action="store_true",
help="Do not skip weekends / TW holidays",
)
return parser
async def main() -> int:
parser = build_parser()
args = parser.parse_args()
handlers = {
"status": cmd_status,
"preview": cmd_preview,
"preview-dates": cmd_preview_dates,
"fill-today": cmd_fill_today,
"fill-range": cmd_fill_range,
"fill-dates": cmd_fill_dates,
"teams-test": cmd_teams_test,
}
try:
return await handlers[args.command](args)
except ValueError as exc:
print(f"ERROR: {exc}", file=sys.stderr)
return 1
if __name__ == "__main__":
raise SystemExit(asyncio.run(main()))