Initial commit: LoraTester Android + server

This commit is contained in:
2026-06-04 13:05:21 +03:00
commit 83d0353754
124 changed files with 7892 additions and 0 deletions
+141
View File
@@ -0,0 +1,141 @@
"""SQLite schema creation and migrations."""
from __future__ import annotations
import sqlite3
SCHEMA_VERSION = 3
def table_exists(conn: sqlite3.Connection, name: str) -> bool:
row = conn.execute(
"SELECT 1 FROM sqlite_master WHERE type='table' AND name=?",
(name,),
).fetchone()
return row is not None
def column_exists(conn: sqlite3.Connection, table: str, column: str) -> bool:
if not table_exists(conn, table):
return False
cols = conn.execute(f"PRAGMA table_info({table})").fetchall()
return any(c[1] == column for c in cols)
def ensure_column(
conn: sqlite3.Connection, table: str, column: str, ddl: str, log: list[str]
) -> None:
if not column_exists(conn, table, column):
conn.execute(f"ALTER TABLE {table} ADD COLUMN {column} {ddl}")
log.append(f"ALTER {table} ADD {column}")
def get_schema_version(conn: sqlite3.Connection) -> int:
if not table_exists(conn, "schema_version"):
return 0
row = conn.execute("SELECT version FROM schema_version LIMIT 1").fetchone()
return int(row[0]) if row else 0
def set_schema_version(conn: sqlite3.Connection, version: int) -> None:
conn.execute(
"""
CREATE TABLE IF NOT EXISTS schema_version (version INTEGER NOT NULL);
"""
)
conn.execute("DELETE FROM schema_version")
conn.execute("INSERT INTO schema_version (version) VALUES (?)", (version,))
def apply_migrations(conn: sqlite3.Connection) -> list[str]:
log: list[str] = []
conn.executescript(
"""
CREATE TABLE IF NOT EXISTS devices (
device_id TEXT PRIMARY KEY,
label TEXT,
last_seen REAL NOT NULL
);
CREATE TABLE IF NOT EXISTS telemetry (
id INTEGER PRIMARY KEY AUTOINCREMENT,
device_id TEXT NOT NULL,
lat REAL,
lon REAL,
rssi REAL,
range_m REAL,
raw_frame TEXT,
ts REAL NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_telemetry_device_ts
ON telemetry(device_id, ts DESC);
CREATE TABLE IF NOT EXISTS chat (
id INTEGER PRIMARY KEY AUTOINCREMENT,
device_id TEXT NOT NULL,
text TEXT NOT NULL,
ts REAL NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_chat_ts ON chat(ts);
"""
)
log.append("ensure base tables")
ensure_column(conn, "telemetry", "source", "TEXT", log)
ensure_column(conn, "telemetry", "meta", "TEXT", log)
ensure_column(conn, "telemetry", "role", "TEXT", log)
if not table_exists(conn, "tracks"):
conn.executescript(
"""
CREATE TABLE tracks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
device_id TEXT NOT NULL,
started_at REAL NOT NULL,
ended_at REAL,
label TEXT
);
CREATE INDEX IF NOT EXISTS idx_tracks_device ON tracks(device_id, started_at DESC);
"""
)
log.append("CREATE tracks")
if not table_exists(conn, "track_points"):
conn.executescript(
"""
CREATE TABLE track_points (
id INTEGER PRIMARY KEY AUTOINCREMENT,
track_id INTEGER NOT NULL,
ts REAL NOT NULL,
lat REAL NOT NULL,
lon REAL NOT NULL,
altitude_gps REAL,
elevation_m REAL,
rssi REAL,
role TEXT,
meta TEXT,
FOREIGN KEY (track_id) REFERENCES tracks(id)
);
CREATE INDEX IF NOT EXISTS idx_track_points_track_ts
ON track_points(track_id, ts);
"""
)
log.append("CREATE track_points")
set_schema_version(conn, SCHEMA_VERSION)
log.append(f"schema_version={SCHEMA_VERSION}")
return log
def check_db_ok(conn: sqlite3.Connection) -> bool:
required = [
("devices", None),
("telemetry", "meta"),
("tracks", None),
("track_points", "elevation_m"),
]
for table, col in required:
if not table_exists(conn, table):
return False
if col and not column_exists(conn, table, col):
return False
return True