Fix schema: add missing tables and correct forecast_snapshots structure

- Add booking_com_hotels and booking_com_rates (competitor rate scraper tables)
- Fix forecast_snapshots: was using old pivoted schema (snapshot_date,
  prophet_value etc.); code expects normalised rows (perception_date, model,
  forecast_value, actual_value). Add DO block migration to drop+recreate if
  old schema is detected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jtricerolph 2026-07-05 09:28:52 +00:00
parent 3c66e777e4
commit effad0a4e2

View file

@ -273,6 +273,45 @@ CREATE TABLE IF NOT EXISTS booking_scrape_log (
CREATE INDEX IF NOT EXISTS idx_booking_scrape_log_started ON booking_scrape_log(started_at DESC);
-- ============================================
-- COMPETITOR RATE SCRAPER (booking.com hotels and rates)
-- ============================================
CREATE TABLE IF NOT EXISTS booking_com_hotels (
id SERIAL PRIMARY KEY,
booking_com_id VARCHAR(50) UNIQUE,
name VARCHAR(255) NOT NULL,
booking_com_url TEXT,
star_rating DECIMAL(2,1),
review_score DECIMAL(3,1),
review_count INTEGER DEFAULT 0,
tier VARCHAR(20) DEFAULT 'market',
display_order INTEGER DEFAULT 999,
notes TEXT,
is_active BOOLEAN DEFAULT TRUE,
first_seen_at TIMESTAMPTZ DEFAULT NOW(),
last_seen_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS booking_com_rates (
id SERIAL PRIMARY KEY,
hotel_id INTEGER NOT NULL REFERENCES booking_com_hotels(id) ON DELETE CASCADE,
rate_date DATE NOT NULL,
availability_status VARCHAR(30),
rate_gross DECIMAL(10,2),
currency VARCHAR(10) DEFAULT 'GBP',
room_type VARCHAR(100),
breakfast_included BOOLEAN DEFAULT FALSE,
free_cancellation BOOLEAN DEFAULT FALSE,
no_prepayment BOOLEAN DEFAULT FALSE,
rooms_left INTEGER,
scrape_batch_id UUID REFERENCES booking_scrape_log(batch_id),
scraped_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_booking_com_rates_hotel_date ON booking_com_rates(hotel_id, rate_date);
CREATE INDEX IF NOT EXISTS idx_booking_com_rates_date ON booking_com_rates(rate_date);
-- ============================================
-- SYNC LOGGING
-- ============================================
@ -663,22 +702,45 @@ CREATE TABLE IF NOT EXISTS daily_budgets (
CREATE INDEX IF NOT EXISTS idx_daily_budgets_date ON daily_budgets(date, budget_type);
-- Forecast snapshots (for tracking how forecasts evolve over time)
-- Forecast snapshots (one row per perception_date+target_date+model+metric)
CREATE TABLE IF NOT EXISTS forecast_snapshots (
id SERIAL PRIMARY KEY,
snapshot_date DATE NOT NULL,
perception_date DATE NOT NULL,
target_date DATE NOT NULL,
model VARCHAR(50) NOT NULL,
metric_code VARCHAR(50) NOT NULL,
days_out INTEGER NOT NULL,
prophet_value DECIMAL(12,2),
xgboost_value DECIMAL(12,2),
pickup_value DECIMAL(12,2),
catboost_value DECIMAL(12,2),
days_out INTEGER,
forecast_value DECIMAL(12,2),
actual_value DECIMAL(12,2),
created_at TIMESTAMP DEFAULT NOW(),
UNIQUE(snapshot_date, target_date, metric_code)
UNIQUE(perception_date, target_date, model, metric_code)
);
-- Migration: drop old pivoted-column schema and recreate if snapshot_date column exists
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'forecast_snapshots' AND column_name = 'snapshot_date'
) THEN
DROP TABLE forecast_snapshots;
CREATE TABLE forecast_snapshots (
id SERIAL PRIMARY KEY,
perception_date DATE NOT NULL,
target_date DATE NOT NULL,
model VARCHAR(50) NOT NULL,
metric_code VARCHAR(50) NOT NULL,
days_out INTEGER,
forecast_value DECIMAL(12,2),
actual_value DECIMAL(12,2),
created_at TIMESTAMP DEFAULT NOW(),
UNIQUE(perception_date, target_date, model, metric_code)
);
END IF;
END $$;
CREATE INDEX IF NOT EXISTS idx_forecast_snapshots_target ON forecast_snapshots(target_date, metric_code);
CREATE INDEX IF NOT EXISTS idx_forecast_snapshots_perception ON forecast_snapshots(perception_date, model, metric_code);
-- ============================================
-- USER ROLES (migration for existing users table)