← back to Commercialrealestate

scripts/export-sfr-snapshot.js

43 lines

// export-sfr-snapshot.js — dump the scraped single-family for-sale listings (public.sfr in the
// local `cre` DB) to a static JSON snapshot: data/sfr-redfin.json.
//
// WHY: prod (Kamatera) has no Postgres, so the live crcp.agentabrams.com serves data-backed pages
// from static snapshots with a DB->snapshot fallback (same pattern as data/condos-redfin.json /
// data/closed-sales.json). fetch-sfr-redfin.js only writes to the DB, so this is the companion
// exporter that produces the file /api/residential falls back to on prod.
//
// Run AFTER fetch-sfr-redfin.js:  node scripts/export-sfr-snapshot.js
'use strict';
const fs = require('fs');
const path = require('path');
const { Pool } = require('pg');

const ROOT = path.join(__dirname, '..');
const OUT = path.join(ROOT, 'data', 'sfr-redfin.json');
const pool = new Pool({ host: '/tmp', port: 5432, database: 'cre', user: process.env.USER || 'stevestudio2' });

(async () => {
  // Tight, card-safe column projection. listing_text is the constant label "Single Family
  // Residential" for every gis-csv row, so it's dropped (the client tags type itself).
  const { rows } = await pool.query(`
    SELECT id, address, city, zip, price, beds, baths, sqft, year_built,
           firm_name, broker_name, source, lat, lng, created_at
      FROM sfr
     WHERE price > 0
     ORDER BY created_at DESC, price DESC`);

  const payload = {
    meta: {
      source: 'Redfin LA County single-family for-sale — feed-first (gis-csv), via Browserbase',
      note: 'Listing agent/firm only present where Redfin exposed it (fetch-sfr-agents.js); NULL otherwise (honest labeling).',
      fetched_at: new Date().toISOString(),
      count: rows.length,
    },
    sfr: rows,
  };
  fs.writeFileSync(OUT, JSON.stringify(payload, null, 2));
  const kb = (fs.statSync(OUT).size / 1024).toFixed(0);
  console.log(JSON.stringify({ count: rows.length, out: OUT, size_kb: +kb }));
  await pool.end();
})().catch(e => { console.error(e.message); process.exit(1); });