[object Object]

← back to Homesonspec

geocode: one-time dr-horton coord backfill from CACHED snapshots (yolo iter-4)

fa040b5f7b246e6a5742fa835ab6533047f59893 · 2026-07-29 00:42:39 -0700 · Steve Abrams

Backfills the existing ~22.5k dr-horton homes' coords with ZERO HTTP: reads each RawSnapshot HTML
already on disk, extracts the community ld+json coord (same logic as the adapter), and UPDATEs homes
by exact sourceUrl match. Beats re-fetching thousands of pages (heavy, impolite, races the loop).
State-gated + US bbox. $0. Raised execFileSync maxBuffer for the 15,933-snapshot list query.

Files touched

Diff

commit fa040b5f7b246e6a5742fa835ab6533047f59893
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Jul 29 00:42:39 2026 -0700

    geocode: one-time dr-horton coord backfill from CACHED snapshots (yolo iter-4)
    
    Backfills the existing ~22.5k dr-horton homes' coords with ZERO HTTP: reads each RawSnapshot HTML
    already on disk, extracts the community ld+json coord (same logic as the adapter), and UPDATEs homes
    by exact sourceUrl match. Beats re-fetching thousands of pages (heavy, impolite, races the loop).
    State-gated + US bbox. $0. Raised execFileSync maxBuffer for the 15,933-snapshot list query.
---
 scripts/backfill-drh-geo-from-cache.mjs | 64 +++++++++++++++++++++++++++++++++
 1 file changed, 64 insertions(+)

diff --git a/scripts/backfill-drh-geo-from-cache.mjs b/scripts/backfill-drh-geo-from-cache.mjs
new file mode 100644
index 00000000..f382d4c5
--- /dev/null
+++ b/scripts/backfill-drh-geo-from-cache.mjs
@@ -0,0 +1,64 @@
+#!/usr/bin/env node
+// One-time backfill of dr-horton home coordinates FROM CACHED SNAPSHOTS — $0, ZERO HTTP.
+// The adapter now captures each community's in-feed ld+json coord (yolo iter-4), but the existing
+// ~22.5k dr-horton homes were extracted before that. Rather than re-fetch thousands of pages
+// (heavy + impolite + races the loop), read the RawSnapshot HTML already on disk, extract the
+// community coord, and UPDATE its homes by sourceUrl (exact match — no name ambiguity).
+// State-gated + coarse US bbox (a wrong pin is worse than null).
+import { execFileSync } from "node:child_process";
+import { readFileSync } from "node:fs";
+
+const DB = process.env.DATABASE_URL || "postgresql://macstudio3@localhost/homesonspec?host=/tmp";
+const psql = (sql) => execFileSync("psql", [DB, "-tAc", sql], { encoding: "utf8", maxBuffer: 256 * 1024 * 1024 }).trim();
+const q = (s) => String(s).replace(/'/g, "''");
+
+// Same community-coord logic as the dr-horton adapter (lowercase ld+json lat/lon; state-gated + US bbox).
+function communityGeo(html, state) {
+  const m = html.match(/"latitude"\s*:\s*(-?\d+(?:\.\d+)?)\s*,\s*"longitude"\s*:\s*(-?\d+(?:\.\d+)?)/);
+  if (!m) return null;
+  const lat = Number(m[1]), lon = Number(m[2]);
+  if (!Number.isFinite(lat) || !Number.isFinite(lon) || lat === 0 || lon === 0) return null;
+  if (lat < 15 || lat > 72 || lon < -180 || lon > -60) return null;
+  const region = html.match(/"addressregion"\s*:\s*"([A-Za-z]{2})"/i)?.[1]?.toUpperCase();
+  if (state && region && region !== String(state).toUpperCase()) return null;
+  return { lat, lon };
+}
+
+// dr-horton community-page snapshots (url path = /state/metro/city/community == 4 segments)
+const rows = psql(`select r.url || E'\\t' || r."storagePath" from "RawSnapshot" r
+  join "SourceRegistry" sr on sr.id=r."sourceId" where sr.key='dr-horton-site'`).split("\n").filter(Boolean);
+console.log(`dr-horton snapshots: ${rows.length}  · $0 (cache re-extract, no HTTP)`);
+
+let pinned = 0, communities = 0, skippedNoGeo = 0, missing = 0, updates = [];
+const flush = () => { if (updates.length) { psql(updates.join("\n")); updates = []; } };
+
+for (const line of rows) {
+  const [url, path] = line.split("\t");
+  if (!url || !path) continue;
+  const segs = url.replace(/^https?:\/\/[^/]+/, "").split("/").filter(Boolean);
+  if (segs.length !== 4) continue; // not a community page
+  // any still-ungeocoded homes for this community page? (also gives us the state for the gate)
+  const state = psql(`select state from "InventoryHome" h join "Builder" b on b.id=h."builderId"
+    where b.slug='dr-horton' and h."sourceUrl"='${q(url)}' and h.lat is null limit 1`);
+  if (!state) continue; // no ungeocoded homes here
+  let html;
+  try { html = readFileSync(path, "utf8"); } catch { missing++; continue; }
+  const g = communityGeo(html, state);
+  if (!g) { skippedNoGeo++; continue; }
+  communities++;
+  updates.push(`update "InventoryHome" set lat=${g.lat}, lon=${g.lon} where "sourceUrl"='${q(url)}' and lat is null;`);
+  // also the community record
+  updates.push(`update "Community" c set lat=${g.lat}, lon=${g.lon} from "InventoryHome" h
+    where h."communityId"=c.id and h."sourceUrl"='${q(url)}' and c.lat is null;`);
+  if (updates.length >= 200) { flush(); }
+  if (communities % 200 === 0) {
+    pinned = Number(psql(`select count(*) from "InventoryHome" h join "Builder" b on b.slug='dr-horton'
+      where b.id=h."builderId" and h.status='PUBLISHED' and h.lat is not null`));
+    console.log(`  ${communities} communities processed · dr-horton geocoded: ${pinned}`);
+  }
+}
+flush();
+pinned = Number(psql(`select count(*) from "InventoryHome" h join "Builder" b on b.slug='dr-horton'
+  where b.id=h."builderId" and h.status='PUBLISHED' and h.lat is not null`));
+console.log(`\nDONE: ${communities} communities geocoded, ${skippedNoGeo} had no in-feed coord, ${missing} snapshot files missing.`);
+console.log(`dr-horton now geocoded: ${pinned}. $0.`);

← d99677fd dr-horton: capture community coords from in-feed ld+json (yo  ·  back to Homesonspec  ·  publisher: promote lat/lon on the UPDATE branch too (contrar 12220514 →