Files
SixFlagsSuperCalendar/scripts/debug.ts
josh 91e09b0548
All checks were successful
Build and Deploy / Build & Push (push) Successful in 3m9s
feat: detect passholder preview days and filter plain buyouts
- Buyout days are now treated as closed unless they carry a Passholder
  Preview event, in which case they surface as a distinct purple cell
  in the UI showing "Passholder" + hours
- DB gains a special_type column (auto-migrated on next startup)
- scrape.ts threads specialType through to upsertDay
- debug.ts now shows events, isBuyout, isPassholderPreview, and
  specialType in the parsed result section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 10:53:05 -04:00

141 lines
5.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Debug a specific park + date to inspect raw API data and parsed output.
*
* Usage:
* npm run debug -- --park greatadventure --date 2026-07-04
*
* Output is printed to the terminal and saved to debug/{parkId}_{date}.txt
*/
import fs from "fs";
import path from "path";
import { openDb, getApiId } from "../lib/db";
import { PARKS } from "../lib/parks";
import { scrapeMonthRaw } from "../lib/scrapers/sixflags";
function arg(flag: string): string | undefined {
const i = process.argv.indexOf(flag);
return i !== -1 ? process.argv[i + 1] : undefined;
}
function fmt24(time: string): string {
const [h, m] = time.split(":").map(Number);
const period = h >= 12 ? "pm" : "am";
const h12 = h % 12 || 12;
return m === 0 ? `${h12}${period}` : `${h12}:${String(m).padStart(2, "0")}${period}`;
}
async function main() {
const parkId = arg("--park");
const dateStr = arg("--date");
if (!parkId || !dateStr) {
console.error("Usage: npm run debug -- --park <parkId> --date <YYYY-MM-DD>");
console.error("\nAvailable park IDs:");
for (const p of PARKS) console.error(` ${p.id.padEnd(24)} ${p.name}`);
process.exit(1);
}
const park = PARKS.find((p) => p.id === parkId);
if (!park) {
console.error(`Unknown park: "${parkId}"`);
process.exit(1);
}
const match = dateStr.match(/^(\d{4})-(\d{2})-(\d{2})$/);
if (!match) {
console.error(`Invalid date: "${dateStr}" — expected YYYY-MM-DD`);
process.exit(1);
}
const [, yearStr, monthStr, dayStr] = match;
const year = parseInt(yearStr);
const month = parseInt(monthStr);
const day = parseInt(dayStr);
const db = openDb();
const apiId = getApiId(db, park.id);
db.close();
if (apiId === null) {
console.error(`No API ID found for ${park.name} — run: npm run discover`);
process.exit(1);
}
// Collect all output so we can write it to a file as well
const lines: string[] = [];
const out = (...args: string[]) => {
const line = args.join(" ");
lines.push(line);
console.log(line);
};
out(`Park : ${park.name} (${park.id})`);
out(`API ID : ${apiId}`);
out(`Date : ${dateStr}`);
out(`Fetched : ${new Date().toISOString()}`);
out("");
out(`Fetching ${year}-${String(month).padStart(2, "0")} from API...`);
const raw = await scrapeMonthRaw(apiId, year, month);
const targetDate = `${String(month).padStart(2, "0")}/${String(day).padStart(2, "0")}/${year}`;
const dayData = raw.dates.find((d) => d.date === targetDate);
if (!dayData) {
console.error(`Date ${dateStr} not found in API response.`);
console.error(`Dates returned by API: ${raw.dates.map((d) => d.date).join(", ")}`);
process.exit(1);
}
// ── Raw API response ───────────────────────────────────────────────────────
out("");
out("── Raw API response ─────────────────────────────────────────");
out(JSON.stringify(dayData, null, 2));
// ── Parsed result ──────────────────────────────────────────────────────────
const operating =
dayData.operatings?.find((o: { operatingTypeName: string }) => o.operatingTypeName === "Park") ??
dayData.operatings?.[0];
const item = operating?.items?.[0];
const hoursLabel =
item?.timeFrom && item?.timeTo
? `${fmt24(item.timeFrom)} ${fmt24(item.timeTo)}`
: undefined;
const isBuyout = item?.isBuyout ?? false;
const isPassholderPreview = dayData.events?.some((e: { extEventName: string }) =>
e.extEventName.toLowerCase().includes("passholder preview")
) ?? false;
const isOpen = !dayData.isParkClosed && hoursLabel !== undefined && (!isBuyout || isPassholderPreview);
const specialType = isPassholderPreview ? "passholder_preview" : null;
out("");
out("── Parsed result ────────────────────────────────────────────");
out(` isParkClosed : ${dayData.isParkClosed}`);
out(` events : ${dayData.events?.length ?? 0} (${dayData.events?.map((e: { extEventName: string }) => e.extEventName).join(", ") || "none"})`);
out(` operatings : ${dayData.operatings?.length ?? 0} entr${dayData.operatings?.length === 1 ? "y" : "ies"}`);
if (operating) {
out(` selected : "${operating.operatingTypeName}" (${operating.items?.length ?? 0} item(s))`);
if (item) {
out(` timeFrom : ${item.timeFrom}${fmt24(item.timeFrom)}`);
out(` timeTo : ${item.timeTo}${fmt24(item.timeTo)}`);
out(` isBuyout : ${isBuyout}`);
}
}
out(` isPassholderPreview : ${isPassholderPreview}`);
out(` hoursLabel : ${hoursLabel ?? "(none)"}`);
out(` isOpen : ${isOpen}`);
out(` specialType : ${specialType ?? "(none)"}`)
// ── Write to file ──────────────────────────────────────────────────────────
const debugDir = path.join(process.cwd(), "debug");
fs.mkdirSync(debugDir, { recursive: true });
const outPath = path.join(debugDir, `${parkId}_${dateStr}.txt`);
fs.writeFileSync(outPath, lines.join("\n") + "\n");
console.log(`\nSaved to debug/${parkId}_${dateStr}.txt`);
}
main().catch((err) => {
console.error("Fatal:", err);
process.exit(1);
});