#!/usr/bin/env python3 """ ui.py - Main Application Entry Point and FastAPI App Initialization Orchestrates all components and initializes the FastAPI application """ import asyncio import logging from typing import Dict, Any, Optional from pathlib import Path from fastapi import FastAPI, WebSocket from fastapi.middleware.cors import CORSMiddleware import uvicorn from dotenv import load_dotenv # Import application modules from db import DatabaseManager from utils import load_config, setup_logging from main import BinanceDataCollector # Import UI modules from ui_models import serialize_for_json from ui_routes import APIRoutes from ui_websocket import handle_websocket_connection, broadcast_status_updates, websocket_connections from ui_state import state_manager, get_current_status # Load environment variables load_dotenv('variables.env') # Setup logging setup_logging() logger = logging.getLogger(__name__) # Global application components app = FastAPI( title="Crypto Trading Data Collector", version="3.1.0", description="Real-time cryptocurrency market data collection and analysis platform" ) db_manager: Optional[DatabaseManager] = None data_collector: Optional[BinanceDataCollector] = None config: Dict[str, Any] = {} api_routes: Optional[APIRoutes] = None # Add CORS middleware app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.on_event("startup") async def startup_event(): """Initialize application on startup""" global db_manager, data_collector, config, api_routes try: logger.info("=" * 80) logger.info("Starting Crypto Trading Data Collector v3.1.0") logger.info("=" * 80) # Load configuration config = load_config() logger.info("✓ Configuration loaded successfully") # Initialize database db_manager = DatabaseManager() await db_manager.initialize() logger.info("✓ Database initialized successfully") # Initialize data collector data_collector = BinanceDataCollector() await data_collector.initialize() logger.info("✓ Data collector initialized successfully") # Initialize API routes api_routes = APIRoutes( app, db_manager, data_collector, config, state_manager ) logger.info("✓ API routes registered successfully") # Restore collection state if it was running before reload if state_manager.get("is_collecting", False): logger.info("Restoring collection state from persistent storage...") try: await data_collector.start_continuous_collection() logger.info("✓ Collection state restored successfully") except Exception as e: logger.error(f"✗ Error restoring collection state: {e}") state_manager.update(is_collecting=False) # Start WebSocket broadcaster async def status_getter(): return await get_current_status(db_manager, data_collector, config) asyncio.create_task(broadcast_status_updates(status_getter)) logger.info("✓ WebSocket broadcaster started") logger.info("=" * 80) logger.info("FastAPI application startup complete - Ready to serve requests") logger.info("=" * 80) except Exception as e: logger.error("=" * 80) logger.error(f"FATAL ERROR during startup: {e}", exc_info=True) logger.error("=" * 80) raise @app.on_event("shutdown") async def shutdown_event(): """Clean shutdown""" global db_manager, data_collector try: logger.info("=" * 80) logger.info("Shutting down Crypto Trading Data Collector") logger.info("=" * 80) # Save current state before shutdown if data_collector: state_manager.update( is_collecting=data_collector.is_collecting if hasattr(data_collector, 'is_collecting') else False, websocket_collection_running=data_collector.websocket_collection_running if hasattr(data_collector, 'websocket_collection_running') else False ) logger.info("✓ State saved") # Close database connections if db_manager: try: await db_manager.close() logger.info("✓ Database connections closed") except Exception as e: logger.error(f"✗ Error closing database: {e}") logger.info("=" * 80) logger.info("Shutdown complete") logger.info("=" * 80) except Exception as e: logger.error(f"Error during shutdown: {e}", exc_info=True) @app.websocket("/ws") async def websocket_endpoint(websocket: WebSocket): """WebSocket endpoint for real-time updates""" await handle_websocket_connection(websocket) def main(): """Main entry point for running the application""" import os # Get configuration from environment or use defaults host = os.getenv("WEB_HOST", "0.0.0.0") port = int(os.getenv("WEB_PORT", "8000")) reload = os.getenv("WEB_RELOAD", "False").lower() == "true" logger.info(f"Starting server on {host}:{port} (reload={reload})") uvicorn.run( "ui:app", host=host, port=port, reload=reload, log_level="info" ) if __name__ == "__main__": main()