122 lines
4.7 KiB
Python
122 lines
4.7 KiB
Python
import sqlite3
|
|
from pathlib import Path
|
|
|
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
|
DB_PATH = BASE_DIR / "data" / "staysense.db"
|
|
|
|
|
|
def get_conn() -> sqlite3.Connection:
|
|
conn = sqlite3.connect(DB_PATH)
|
|
conn.row_factory = sqlite3.Row
|
|
conn.execute("PRAGMA foreign_keys = ON")
|
|
return conn
|
|
|
|
|
|
def init_db() -> None:
|
|
DB_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
with get_conn() as conn:
|
|
conn.executescript(
|
|
"""
|
|
CREATE TABLE IF NOT EXISTS spot (
|
|
id TEXT PRIMARY KEY,
|
|
lat REAL NOT NULL,
|
|
lon REAL NOT NULL,
|
|
osm_area_type TEXT NOT NULL CHECK (osm_area_type IN ('residential', 'industrial', 'commercial', 'parking', 'nature')),
|
|
road_type TEXT NOT NULL CHECK (road_type IN ('residential', 'primary', 'secondary', 'service', 'unknown')),
|
|
distance_police_m INTEGER NOT NULL,
|
|
distance_fire_m INTEGER NOT NULL,
|
|
distance_hospital_m INTEGER NOT NULL,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS local_event (
|
|
id TEXT PRIMARY KEY,
|
|
spot_id TEXT NOT NULL,
|
|
event_type TEXT NOT NULL CHECK (event_type IN ('market', 'waste', 'event', 'construction')),
|
|
start_datetime TEXT NOT NULL,
|
|
end_datetime TEXT NOT NULL,
|
|
risk_modifier INTEGER NOT NULL,
|
|
source TEXT NOT NULL,
|
|
created_at TEXT NOT NULL,
|
|
FOREIGN KEY (spot_id) REFERENCES spot(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS community_signal (
|
|
id TEXT PRIMARY KEY,
|
|
spot_id TEXT NOT NULL,
|
|
signal_type TEXT NOT NULL CHECK (signal_type IN ('calm', 'noise', 'knock', 'police')),
|
|
hashed_device TEXT NOT NULL,
|
|
timestamp TEXT NOT NULL,
|
|
day_bucket TEXT NOT NULL,
|
|
FOREIGN KEY (spot_id) REFERENCES spot(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS osm_poi (
|
|
id TEXT PRIMARY KEY,
|
|
poi_type TEXT NOT NULL CHECK (poi_type IN ('police', 'fire', 'hospital')),
|
|
lat REAL NOT NULL,
|
|
lon REAL NOT NULL,
|
|
source TEXT NOT NULL,
|
|
imported_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS osm_zone (
|
|
id TEXT PRIMARY KEY,
|
|
zone_type TEXT NOT NULL CHECK (zone_type IN ('residential', 'industrial', 'commercial', 'parking', 'nature')),
|
|
lat REAL NOT NULL,
|
|
lon REAL NOT NULL,
|
|
source TEXT NOT NULL,
|
|
imported_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS osm_road (
|
|
id TEXT PRIMARY KEY,
|
|
road_type TEXT NOT NULL CHECK (road_type IN ('residential', 'primary', 'secondary', 'service')),
|
|
lat REAL NOT NULL,
|
|
lon REAL NOT NULL,
|
|
source TEXT NOT NULL,
|
|
imported_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS open_data_event (
|
|
id TEXT PRIMARY KEY,
|
|
event_type TEXT NOT NULL CHECK (event_type IN ('market', 'waste', 'event', 'construction')),
|
|
lat REAL NOT NULL,
|
|
lon REAL NOT NULL,
|
|
start_datetime TEXT NOT NULL,
|
|
end_datetime TEXT NOT NULL,
|
|
risk_modifier INTEGER NOT NULL,
|
|
source TEXT NOT NULL,
|
|
imported_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS data_source_state (
|
|
source_name TEXT PRIMARY KEY,
|
|
imported_at TEXT NOT NULL,
|
|
record_count INTEGER NOT NULL,
|
|
notes TEXT NOT NULL
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_signal_spot_timestamp
|
|
ON community_signal (spot_id, timestamp);
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS ux_signal_spot_device_day
|
|
ON community_signal (spot_id, hashed_device, day_bucket);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_local_event_spot_window
|
|
ON local_event (spot_id, start_datetime, end_datetime);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_osm_poi_type
|
|
ON osm_poi (poi_type);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_osm_zone_type
|
|
ON osm_zone (zone_type);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_open_data_event_window
|
|
ON open_data_event (start_datetime, end_datetime);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_open_data_event_source
|
|
ON open_data_event (source);
|
|
"""
|
|
)
|