import os import sys import argparse from dotenv import load_dotenv from loguru import logger load_dotenv() def setup_logging(log_file: str | None = None) -> None: logger.remove() logger.add( sys.stderr, level="INFO", format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {message}", ) if log_file: log_dir = os.getenv("LOGDIR") if log_dir: logger.add( f"{log_dir}/{log_file}", rotation="7 days", retention="30 days", compression="zip", level="DEBUG", ) def main() -> None: parser = argparse.ArgumentParser( prog="discoverydash", description="Spotify streaming discovery data: playlist, search & radio", ) subparsers = parser.add_subparsers(dest="mode", required=True) # update mode (full pipeline) subparsers.add_parser("update", help="Daily update: tables → hypers → email") # hypers mode (standalone) subparsers.add_parser("hypers", help="Rebuild hyper tables and trigger Tableau refresh") # backfill mode bf = subparsers.add_parser("backfill", help="Backfill a single source from a base date") bf.add_argument("source", choices=["playlist", "search", "radio", "p", "s", "r"]) bf.add_argument("base_date", help="Start date (YYYY-MM-DD)") bf.add_argument("cc_or_uri", nargs="+", help="Country codes (search/radio) or playlist URIs") # backfill-overrides mode bfo = subparsers.add_parser( "backfill-overrides", help="Backfill radio data for all ISRCs in the override table", ) bfo.add_argument("--from", dest="from_date", default=None, help="Start date (YYYY-MM-DD). Defaults to earliest date in radio_main.") parser.add_argument( "--log-file", "-l", type=str, default="discoverydash-cea.log", help="Log file name (stored in LOGDIR)", ) args = parser.parse_args() setup_logging(args.log_file) try: if args.mode == "update": from .services.tables import run_update from .services.hypers import run_hypers from .services.email import send_status_email statuses = run_update() any_updated = any(v is not None for v in statuses.values()) if any_updated: hypers_ok = run_hypers() send_status_email(statuses, hypers_ok) else: logger.info("No sources updated — skipping hypers and email") elif args.mode == "hypers": from .services.hypers import run_hypers run_hypers() elif args.mode == "backfill": from .services.tables import run_backfill run_backfill(args.source, args.base_date, args.cc_or_uri) elif args.mode == "backfill-overrides": from .services.tables import run_backfill_overrides run_backfill_overrides(args.from_date) except KeyboardInterrupt: logger.info("Interrupted by user") sys.exit(130) except SystemExit: raise except Exception as e: logger.exception(f"Fatal error: {e}") sys.exit(1) if __name__ == "__main__": main()