Alpha stage commit
This commit is contained in:
196
schemas/database.sql
Normal file
196
schemas/database.sql
Normal file
@@ -0,0 +1,196 @@
|
||||
-- BikepackPilot database draft
|
||||
-- Requires PostgreSQL + PostGIS.
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS postgis;
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
|
||||
CREATE TYPE confidence_level AS ENUM (
|
||||
'known_good', 'high', 'medium', 'low', 'unknown', 'restricted', 'conflicting'
|
||||
);
|
||||
|
||||
CREATE TYPE warning_severity AS ENUM ('info', 'notice', 'warning', 'critical');
|
||||
|
||||
CREATE TYPE poi_category AS ENUM (
|
||||
'water', 'food', 'sleep', 'repair', 'bailout', 'charging', 'emergency'
|
||||
);
|
||||
|
||||
CREATE TABLE users (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
email TEXT UNIQUE,
|
||||
display_name TEXT,
|
||||
default_profile TEXT NOT NULL DEFAULT 'loaded_gravel',
|
||||
privacy_mode TEXT NOT NULL DEFAULT 'private',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE trips (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
owner_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
title TEXT NOT NULL,
|
||||
start_point GEOGRAPHY(Point, 4326) NOT NULL,
|
||||
end_point GEOGRAPHY(Point, 4326) NOT NULL,
|
||||
start_date DATE,
|
||||
end_date DATE,
|
||||
profile TEXT NOT NULL,
|
||||
constraints JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
status TEXT NOT NULL DEFAULT 'draft',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE routes (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
trip_id UUID NOT NULL REFERENCES trips(id) ON DELETE CASCADE,
|
||||
provider TEXT NOT NULL,
|
||||
geometry GEOGRAPHY(LineString, 4326) NOT NULL,
|
||||
distance_m INTEGER NOT NULL,
|
||||
ascent_m INTEGER DEFAULT 0,
|
||||
descent_m INTEGER DEFAULT 0,
|
||||
surface_breakdown JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
suitability_score NUMERIC(5,2),
|
||||
warnings JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
attribution TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE route_segments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
route_id UUID NOT NULL REFERENCES routes(id) ON DELETE CASCADE,
|
||||
seq INTEGER NOT NULL,
|
||||
start_m INTEGER NOT NULL,
|
||||
end_m INTEGER NOT NULL,
|
||||
geometry GEOGRAPHY(LineString, 4326) NOT NULL,
|
||||
distance_m INTEGER NOT NULL,
|
||||
ascent_m INTEGER DEFAULT 0,
|
||||
descent_m INTEGER DEFAULT 0,
|
||||
avg_grade_percent NUMERIC(5,2),
|
||||
max_grade_percent NUMERIC(5,2),
|
||||
surface TEXT,
|
||||
smoothness TEXT,
|
||||
highway TEXT,
|
||||
bicycle_access TEXT,
|
||||
legal_access_confidence confidence_level NOT NULL DEFAULT 'unknown',
|
||||
source_confidence confidence_level NOT NULL DEFAULT 'unknown',
|
||||
traffic_stress NUMERIC(4,2),
|
||||
official_cycle_route BOOLEAN DEFAULT false,
|
||||
protected_area_overlap BOOLEAN DEFAULT false,
|
||||
scores JSONB NOT NULL DEFAULT '{}'::jsonb
|
||||
);
|
||||
|
||||
CREATE INDEX route_segments_route_seq_idx ON route_segments(route_id, seq);
|
||||
CREATE INDEX route_segments_geom_idx ON route_segments USING GIST(geometry);
|
||||
|
||||
CREATE TABLE pois (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
category poi_category NOT NULL,
|
||||
name TEXT,
|
||||
location GEOGRAPHY(Point, 4326) NOT NULL,
|
||||
source TEXT NOT NULL,
|
||||
source_id TEXT,
|
||||
confidence confidence_level NOT NULL DEFAULT 'unknown',
|
||||
opening_hours TEXT,
|
||||
last_seen_at TIMESTAMPTZ,
|
||||
last_confirmed_by_user_at TIMESTAMPTZ,
|
||||
tags JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX pois_category_idx ON pois(category);
|
||||
CREATE INDEX pois_location_idx ON pois USING GIST(location);
|
||||
CREATE UNIQUE INDEX pois_source_source_id_idx ON pois(source, source_id) WHERE source_id IS NOT NULL;
|
||||
|
||||
CREATE TABLE route_pois (
|
||||
route_id UUID NOT NULL REFERENCES routes(id) ON DELETE CASCADE,
|
||||
poi_id UUID NOT NULL REFERENCES pois(id) ON DELETE CASCADE,
|
||||
meters_from_start INTEGER,
|
||||
distance_from_route_m INTEGER,
|
||||
detour_distance_m INTEGER,
|
||||
relevance_score NUMERIC(5,2),
|
||||
PRIMARY KEY (route_id, poi_id)
|
||||
);
|
||||
|
||||
CREATE TABLE stages (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
route_id UUID NOT NULL REFERENCES routes(id) ON DELETE CASCADE,
|
||||
day_index INTEGER NOT NULL,
|
||||
start_m INTEGER NOT NULL,
|
||||
end_m INTEGER NOT NULL,
|
||||
distance_m INTEGER NOT NULL,
|
||||
ascent_m INTEGER DEFAULT 0,
|
||||
descent_m INTEGER DEFAULT 0,
|
||||
endpoint GEOGRAPHY(Point, 4326) NOT NULL,
|
||||
sleep_candidates JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
water_pois JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
food_pois JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
repair_pois JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
bailout_pois JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
warnings JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
plan_b JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE service_gaps (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
route_id UUID NOT NULL REFERENCES routes(id) ON DELETE CASCADE,
|
||||
category poi_category NOT NULL,
|
||||
start_m INTEGER NOT NULL,
|
||||
end_m INTEGER NOT NULL,
|
||||
distance_m INTEGER NOT NULL,
|
||||
severity warning_severity NOT NULL,
|
||||
explanation TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE rule_cards (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
rule_type TEXT NOT NULL,
|
||||
jurisdiction TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
confidence confidence_level NOT NULL DEFAULT 'unknown',
|
||||
summary TEXT NOT NULL,
|
||||
source_url TEXT,
|
||||
source_name TEXT,
|
||||
last_reviewed_at DATE,
|
||||
valid_from DATE,
|
||||
valid_to DATE,
|
||||
geometry GEOGRAPHY(Geometry, 4326),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX rule_cards_geom_idx ON rule_cards USING GIST(geometry);
|
||||
|
||||
CREATE TABLE local_reports (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
route_id UUID REFERENCES routes(id) ON DELETE SET NULL,
|
||||
route_segment_id UUID REFERENCES route_segments(id) ON DELETE SET NULL,
|
||||
report_type TEXT NOT NULL,
|
||||
location GEOGRAPHY(Point, 4326) NOT NULL,
|
||||
meters_from_start INTEGER,
|
||||
observed_at TIMESTAMPTZ NOT NULL,
|
||||
details TEXT,
|
||||
payload JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
trust_score NUMERIC(5,2) DEFAULT 0,
|
||||
expires_at TIMESTAMPTZ,
|
||||
moderation_status TEXT NOT NULL DEFAULT 'pending',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX local_reports_location_idx ON local_reports USING GIST(location);
|
||||
CREATE INDEX local_reports_type_idx ON local_reports(report_type);
|
||||
|
||||
CREATE TABLE offline_packs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
trip_id UUID NOT NULL REFERENCES trips(id) ON DELETE CASCADE,
|
||||
route_id UUID NOT NULL REFERENCES routes(id) ON DELETE CASCADE,
|
||||
bbox GEOGRAPHY(Polygon, 4326),
|
||||
route_buffer_km NUMERIC(6,2) NOT NULL DEFAULT 5,
|
||||
included_layers JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
data_versions JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
files JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
warnings JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
status TEXT NOT NULL DEFAULT 'queued',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
expires_at TIMESTAMPTZ
|
||||
);
|
||||
Reference in New Issue
Block a user