""" Climate-Resilient Agriculture Platform - Unified Entry Point Optimized for Hugging Face Spaces and single-container deployment Starts ALL services: 1. Pest Prediction API (Port 8000) 2. Weather Intelligence API (Port 8001) 3. Water Management API (Port 8002) 4. Master API Server (Port 7860/8003) 5. Telegram Bot (Background Task) """ import os import sys import asyncio import logging import threading import time from pathlib import Path from dotenv import load_dotenv import uvicorn # Load environment variables load_dotenv() # Setup logging logging.basicConfig( level=logging.INFO, format="%(asctime)s | %(levelname)s | %(name)s | %(message)s", handlers=[logging.StreamHandler(sys.stdout)] ) logger = logging.getLogger("MasterApp") # Ensure required directories exist Path("data").mkdir(exist_ok=True) # Define services to run # Format: (module_name, port_number) SERVICES = [ ("pest:app", 8000), ("weather:app", 8001), ("water:app", 8002) ] def run_service(app_path: str, port: int): """Run a sub-service in its own thread""" try: logger.info(f"[STARTUP] Launching {app_path} on port {port}...") uvicorn.run( app_path, host="127.0.0.1", port=port, log_level="error", # Keep logs clean access_log=False ) except Exception as e: logger.error(f"[ERROR] Service {app_path} failed: {e}") def run_telegram_bot(): """Runs Telegram bot in its own event loop""" try: logger.info("[BOT] Initializing Telegram bot thread...") # Give services time to start up time.sleep(5) loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) from telegram_bot import main as bot_main # If bot_main is async → await it if asyncio.iscoroutinefunction(bot_main): loop.run_until_complete(bot_main()) else: bot_main() except Exception as e: logger.warning(f"[BOT WARNING] Telegram bot failed to initialize: {type(e).__name__}") logger.info(f"[BOT INFO] This is normal if there's no internet/network access") logger.info(f"[BOT INFO] REST APIs will continue working without the bot") # Don't crash the app - just continue without the bot def run_app(): """Starts the full platform""" logger.info("=" * 80) logger.info("🌾 FarmSense MASTER STARTUP") logger.info("=" * 80) # 1. Start sub-services in background threads for app_path, port in SERVICES: t = threading.Thread(target=run_service, args=(app_path, port), daemon=True) t.start() logger.info(f"[STARTUP] {app_path} started in background") # 2. Wait a moment for services to bind time.sleep(2) # 3. Start Telegram bot in background thread bot_thread = threading.Thread( target=run_telegram_bot, name="TelegramBotThread", daemon=True ) bot_thread.start() logger.info("[STARTUP] Telegram Bot started in background") # 4. Run Main Master API on port 7860 (Hugging Face Default) # This remains on the main thread main_port = int(os.getenv("PORT", 7860)) logger.info(f"[STARTUP] Master API starting on port {main_port}...") uvicorn.run( "api_server:app", host="0.0.0.0", port=main_port, log_level="info", access_log=True, reload=False ) if __name__ == "__main__": try: run_app() except KeyboardInterrupt: logger.info("[SHUTDOWN] Graceful shutdown initiated") except Exception as e: logger.exception(f"[FATAL ERROR] {e}") sys.exit(1) finally: logger.info("[EXIT] Application stopped")