← back to Homesonspec

apps/workers/src/lennar-mac-fetch.mts

42 lines

// Mac-side Lennar fetch (Mac IP is NOT 403-blocked by lennar.com, unlike Kamatera).
// Drives the real lennarAdapter.fetch()+extract() (full robots/rate-limit politeness via
// LiveFetcher), collects per-home {invId, street, city, state, images}, writes JSONL for
// application to the Kamatera DB. NO DB writes here — pure fetch→extract→file. [TK-10005]
import { lennarAdapter } from "@homesonspec/collector-lennar";
import { writeFileSync, appendFileSync } from "fs";

const STATES = ["california", "colorado", "florida", "arizona", "alabama", "arkansas", "delaware"];
const OUT = "/tmp/lennar-images.jsonl";
writeFileSync(OUT, "");
const registry = { key: "lennar-site", collectionMethod: "CRAWL", rateLimitRpm: 60, mediaRights: "LICENSED" } as any;

let grandTotal = 0, grandImg = 0;
for (const st of STATES) {
  process.env.LENNAR_STATE_FILTER = st;
  let sTotal = 0, sImg = 0;
  try {
    for await (const page of lennarAdapter.fetch({ mode: "live", fixtureDir: "/tmp/none", registry })) {
      const out = lennarAdapter.extract(page);
      for (const r of out.records) {
        if (r.entityType !== "inventory_home") continue;
        sTotal++;
        const f: any = r.fields;
        const imgs: string[] = f.images?.value ?? [];
        const invId: string | null = f.builderInventoryId?.value ?? null;
        const street: string | null = f.street?.value ?? null;
        const city: string | null = f.city?.value ?? null;
        const state: string | null = f.state?.value ?? null;
        if (imgs.length && invId && street) {
          sImg++;
          appendFileSync(OUT, JSON.stringify({ invId, street, city, state, images: imgs }) + "\n");
        }
      }
    }
  } catch (e) {
    console.error(`  ${st} ERROR:`, (e as Error).message);
  }
  grandTotal += sTotal; grandImg += sImg;
  console.log(`${st}: ${sImg}/${sTotal} homes with images`);
}
console.log(`TOTAL: ${grandImg}/${grandTotal} written to ${OUT}`);