Spaces:
Sleeping
Sleeping
| """SQLite + ChromaDB initialization and management.""" | |
| import sqlite3, json, logging | |
| from pathlib import Path | |
| from datetime import datetime | |
| from typing import List, Dict, Any | |
| from contextlib import contextmanager | |
| from app.core.config import settings | |
| logger = logging.getLogger("deltamind.db") | |
| DB_PATH = settings.data_dir / "deltamind.db" | |
| def init_database(): | |
| """Initialize SQLite database with operational tables.""" | |
| Path(settings.data_dir).mkdir(parents=True, exist_ok=True) | |
| with get_db() as conn: | |
| c = conn.cursor() | |
| c.execute("""CREATE TABLE IF NOT EXISTS flare_detections ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, latitude REAL, longitude REAL, | |
| brightness_temp REAL, frp REAL, confidence REAL, satellite TEXT, | |
| detection_time TEXT, oml_id TEXT, severity TEXT DEFAULT 'medium', | |
| created_at TEXT DEFAULT CURRENT_TIMESTAMP)""") | |
| c.execute("""CREATE TABLE IF NOT EXISTS spill_reports ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, latitude REAL, longitude REAL, | |
| spill_source TEXT, spill_cause TEXT, spill_volume REAL, status TEXT, | |
| report_date TEXT, oml_id TEXT, severity TEXT DEFAULT 'medium', | |
| created_at TEXT DEFAULT CURRENT_TIMESTAMP)""") | |
| c.execute("""CREATE TABLE IF NOT EXISTS anomaly_detections ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, source TEXT, anomaly_type TEXT, | |
| latitude REAL, longitude REAL, severity_score REAL, description TEXT, | |
| raw_data TEXT, status TEXT DEFAULT 'active', | |
| created_at TEXT DEFAULT CURRENT_TIMESTAMP)""") | |
| c.execute("""CREATE TABLE IF NOT EXISTS maintenance_predictions ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, equipment_id TEXT, equipment_type TEXT, | |
| oml_id TEXT, failure_probability REAL, predicted_days_to_failure INTEGER, | |
| recommended_action TEXT, severity TEXT, status TEXT DEFAULT 'pending', | |
| created_at TEXT DEFAULT CURRENT_TIMESTAMP)""") | |
| c.execute("""CREATE TABLE IF NOT EXISTS chat_history ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT, role TEXT, | |
| content TEXT, provider TEXT, metadata TEXT, | |
| created_at TEXT DEFAULT CURRENT_TIMESTAMP)""") | |
| c.execute("""CREATE TABLE IF NOT EXISTS production_data ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, oml_id TEXT, well_id TEXT, | |
| field_name TEXT, oil_rate_bpd REAL, gas_rate_mmscfd REAL, | |
| water_cut_pct REAL, wellhead_pressure_psi REAL, temperature_f REAL, | |
| status TEXT, record_date TEXT, created_at TEXT DEFAULT CURRENT_TIMESTAMP)""") | |
| c.execute("""CREATE TABLE IF NOT EXISTS alerts ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, alert_type TEXT, severity TEXT, | |
| title TEXT, message TEXT, source TEXT, latitude REAL, longitude REAL, | |
| oml_id TEXT, acknowledged INTEGER DEFAULT 0, | |
| created_at TEXT DEFAULT CURRENT_TIMESTAMP)""") | |
| conn.commit() | |
| logger.info("SQLite Database initialized successfully") | |
| def get_db(): | |
| conn = sqlite3.connect(str(DB_PATH)) | |
| conn.row_factory = sqlite3.Row | |
| try: | |
| yield conn | |
| finally: | |
| conn.close() | |
| def insert_flare(data: Dict) -> int: | |
| with get_db() as conn: | |
| c = conn.cursor() | |
| c.execute("INSERT INTO flare_detections (latitude,longitude,brightness_temp,frp,confidence,satellite,detection_time,oml_id,severity) VALUES (?,?,?,?,?,?,?,?,?)", | |
| (data.get("latitude"),data.get("longitude"),data.get("brightness_temp"), | |
| data.get("frp"),data.get("confidence"),data.get("satellite"), | |
| data.get("detection_time"),data.get("oml_id"),data.get("severity","medium"))) | |
| conn.commit() | |
| return c.lastrowid | |
| def insert_alert(data: Dict) -> int: | |
| with get_db() as conn: | |
| c = conn.cursor() | |
| c.execute("INSERT INTO alerts (alert_type,severity,title,message,source,latitude,longitude,oml_id) VALUES (?,?,?,?,?,?,?,?)", | |
| (data.get("alert_type"),data.get("severity"),data.get("title"), | |
| data.get("message"),data.get("source"),data.get("latitude"), | |
| data.get("longitude"),data.get("oml_id"))) | |
| conn.commit() | |
| return c.lastrowid | |
| def insert_production(data: Dict) -> int: | |
| with get_db() as conn: | |
| c = conn.cursor() | |
| c.execute("INSERT INTO production_data (oml_id,well_id,field_name,oil_rate_bpd,gas_rate_mmscfd,water_cut_pct,wellhead_pressure_psi,temperature_f,status,record_date) VALUES (?,?,?,?,?,?,?,?,?,?)", | |
| (data.get("oml_id"),data.get("well_id"),data.get("field_name"), | |
| data.get("oil_rate_bpd"),data.get("gas_rate_mmscfd"),data.get("water_cut_pct"), | |
| data.get("wellhead_pressure_psi"),data.get("temperature_f"), | |
| data.get("status","active"),data.get("record_date"))) | |
| conn.commit() | |
| return c.lastrowid | |
| def get_recent_alerts(limit: int = 50) -> List[Dict]: | |
| with get_db() as conn: | |
| rows = conn.cursor().execute("SELECT * FROM alerts ORDER BY created_at DESC LIMIT ?", (limit,)).fetchall() | |
| return [dict(r) for r in rows] | |
| def get_production_data(oml_id: str = None, limit: int = 100) -> List[Dict]: | |
| with get_db() as conn: | |
| if oml_id: | |
| rows = conn.cursor().execute("SELECT * FROM production_data WHERE oml_id=? ORDER BY record_date DESC LIMIT ?", (oml_id, limit)).fetchall() | |
| else: | |
| rows = conn.cursor().execute("SELECT * FROM production_data ORDER BY record_date DESC LIMIT ?", (limit,)).fetchall() | |
| return [dict(r) for r in rows] | |
| def save_chat(session_id: str, role: str, content: str, provider: str = "", metadata: dict = None): | |
| with get_db() as conn: | |
| conn.cursor().execute("INSERT INTO chat_history (session_id,role,content,provider,metadata) VALUES (?,?,?,?,?)", | |
| (session_id, role, content, provider, json.dumps(metadata or {}))) | |
| conn.commit() | |
| def get_chat_history(session_id: str, limit: int = 20) -> List[Dict]: | |
| with get_db() as conn: | |
| rows = conn.cursor().execute("SELECT * FROM chat_history WHERE session_id=? ORDER BY created_at DESC LIMIT ?", (session_id, limit)).fetchall() | |
| return [dict(r) for r in rows] | |
| def get_dashboard_stats() -> Dict: | |
| with get_db() as conn: | |
| c = conn.cursor() | |
| return { | |
| "total_alerts": c.execute("SELECT COUNT(*) FROM alerts").fetchone()[0], | |
| "active_alerts": c.execute("SELECT COUNT(*) FROM alerts WHERE acknowledged=0").fetchone()[0], | |
| "critical_alerts": c.execute("SELECT COUNT(*) FROM alerts WHERE severity='critical'").fetchone()[0], | |
| "total_flares": c.execute("SELECT COUNT(*) FROM flare_detections").fetchone()[0], | |
| "total_anomalies": c.execute("SELECT COUNT(*) FROM anomaly_detections").fetchone()[0], | |
| "active_wells": c.execute("SELECT COUNT(DISTINCT well_id) FROM production_data WHERE status='active'").fetchone()[0], | |
| } | |
| # ── ChromaDB RAG Setup ── | |
| _chroma_client = None | |
| _chroma_collection = None | |
| def init_chromadb(): | |
| global _chroma_client, _chroma_collection | |
| try: | |
| import chromadb | |
| from chromadb.config import Settings as CS | |
| Path(settings.chroma_persist_dir).mkdir(parents=True, exist_ok=True) | |
| _chroma_client = chromadb.PersistentClient(path=settings.chroma_persist_dir, settings=CS(anonymized_telemetry=False)) | |
| _chroma_collection = _chroma_client.get_or_create_collection(name=settings.chroma_collection) | |
| logger.info(f"ChromaDB initialized: {_chroma_collection.count()} documents") | |
| except Exception as e: | |
| logger.error(f"ChromaDB init failed: {e}") | |
| def add_documents(documents: List[str], metadatas: List[Dict], ids: List[str]): | |
| global _chroma_collection | |
| if _chroma_collection is None: init_chromadb() | |
| if _chroma_collection: | |
| _chroma_collection.upsert(documents=documents, metadatas=metadatas, ids=ids) | |
| def query_documents(query: str, n_results: int = 5) -> Dict: | |
| global _chroma_collection | |
| if _chroma_collection is None: init_chromadb() | |
| if not _chroma_collection: return {"documents":[],"metadatas":[],"distances":[]} | |
| try: | |
| return _chroma_collection.query(query_texts=[query], n_results=n_results) | |
| except Exception as e: | |
| logger.error(f"ChromaDB Query failed: {e}") | |
| return {"documents":[],"metadatas":[],"distances":[]} | |
| def seed_knowledge_base(): | |
| """Seed initial SOPs and regulations into Vector DB.""" | |
| docs = [ | |
| "Renaissance Africa Energy operates 18 Oil Mining Leases across onshore, swamp, and shallow water terrains in the Niger Delta. Manages Bonny and Forcados terminals and Sea Eagle FPSO.", | |
| "SOP for ESP failure: 1) Confirm alarm via SCADA. 2) Check vibration and temperature. 3) If vibration >0.5 in/s, shutdown. 4) Create work order. 5) Notify supervisor. 6) Schedule workover within 5 days.", | |
| "Pipeline integrity: Monthly aerial surveillance. SAR satellite for ground disturbance. Report unauthorized excavation within 50m of ROW immediately.", | |
| "Gas flaring: NUPRC mandates max 5% flaring. Renaissance targets zero routine flaring by 2028. All flaring reported within 24 hours.", | |
| "Daily production reports by 0700. Include oil rate bpd, gas rate MMSCF/D, water cut %, wellhead pressure, deferred volumes.", | |
| "Security alert: Pipeline breach detected: 1) Alert security. 2) Dispatch patrol. 3) Notify ops manager. 4) GPS + photos. 5) Incident report within 4hrs. 6) Notify partners if loss >100 bpd.", | |
| "Niger Delta: swamps, creeks, mangroves. Rainy season April-October limits access. Many locations boat-only. Community engagement critical.", | |
| "Maintenance: ESP every 12 months. Gas compressors every 6 months. Pipeline assessment annually. Corrosion quarterly. All tracked in SAP PM.", | |
| "Partner reporting: NNPC monthly allocation. TotalEnergies quarterly JV. AENR annual review. Include volumes, costs, HSE stats.", | |
| "HSE: Level 1 Minor, Level 2 Serious, Level 3 Major (>100bbl spill), Level 4 Critical. Level 3+ reported to NUPRC within 24hrs.", | |
| "Emergency response: In case of major spill, activate Tier 2 response. Deploy boom within 2 hours. Notify NOSDRA. Begin wildlife assessment. Coordinate with community leaders.", | |
| ] | |
| categories = ["general","maintenance","integrity","compliance","operations","security","general","maintenance","commercial","hse","hse"] | |
| metas = [{"source":f"doc_{i}","category":categories[i % len(categories)]} for i in range(len(docs))] | |
| ids = [f"kb_{i}" for i in range(len(docs))] | |
| add_documents(docs, metas, ids) | |
| logger.info("Knowledge base seeded.") | |