mirror of
https://github.com/Felitendo/Tanken.git
synced 2026-11-01 17:03:25 +01:00
Replace the monotonically-growing P20/P80 session anchor with a P10/P50/P90 band computed from the last 24 hours of station_prices, per country and fuel. Frontend renders a three-stop HSL gradient (green -> pale yellow at the median -> red), so 1.82 EUR and 1.84 EUR near the median read as near-identical instead of greenish vs brownish. When the band is missing (API down, empty DB, <50 samples), markers stay neutral grey — no fallback to the old session-anchor logic. - station_prices.fuel_type column + idempotent backfill from scan_locations (DE) and station_cache (AT) - persistPriceSnapshot now writes one snapshot per fuel so fuel_type is correct on every row - new getCountryPriceBand with a 10-minute in-memory cache - new GET /api/price-band?fuel=...
140 lines
5.1 KiB
SQL
140 lines
5.1 KiB
SQL
CREATE TABLE IF NOT EXISTS app_meta (
|
|
key TEXT PRIMARY KEY,
|
|
value_json JSONB NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id TEXT PRIMARY KEY,
|
|
data_json JSONB NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
id TEXT PRIMARY KEY,
|
|
data_json JSONB NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS oauth_states (
|
|
key TEXT PRIMARY KEY,
|
|
data_json JSONB NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS price_history (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
timestamp TIMESTAMPTZ NOT NULL,
|
|
min_price DOUBLE PRECISION,
|
|
avg_price DOUBLE PRECISION,
|
|
max_price DOUBLE PRECISION,
|
|
station TEXT,
|
|
num_stations INTEGER
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_price_history_timestamp ON price_history(timestamp DESC);
|
|
|
|
-- Location-based scanning support
|
|
ALTER TABLE price_history ADD COLUMN IF NOT EXISTS location_id TEXT;
|
|
CREATE INDEX IF NOT EXISTS idx_price_history_location ON price_history(location_id, timestamp DESC);
|
|
|
|
-- Per-station price data (populated by scheduler scans)
|
|
CREATE TABLE IF NOT EXISTS station_prices (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
timestamp TIMESTAMPTZ NOT NULL,
|
|
location_id TEXT,
|
|
station_name TEXT NOT NULL,
|
|
station_brand TEXT NOT NULL DEFAULT '',
|
|
price DOUBLE PRECISION NOT NULL
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_station_prices_timestamp ON station_prices(timestamp DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_station_prices_location ON station_prices(location_id, timestamp DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_station_prices_name ON station_prices(station_name, timestamp DESC);
|
|
|
|
-- Tankerkönig station IDs disambiguate rows whose station_name collides
|
|
-- across real-world branches (e.g. many rows named "JET TANKSTELLE"). Older
|
|
-- rows have NULL here; readers should prefer station_id when available
|
|
-- and fall back to station_name otherwise.
|
|
ALTER TABLE station_prices ADD COLUMN IF NOT EXISTS station_id TEXT;
|
|
CREATE INDEX IF NOT EXISTS idx_station_prices_id ON station_prices(station_id, timestamp DESC);
|
|
|
|
-- fuel_type is needed for country-wide price-band aggregation (color heatmap).
|
|
-- New rows are written with the fuel they were scanned for; the two UPDATEs
|
|
-- backfill historical rows from scan_locations (DE admin-curated) and
|
|
-- station_cache (AT grid + DE cached). Both UPDATEs are no-ops once all rows
|
|
-- have a fuel_type, so they are safe to leave in the boot path.
|
|
ALTER TABLE station_prices ADD COLUMN IF NOT EXISTS fuel_type TEXT;
|
|
CREATE INDEX IF NOT EXISTS idx_station_prices_fuel_time ON station_prices(fuel_type, timestamp DESC);
|
|
|
|
UPDATE station_prices sp
|
|
SET fuel_type = sl.fuel_type
|
|
FROM scan_locations sl
|
|
WHERE sp.location_id = sl.id
|
|
AND sp.fuel_type IS NULL;
|
|
|
|
UPDATE station_prices sp
|
|
SET fuel_type = sc.fuel_type
|
|
FROM station_cache sc
|
|
WHERE sp.location_id = sc.location_id
|
|
AND sp.fuel_type IS NULL;
|
|
|
|
-- Persistent station cache (survives restarts, populated by admin scan locations + AT grid)
|
|
CREATE TABLE IF NOT EXISTS station_cache (
|
|
location_id TEXT PRIMARY KEY,
|
|
lat DOUBLE PRECISION NOT NULL,
|
|
lng DOUBLE PRECISION NOT NULL,
|
|
radius_km DOUBLE PRECISION NOT NULL,
|
|
fuel_type TEXT NOT NULL,
|
|
stations JSONB NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- Admin-curated scan locations (max 25 km radius — Tankerkönig list.php limit)
|
|
CREATE TABLE IF NOT EXISTS scan_locations (
|
|
id TEXT PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
country TEXT NOT NULL DEFAULT 'de',
|
|
lat DOUBLE PRECISION NOT NULL,
|
|
lng DOUBLE PRECISION NOT NULL,
|
|
radius_km DOUBLE PRECISION NOT NULL CHECK (radius_km BETWEEN 1 AND 25),
|
|
fuel_type TEXT NOT NULL DEFAULT 'diesel',
|
|
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
|
created_by TEXT,
|
|
source_request_id TEXT,
|
|
last_scan_at TIMESTAMPTZ,
|
|
last_scan_station_count INTEGER,
|
|
last_scan_error TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_scan_locations_enabled ON scan_locations(enabled);
|
|
|
|
-- User-submitted requests for new scan locations
|
|
CREATE TABLE IF NOT EXISTS location_requests (
|
|
id TEXT PRIMARY KEY,
|
|
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
lat DOUBLE PRECISION NOT NULL,
|
|
lng DOUBLE PRECISION NOT NULL,
|
|
radius_km DOUBLE PRECISION NOT NULL CHECK (radius_km BETWEEN 1 AND 25),
|
|
note TEXT,
|
|
status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','approved','denied')),
|
|
admin_note TEXT,
|
|
reviewed_by TEXT,
|
|
reviewed_at TIMESTAMPTZ,
|
|
resulting_location_id TEXT REFERENCES scan_locations(id) ON DELETE SET NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_location_requests_user ON location_requests(user_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_location_requests_status ON location_requests(status, created_at DESC);
|
|
|
|
-- Legacy dump-import cleanup (dump-import feature was removed)
|
|
DROP TABLE IF EXISTS known_stations;
|
|
DELETE FROM station_cache WHERE location_id LIKE 'de-import-%';
|