feat: prefer Six Flags regular waits, show Fast Lane at 0, mark outages on chart
Three usability fixes after a day of using the ride detail page. 1. Six Flags is now the primary source for regular wait times. SF's /wait-times endpoint reports regular waits alongside Fast Lane, and it updates more promptly than Queue-Times around park-open. The sampler and the live /rides + ride-history routes all prefer SF's regularWaittime when its createdDateTime is non-empty; Queue-Times remains the fallback and the authoritative isOpen source. 2. The today chart's Fast Lane line now stays visible when its value is 0 (walk-on). Y-axis bottom padding ensures the line sits clearly above the X-axis frame instead of being clipped against it. The tooltip shows "walk-on" instead of "0 min" for that case. 3. Outages are now explicit on the chart instead of just being gaps. computeOutages walks today's samples to find contiguous closed runs and numbers them chronologically. Each outage renders as a translucent pink ReferenceArea with a "#N" label. The custom tooltip detects when the cursor is over an outage span and shows "Outage #N — Hh Mm" (e.g. "Outage #2 — 1h 28m") in place of the wait/Fast Lane rows. Includes a seed-test-samples.ts dev script for eyeballing the chart with synthetic outage data. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Dev-only: seed today's `ride_wait_samples` for one ride with a mix of
|
||||
* open + outage data so the WaitTimeTodayChart renders something we can
|
||||
* eyeball. Idempotent for a given ride: wipes then reinserts today's rows.
|
||||
*/
|
||||
import Database from "better-sqlite3";
|
||||
import path from "path";
|
||||
|
||||
const PARK_ID = process.argv[2] ?? "greatamerica";
|
||||
const RIDE_SLUG = process.argv[3] ?? "raging-bull";
|
||||
|
||||
const db = new Database(path.join(__dirname, "..", "data", "parks.db"));
|
||||
const ride = db
|
||||
.prepare("SELECT park_id, qt_ride_id, name FROM rides WHERE park_id = ? AND slug = ?")
|
||||
.get(PARK_ID, RIDE_SLUG) as { park_id: string; qt_ride_id: number; name: string } | undefined;
|
||||
|
||||
if (!ride) {
|
||||
console.error(`No ride found for ${PARK_ID}/${RIDE_SLUG} — trigger sampler first.`);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log("Seeding for:", ride.name);
|
||||
|
||||
const TZ_OFFSET_HOURS = -5; // Central daylight = UTC-5 (May 30, 2026 is CDT)
|
||||
const today = new Date();
|
||||
const yyyy = today.getUTCFullYear();
|
||||
const mm = String(today.getUTCMonth() + 1).padStart(2, "0");
|
||||
const dd = String(today.getUTCDate()).padStart(2, "0");
|
||||
const todayLocal = `${yyyy}-${mm}-${dd}`;
|
||||
|
||||
// Wipe today's rows for this ride.
|
||||
db.prepare(
|
||||
"DELETE FROM ride_wait_samples WHERE park_id = ? AND qt_ride_id = ? AND local_date = ?",
|
||||
).run(ride.park_id, ride.qt_ride_id, todayLocal);
|
||||
|
||||
const insert = db.prepare(
|
||||
`INSERT INTO ride_wait_samples
|
||||
(park_id, qt_ride_id, recorded_at, local_date, local_time, is_open, wait_minutes, fast_lane_minutes)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
);
|
||||
|
||||
// Build samples at 5-min cadence from 10:00 local through 18:00 local with
|
||||
// two outages: 12:15-12:45 (#1, 30 min) and 15:30-17:00 (#2, 1h 30m).
|
||||
const startLocalMin = 10 * 60;
|
||||
const endLocalMin = 18 * 60;
|
||||
const o1Start = 12 * 60 + 15;
|
||||
const o1End = 12 * 60 + 45;
|
||||
const o2Start = 15 * 60 + 30;
|
||||
const o2End = 17 * 60;
|
||||
|
||||
const dateBase = new Date(`${todayLocal}T00:00:00Z`).getTime();
|
||||
let count = 0;
|
||||
for (let m = startLocalMin; m <= endLocalMin; m += 5) {
|
||||
const isOpen = !((m >= o1Start && m < o1End) || (m >= o2Start && m < o2End));
|
||||
const wait = isOpen ? 15 + Math.floor((m - startLocalMin) / 30) * 5 : null;
|
||||
const fl = isOpen ? (m < 12 * 60 ? 0 : 5) : null; // walk-on morning, 5 min afternoon
|
||||
const localTime = `${String(Math.floor(m / 60)).padStart(2, "0")}:${String(m % 60).padStart(2, "0")}`;
|
||||
const utcMin = m - TZ_OFFSET_HOURS * 60;
|
||||
const recordedAt = new Date(dateBase + utcMin * 60_000).toISOString();
|
||||
insert.run(
|
||||
ride.park_id,
|
||||
ride.qt_ride_id,
|
||||
recordedAt,
|
||||
todayLocal,
|
||||
localTime,
|
||||
isOpen ? 1 : 0,
|
||||
wait,
|
||||
fl,
|
||||
);
|
||||
count++;
|
||||
}
|
||||
console.log(`Inserted ${count} samples for ${todayLocal}`);
|
||||
db.close();
|
||||
Reference in New Issue
Block a user