feat: implement reverse scan (country → specific airports)
All checks were successful
Deploy / deploy (push) Successful in 30s
All checks were successful
Deploy / deploy (push) Successful in 30s
- DB schema: relaxed origin CHECK to >=2 chars, added scan_mode column to
scans and scheduled_scans, added origin_airport to routes and flights,
updated unique index to (scan_id, COALESCE(origin_airport,''), destination)
- Migrations: init_db.py recreates tables and adds columns via guarded ALTERs
- API: scan_mode field on ScanRequest/Scan; Route/Flight expose origin_airport;
GET /scans/{id}/flights accepts origin_airport filter; CreateScheduleRequest
and Schedule carry scan_mode; scheduler and run-now pass scan_mode through
- scan_processor: _write_route_incremental accepts origin_airport; process_scan
branches on scan_mode=reverse (country → airports × destinations × dates)
- Frontend: new CountrySelect component (populated from GET /api/v1/countries);
Scans page adds Direction toggle + CountrySelect for both modes; ScanDetails
shows Origin column for reverse scans and uses composite route keys; Re-run
preserves scan_mode
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -20,8 +20,11 @@ CREATE TABLE IF NOT EXISTS scans (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
-- Search parameters (validated by CHECK constraints)
|
||||
origin TEXT NOT NULL CHECK(length(origin) = 3),
|
||||
-- origin stores IATA code (forward scans) or ISO country code (reverse scans)
|
||||
origin TEXT NOT NULL CHECK(length(origin) >= 2),
|
||||
country TEXT NOT NULL CHECK(length(country) >= 2),
|
||||
scan_mode TEXT NOT NULL DEFAULT 'forward'
|
||||
CHECK(scan_mode IN ('forward', 'reverse')),
|
||||
start_date TEXT NOT NULL, -- ISO 8601: YYYY-MM-DD
|
||||
end_date TEXT NOT NULL,
|
||||
|
||||
@@ -81,7 +84,10 @@ CREATE TABLE IF NOT EXISTS routes (
|
||||
-- Foreign key to scans (cascade delete)
|
||||
scan_id INTEGER NOT NULL,
|
||||
|
||||
-- Destination airport
|
||||
-- Route airports
|
||||
-- For forward scans: origin_airport is NULL (implicit from scan.origin)
|
||||
-- For reverse scans: origin_airport is the variable origin IATA
|
||||
origin_airport TEXT,
|
||||
destination TEXT NOT NULL CHECK(length(destination) = 3),
|
||||
destination_name TEXT NOT NULL,
|
||||
destination_city TEXT,
|
||||
@@ -120,9 +126,9 @@ CREATE INDEX IF NOT EXISTS idx_routes_min_price
|
||||
ON routes(min_price)
|
||||
WHERE min_price IS NOT NULL; -- Partial index for routes with prices
|
||||
|
||||
-- One route row per (scan, destination) — enables incremental upsert writes
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_routes_scan_dest
|
||||
ON routes(scan_id, destination);
|
||||
-- One route row per (scan, origin_airport, destination) — supports both forward and reverse scans
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_routes_scan_origin_dest
|
||||
ON routes(scan_id, COALESCE(origin_airport, ''), destination);
|
||||
|
||||
-- ============================================================================
|
||||
-- Triggers: Auto-update timestamps and aggregates
|
||||
@@ -191,6 +197,8 @@ CREATE TABLE IF NOT EXISTS flights (
|
||||
scan_id INTEGER NOT NULL,
|
||||
|
||||
-- Route
|
||||
-- origin_airport: NULL for forward scans, specific IATA for reverse scans
|
||||
origin_airport TEXT,
|
||||
destination TEXT NOT NULL CHECK(length(destination) = 3),
|
||||
date TEXT NOT NULL, -- ISO 8601: YYYY-MM-DD
|
||||
|
||||
@@ -273,8 +281,10 @@ CREATE TABLE IF NOT EXISTS scheduled_scans (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
-- Scan parameters (same as scans table)
|
||||
origin TEXT NOT NULL CHECK(length(origin) = 3),
|
||||
origin TEXT NOT NULL CHECK(length(origin) >= 2),
|
||||
country TEXT NOT NULL CHECK(length(country) >= 2),
|
||||
scan_mode TEXT NOT NULL DEFAULT 'forward'
|
||||
CHECK(scan_mode IN ('forward', 'reverse')),
|
||||
window_months INTEGER NOT NULL DEFAULT 1
|
||||
CHECK(window_months >= 1 AND window_months <= 12),
|
||||
seat_class TEXT NOT NULL DEFAULT 'economy',
|
||||
|
||||
Reference in New Issue
Block a user