Files
boc/vims-core/core/database.py
T

221 lines
6.5 KiB
Python
Raw Normal View History

"""
VIMS Core — Database Manager
Hanterar databas per instans/ämne.
"""
import sqlite3
import json
from pathlib import Path
from typing import List, Dict, Optional
from datetime import datetime
class VIMSDatabase:
"""
Databas-hanterare för VIMS-instanser.
Usage:
db = VIMSDatabase("atm-monitoring")
db.create_schema()
db.add_detection(atm_id="ATM-001", anomaly_type="skimming", confidence=0.94)
"""
def __init__(self, topic: str, db_dir: str = "data"):
self.topic = topic
self.table_prefix = topic.replace("-", "_")
self.db_path = Path(db_dir) / f"{topic}.db"
self.db_path.parent.mkdir(parents=True, exist_ok=True)
def _connect(self):
"""Skapa databas-anslutning."""
conn = sqlite3.connect(str(self.db_path))
conn.row_factory = sqlite3.Row
return conn
def create_schema(self, anomaly_classes: List[str]):
"""
Skapa databas-schema för ämne.
Args:
anomaly_classes: Lista med anomaliklasser
"""
conn = self._connect()
cursor = conn.cursor()
# Huvudtabell för detektioner
cursor.execute(f"""
CREATE TABLE IF NOT EXISTS {self.table_prefix}_detections (
id INTEGER PRIMARY KEY AUTOINCREMENT,
asset_id TEXT NOT NULL,
image_path TEXT,
anomaly_type TEXT NOT NULL,
confidence REAL,
severity INTEGER,
bbox TEXT,
detected_at DATETIME DEFAULT CURRENT_TIMESTAMP,
verified BOOLEAN DEFAULT FALSE,
status TEXT DEFAULT 'open',
metadata TEXT
)
""")
# Assets-tabell
cursor.execute(f"""
CREATE TABLE IF NOT EXISTS {self.table_prefix}_assets (
id TEXT PRIMARY KEY,
name TEXT,
location TEXT,
status TEXT DEFAULT 'active',
last_check DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
""")
# Alerts-tabell
cursor.execute(f"""
CREATE TABLE IF NOT EXISTS {self.table_prefix}_alerts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
detection_id INTEGER,
severity INTEGER,
message TEXT,
sent_at DATETIME,
status TEXT DEFAULT 'pending',
FOREIGN KEY (detection_id) REFERENCES {self.table_prefix}_detections(id)
)
""")
# Index
cursor.execute(f"""
CREATE INDEX IF NOT EXISTS idx_{self.table_prefix}_detections_asset
ON {self.table_prefix}_detections(asset_id)
""")
cursor.execute(f"""
CREATE INDEX IF NOT EXISTS idx_{self.table_prefix}_detections_type
ON {self.table_prefix}_detections(anomaly_type)
""")
cursor.execute(f"""
CREATE INDEX IF NOT EXISTS idx_{self.table_prefix}_detections_status
ON {self.table_prefix}_detections(status)
""")
conn.commit()
conn.close()
print(f"Schema created for {self.topic}")
def add_detection(
self,
asset_id: str,
anomaly_type: str,
confidence: float,
severity: int,
image_path: Optional[str] = None,
bbox: Optional[List[float]] = None,
metadata: Optional[Dict] = None
) -> int:
"""
Lägg till ny detektion.
Returns:
ID för insatt rad
"""
conn = self._connect()
cursor = conn.cursor()
cursor.execute(f"""
INSERT INTO {self.table_prefix}_detections
(asset_id, image_path, anomaly_type, confidence, severity, bbox, metadata)
VALUES (?, ?, ?, ?, ?, ?, ?)
""", (
asset_id,
image_path,
anomaly_type,
confidence,
severity,
json.dumps(bbox) if bbox else None,
json.dumps(metadata) if metadata else None
))
detection_id = cursor.lastrowid
# Skapa alert om severity >= 4
if severity >= 4:
cursor.execute(f"""
INSERT INTO {self.table_prefix}_alerts
(detection_id, severity, message)
VALUES (?, ?, ?)
""", (
detection_id,
severity,
f"Critical {anomaly_type} detected on {asset_id}"
))
conn.commit()
conn.close()
return detection_id
def get_detections(
self,
asset_id: Optional[str] = None,
status: Optional[str] = None,
limit: int = 100
) -> List[Dict]:
"""Hämta detektioner med filter."""
conn = self._connect()
cursor = conn.cursor()
query = f"SELECT * FROM {self.table_prefix}_detections WHERE 1=1"
params = []
if asset_id:
query += " AND asset_id = ?"
params.append(asset_id)
if status:
query += " AND status = ?"
params.append(status)
query += " ORDER BY detected_at DESC LIMIT ?"
params.append(limit)
cursor.execute(query, params)
rows = cursor.fetchall()
conn.close()
return [dict(row) for row in rows]
def get_stats(self) -> Dict:
"""Hämta statistik för ämne."""
conn = self._connect()
cursor = conn.cursor()
cursor.execute(f"""
SELECT
COUNT(*) as total_detections,
COUNT(DISTINCT asset_id) as assets_monitored,
COUNT(CASE WHEN status = 'open' THEN 1 END) as open_anomalies,
MAX(severity) as max_severity
FROM {self.table_prefix}_detections
""")
row = cursor.fetchone()
conn.close()
return dict(row) if row else {}
def verify_detection(self, detection_id: int, verdict: str, notes: str = ""):
"""Verifiera detektion manuellt."""
conn = self._connect()
cursor = conn.cursor()
cursor.execute(f"""
UPDATE {self.table_prefix}_detections
SET verified = TRUE, status = ?
WHERE id = ?
""", (verdict, detection_id))
conn.commit()
conn.close()