| """
|
| SQLite database setup using Python's built-in sqlite3.
|
| Database file: backend/evolve.db
|
| No extra packages needed — sqlite3 is part of Python stdlib.
|
| """
|
| import sqlite3
|
| import os
|
| import json
|
| from datetime import datetime
|
| from pathlib import Path
|
|
|
|
|
| import os
|
| _env_path = os.environ.get("DB_PATH")
|
| DB_PATH = Path(_env_path) if _env_path else Path(__file__).parent.parent / "evolve.db"
|
|
|
|
|
| def get_conn() -> sqlite3.Connection:
|
| """Return a new SQLite connection with row_factory for dict-like access."""
|
| conn = sqlite3.connect(str(DB_PATH))
|
| conn.row_factory = sqlite3.Row
|
| conn.execute("PRAGMA journal_mode=WAL")
|
| conn.execute("PRAGMA foreign_keys=ON")
|
| return conn
|
|
|
|
|
| def init_db():
|
| """Create all tables if they don't exist. Called once on startup."""
|
| conn = get_conn()
|
| cur = conn.cursor()
|
|
|
|
|
| cur.execute("""
|
| CREATE TABLE IF NOT EXISTS bms_data (
|
| id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| vehicle_id TEXT NOT NULL,
|
| timestamp TEXT NOT NULL,
|
| soc REAL,
|
| voltage REAL,
|
| current REAL,
|
| temperature REAL,
|
| cycle_count INTEGER,
|
| odometer_km REAL,
|
| uploaded_at TEXT NOT NULL DEFAULT (datetime('now'))
|
| )
|
| """)
|
|
|
| cur.execute("""
|
| CREATE TABLE IF NOT EXISTS fleet_data (
|
| id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| asset_id TEXT NOT NULL UNIQUE,
|
| category TEXT,
|
| route_distance_km REAL,
|
| payload_tons REAL,
|
| duty_cycle_hrs REAL,
|
| dwell_time_hrs REAL,
|
| terrain_score REAL,
|
| diesel_l_per_100km REAL,
|
| age_years REAL,
|
| uploaded_at TEXT NOT NULL DEFAULT (datetime('now'))
|
| )
|
| """)
|
|
|
| cur.execute("""
|
| CREATE TABLE IF NOT EXISTS supplier_data (
|
| id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| supplier_id TEXT NOT NULL UNIQUE,
|
| name TEXT,
|
| tier INTEGER,
|
| material TEXT,
|
| country TEXT,
|
| geopolitical_risk REAL,
|
| esg_score REAL,
|
| quality_score REAL,
|
| lead_time_days INTEGER,
|
| concentration_pct REAL,
|
| uploaded_at TEXT NOT NULL DEFAULT (datetime('now'))
|
| )
|
| """)
|
|
|
| cur.execute("""
|
| CREATE TABLE IF NOT EXISTS inspection_data (
|
| id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| lot_id TEXT NOT NULL,
|
| supplier_id TEXT NOT NULL,
|
| material TEXT,
|
| defect_rate REAL,
|
| inspection_date TEXT,
|
| root_cause_hint TEXT,
|
| uploaded_at TEXT NOT NULL DEFAULT (datetime('now'))
|
| )
|
| """)
|
|
|
|
|
| cur.execute("""
|
| CREATE TABLE IF NOT EXISTS activity_log (
|
| id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| timestamp TEXT NOT NULL DEFAULT (datetime('now')),
|
| user TEXT NOT NULL DEFAULT 'Admin',
|
| action TEXT NOT NULL,
|
| endpoint TEXT,
|
| method TEXT,
|
| status_code INTEGER,
|
| details TEXT -- JSON string for extra metadata
|
| )
|
| """)
|
|
|
|
|
| cur.execute("""
|
| CREATE TABLE IF NOT EXISTS upload_history (
|
| id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| timestamp TEXT NOT NULL DEFAULT (datetime('now')),
|
| user TEXT NOT NULL DEFAULT 'Admin',
|
| data_type TEXT NOT NULL, -- bms / fleet / suppliers / inspections
|
| filename TEXT,
|
| row_count INTEGER,
|
| status TEXT NOT NULL DEFAULT 'success',
|
| error_msg TEXT
|
| )
|
| """)
|
|
|
| conn.commit()
|
| conn.close()
|
| print(f"[DB] SQLite database ready at: {DB_PATH}")
|
|
|
|
|
|
|
|
|
| def log_activity(action: str, endpoint: str = None, method: str = None,
|
| status_code: int = 200, details: dict = None, user: str = "Admin"):
|
| """Insert one row into activity_log."""
|
| conn = get_conn()
|
| conn.execute(
|
| """INSERT INTO activity_log (timestamp, user, action, endpoint, method, status_code, details)
|
| VALUES (datetime('now'), ?, ?, ?, ?, ?, ?)""",
|
| (user, action, endpoint, method, status_code, json.dumps(details) if details else None)
|
| )
|
| conn.commit()
|
| conn.close()
|
|
|
|
|
| def log_upload(data_type: str, filename: str, row_count: int,
|
| status: str = "success", error_msg: str = None, user: str = "Admin"):
|
| """Insert one row into upload_history."""
|
| conn = get_conn()
|
| conn.execute(
|
| """INSERT INTO upload_history (timestamp, user, data_type, filename, row_count, status, error_msg)
|
| VALUES (datetime('now'), ?, ?, ?, ?, ?, ?)""",
|
| (user, data_type, filename, row_count, status, error_msg)
|
| )
|
| conn.commit()
|
| conn.close()
|
|
|
|
|
| def get_activity_log(limit: int = 100) -> list:
|
| conn = get_conn()
|
| rows = conn.execute(
|
| "SELECT * FROM activity_log ORDER BY timestamp DESC LIMIT ?", (limit,)
|
| ).fetchall()
|
| conn.close()
|
| return [dict(r) for r in rows]
|
|
|
|
|
| def get_upload_history(limit: int = 50) -> list:
|
| conn = get_conn()
|
| rows = conn.execute(
|
| "SELECT * FROM upload_history ORDER BY timestamp DESC LIMIT ?", (limit,)
|
| ).fetchall()
|
| conn.close()
|
| return [dict(r) for r in rows]
|
|
|