← back to Ventura Corridor
src/jobs/daily_snapshot.ts
48 lines
/**
* Daily corridor activity snapshot.
*
* Run nightly (or any time — UPSERT keys on snap_date) to capture today's
* pitch counts grouped by status / outreach_channel / dw_proximity / totals
* into pitch_daily_snapshot. The /today.html chart reads this table.
*
* $ npx tsx src/jobs/daily_snapshot.ts # snapshot today (default)
* $ npx tsx src/jobs/daily_snapshot.ts 2026-05-06 # snapshot a specific date
*/
import 'dotenv/config';
import { pool, query } from '../../db/pool.ts';
const TARGET = process.argv[2] || new Date().toISOString().slice(0, 10);
async function main() {
if (!/^\d{4}-\d{2}-\d{2}$/.test(TARGET)) {
throw new Error(`bad date arg: ${TARGET} — expected YYYY-MM-DD`);
}
const r = await query(`SELECT dim, bucket, count FROM v_pitch_today_status`);
let upserted = 0;
for (const row of r.rows) {
await query(
`
INSERT INTO pitch_daily_snapshot (snap_date, dim, bucket, count, captured_at)
VALUES ($1, $2, $3, $4, NOW())
ON CONFLICT (snap_date, dim, bucket)
DO UPDATE SET count = EXCLUDED.count, captured_at = NOW()
`,
[TARGET, row.dim, row.bucket, row.count]
);
upserted++;
}
console.log(
`[daily_snapshot] ${TARGET}: ${upserted} buckets upserted (${r.rows
.filter((x) => x.dim === 'totals')
.map((x) => `${x.bucket}=${x.count}`)
.join(' · ')})`
);
await pool.end();
}
main().catch((e) => {
console.error('[daily_snapshot] failed:', e);
process.exit(1);
});