""" FocusTrack - Main Entry Point Starts the tracker service and optionally the dashboard. """ import sys import os import argparse import threading import subprocess import logging from pathlib import Path # Add project root to path ROOT = Path(__file__).parent sys.path.insert(0, str(ROOT)) # ── Create required directories BEFORE logging setup ────────────────────────── (ROOT / "logs").mkdir(exist_ok=True) (ROOT / "data").mkdir(exist_ok=True) from tracker import ActivityTracker from database import Database logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", handlers=[ logging.FileHandler(ROOT / "logs" / "focustrack.log"), logging.StreamHandler(), ], ) logger = logging.getLogger("focustrack.main") def start_dashboard(): """Launch the Streamlit dashboard in a subprocess.""" ui_path = Path(__file__).parent / "app.py" cmd = [sys.executable, "-m", "streamlit", "run", str(ui_path), "--server.port", "8501", "--server.headless", "true", "--server.address", "localhost", "--browser.gatherUsageStats", "false"] logger.info("Starting FocusTrack Dashboard at http://localhost:8501") proc = subprocess.Popen(cmd) return proc def start_tray(tracker: ActivityTracker): """Start system tray icon (optional, graceful fallback).""" try: from tray import TrayApp tray = TrayApp(tracker) tray.run() except Exception as e: logger.warning(f"System tray unavailable: {e}. Running headless.") def main(): parser = argparse.ArgumentParser(description="FocusTrack - Local Activity Tracker") parser.add_argument("--no-tray", action="store_true", help="Disable system tray") parser.add_argument("--no-dashboard", action="store_true", help="Don't open browser dashboard") parser.add_argument("--tracker-only", action="store_true", help="Run tracker without dashboard") args = parser.parse_args() # Initialize database db = Database() db.initialize() logger.info("Database initialized") # Start tracker tracker = ActivityTracker(db) tracker_thread = threading.Thread(target=tracker.run, daemon=True) tracker_thread.start() logger.info("Activity tracker started") # Start dashboard dashboard_proc = None if not args.tracker_only and not args.no_dashboard: dashboard_proc = start_dashboard() # Start tray if not args.no_tray: tray_thread = threading.Thread(target=start_tray, args=(tracker,), daemon=True) tray_thread.start() logger.info("FocusTrack is running. Press Ctrl+C to stop.") try: tracker_thread.join() except KeyboardInterrupt: logger.info("Shutting down FocusTrack...") tracker.stop() if dashboard_proc: dashboard_proc.terminate() sys.exit(0) if __name__ == "__main__": main()