180 lines
5.5 KiB
Python
180 lines
5.5 KiB
Python
#!/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()
|