"""DeltaMind FastAPI Application - Main Entry Point.""" import logging, json, asyncio from pathlib import Path from typing import Optional from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Query from fastapi.staticfiles import StaticFiles from fastapi.responses import HTMLResponse from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel from apscheduler.schedulers.asyncio import AsyncIOScheduler from datetime import datetime from app.core.config import settings, ensure_directories from app.core.database import (init_database, init_chromadb, seed_knowledge_base, get_recent_alerts, get_dashboard_stats, get_production_data, get_chat_history, insert_alert) from app.llm_router import llm_router from app.rag_engine import rag_engine from app.data_sources.firms import firms_client from app.data_sources.nosdra import nosdra_client from app.data_sources.weather import weather_client from app.data_sources.satellite import satellite_client from app.models.anomaly import anomaly_detector from app.models.predictive import predictive_model from app.reports.generator import report_generator from app.seed_data import seed_production from app.services.notifications import send_critical_alert_email logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(name)s] %(levelname)s: %(message)s") logger = logging.getLogger("deltamind") app = FastAPI(title="DeltaMind", version="3.0.0", description="AI-Powered Operational Intelligence - Renaissance Africa Energy") app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]) scheduler = AsyncIOScheduler() class ChatRequest(BaseModel): query: str session_id: Optional[str] = None class AnomalyRequest(BaseModel): oil_rate: float pressure: float water_cut: float temperature: float class PredictRequest(BaseModel): id: str = "EQ-001" type: str = "ESP" vibration: float = 0.3 temperature: float = 70 runtime_hours: int = 4000 pressure: float = 150 class ReportRequest(BaseModel): report_type: str = "daily" partner: Optional[str] = None agency: Optional[str] = None @app.on_event("startup") async def startup(): ensure_directories() init_database() init_chromadb() seed_knowledge_base() scheduler.add_job(firms_client.summary, 'interval', hours=6) scheduler.add_job(weather_client.all_locations, 'interval', hours=1) scheduler.start() logger.info("DeltaMind started with background schedulers active") frontend_path = Path(__file__).parent.parent / "frontend" if frontend_path.exists(): app.mount("/static", StaticFiles(directory=str(frontend_path)), name="static") @app.get("/", response_class=HTMLResponse) async def root(): idx = frontend_path / "index.html" if idx.exists(): # FIXED: Added encoding="utf-8" to prevent Windows charmap decoding errors return HTMLResponse(idx.read_text(encoding="utf-8")) return HTMLResponse("
Frontend not found.
") @app.get("/api/health") async def health(): return {"status": "ok", "version": settings.app_version, "llm": llm_router.status(), "timestamp": datetime.now().isoformat()} @app.get("/api/llm/status") async def llm_status(): return llm_router.status() @app.get("/api/dashboard") async def dashboard(): stats = get_dashboard_stats() flares_summary = firms_client.summary() spill_summary = nosdra_client.summary() corridors = satellite_client.corridor_status() fleet = predictive_model.fleet_health() weather = weather_client.all_locations() return {"stats": stats, "flares": flares_summary, "spills": spill_summary, "corridors": corridors, "fleet": fleet, "weather": weather} @app.get("/api/alerts") async def alerts(limit: int = 50): return {"alerts": get_recent_alerts(limit)} @app.get("/api/flares") async def flares(): df = firms_client.get_fires(3) processed = firms_client.process(df) firms_client.store(processed) return {"flares": processed, "count": len(processed)} @app.get("/api/spills") async def spills(): return {"spills": nosdra_client.get_spills()} @app.get("/api/weather") async def weather(): data = weather_client.all_locations() risks = [weather_client.risk(w) for w in data] return {"weather": data, "risks": risks} @app.get("/api/satellite/corridors") async def sat_corridors(): return {"corridors": satellite_client.corridor_status()} @app.get("/api/production") async def production(oml_id: Optional[str] = None, limit: int = 200): return {"data": get_production_data(oml_id, limit)} @app.post("/api/ai/chat") async def chat(req: ChatRequest): return await rag_engine.chat(req.query, req.session_id) @app.get("/api/ai/chat/history") async def chat_history(session_id: str): return {"messages": get_chat_history(session_id)} @app.post("/api/ai/anomaly") async def detect_anomaly(req: AnomalyRequest): return anomaly_detector.detect(req.dict()) @app.post("/api/ai/leak") async def detect_leak(flow_in: float = Query(...), flow_out: float = Query(...), pressure_drop: float = Query(0)): return anomaly_detector.pipeline_leak_detection(flow_in, flow_out, pressure_drop) @app.post("/api/ai/predict") async def predict_maintenance(req: PredictRequest): return predictive_model.predict(req.dict()) @app.get("/api/ai/fleet") async def fleet_health(): return predictive_model.fleet_health() @app.post("/api/reports/generate") async def generate_report(req: ReportRequest): if req.report_type == "daily": return await report_generator.daily_production() elif req.report_type == "partner": return await report_generator.partner_report(req.partner or "NNPC") elif req.report_type == "regulatory": return await report_generator.regulatory_filing(req.agency or "NUPRC") return {"error": "Unknown report type"} @app.post("/api/seed") async def seed(): count = seed_production(30) return {"seeded": count, "message": f"Seeded {count} production records"} @app.post("/api/test-alert") async def test_critical_alert(): """Simulate a critical alert to demonstrate push notifications and AI email.""" alert_data = { "alert_type": "security", "severity": "critical", "title": "Pipeline Pressure Drop", "message": "SCADA detected a 15% pressure drop in Segment 4. Possible vandalism or leak.", "source": "SCADA_AI", "latitude": 5.5, "longitude": 5.9, "oml_id": "OML-18" } alert_id = insert_alert(alert_data) alert_data["id"] = alert_id alert_data["created_at"] = datetime.now().isoformat() # Trigger AI Email Dispatch await send_critical_alert_email(alert_data) # Broadcast via WebSocket to all connected dashboards await manager.broadcast({ "type": "critical_alert", "data": alert_data }) return {"status": "success", "message": "Alert created, email drafted, and push notification sent."} class ConnectionManager: def __init__(self): self.active = [] async def connect(self, ws: WebSocket): await ws.accept() self.active.append(ws) def disconnect(self, ws: WebSocket): if ws in self.active: self.active.remove(ws) async def broadcast(self, msg: dict): for ws in self.active: try: await ws.send_json(msg) except: pass manager = ConnectionManager() @app.websocket("/ws/alerts") async def ws_alerts(ws: WebSocket): await manager.connect(ws) try: while True: await asyncio.sleep(30) await ws.send_json({"type": "ping", "message": "DeltaMind connection active"}) except WebSocketDisconnect: manager.disconnect(ws)