Implement S6 doctor and A1 daily-board with fixtures.
Add operator health checks (make doctor) wrapping platform CLIs, and the fixtures-only daily board skill library with unit tests (make verify).
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build a daily board from scheduling fixtures.
|
||||
|
||||
Usage:
|
||||
python build_board.py [--fixtures PATH] [--date YYYY-MM-DD] [--salon NAME] [--format text|json]
|
||||
|
||||
All data is labeled as fixture/offline — never silent fake live data.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
# Ensure the _lib package is importable regardless of cwd.
|
||||
_REPO_ROOT = pathlib.Path(__file__).resolve().parents[3]
|
||||
sys.path.insert(0, str(_REPO_ROOT / "skills" / "_lib"))
|
||||
|
||||
from lumina_skills.providers.scheduling.fixture_provider import (
|
||||
load_fixtures,
|
||||
load_fixture_metadata,
|
||||
)
|
||||
from lumina_skills.board_builder import build_board, format_board_text
|
||||
|
||||
# Default fixture: Claire Bennett sample day.
|
||||
_DEFAULT_FIXTURE = _REPO_ROOT / "data" / "fixtures" / "scheduling" / "claire_bennett_2026-07-28.json"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Build a daily salon board from fixture data.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--fixtures",
|
||||
type=pathlib.Path,
|
||||
default=_DEFAULT_FIXTURE,
|
||||
help="Path to fixture JSON file (default: Claire Bennett sample day).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--date",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Override board date (YYYY-MM-DD). Defaults to fixture date.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--salon",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Override salon name. Defaults to fixture salon_name.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--format",
|
||||
choices=["text", "json"],
|
||||
default="text",
|
||||
help="Output format (default: text).",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Load fixture data.
|
||||
try:
|
||||
appointments = load_fixtures(args.fixtures)
|
||||
except FileNotFoundError as exc:
|
||||
print(f"Error: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
except (json.JSONDecodeError, KeyError, ValueError) as exc:
|
||||
print(f"Error parsing fixture: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
# Load metadata for defaults.
|
||||
try:
|
||||
meta = load_fixture_metadata(args.fixtures)
|
||||
except (json.JSONDecodeError, KeyError, TypeError, ValueError) as exc:
|
||||
print(f"Warning: could not parse fixture metadata: {exc}", file=sys.stderr)
|
||||
meta = {}
|
||||
|
||||
date = args.date or meta.get("date", "unknown")
|
||||
salon_name = args.salon or meta.get("salon_name", "Unknown Salon")
|
||||
business_hours = meta.get("business_hours")
|
||||
|
||||
# Build the board.
|
||||
board = build_board(
|
||||
appointments=appointments,
|
||||
date=date,
|
||||
salon_name=salon_name,
|
||||
source="fixtures",
|
||||
business_hours=business_hours,
|
||||
)
|
||||
|
||||
# Output.
|
||||
if args.format == "json":
|
||||
print(json.dumps(board.to_dict(), indent=2))
|
||||
else:
|
||||
print(format_board_text(board))
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user