Use Zillow parcel hints for CAD lookup

This commit is contained in:
2026-03-28 03:55:56 -05:00
parent ece8fc548f
commit b77134ced5
11 changed files with 438 additions and 18 deletions
+69 -15
View File
@@ -1,6 +1,7 @@
import os from "node:os";
import path from "node:path";
import { extractZillowIdentifierHints } from "../../web-automation/scripts/zillow-identifiers.js";
import { discoverListingSources, type ListingDiscoveryResult } from "./listing-discovery.js";
import { extractPhotoData, type PhotoExtractionResult, type PhotoSource } from "./photo-review.js";
import { resolvePublicRecords, type PublicRecordsResolution } from "./public-records.js";
@@ -36,6 +37,7 @@ interface AssessPropertyDeps {
renderReportPdfFn?: typeof renderReportPdf;
discoverListingSourcesFn?: typeof discoverListingSources;
extractPhotoDataFn?: typeof extractPhotoData;
extractZillowIdentifierHintsFn?: typeof extractZillowIdentifierHints;
}
interface PurposeGuidance {
@@ -195,14 +197,17 @@ function inferSourceFromUrl(rawUrl: string): PhotoSource | null {
}
}
async function resolvePhotoReview(
options: AssessPropertyOptions,
discoverListingSourcesFn: typeof discoverListingSources,
extractPhotoDataFn: typeof extractPhotoData
): Promise<{
interface ResolvedListingCandidates {
attempts: string[];
listingUrls: Array<{ label: string; url: string }>;
photoReview: Record<string, unknown>;
}> {
zillowUrl: string | null;
harUrl: string | null;
}
async function resolveListingCandidates(
options: AssessPropertyOptions,
discoverListingSourcesFn: typeof discoverListingSources
): Promise<ResolvedListingCandidates> {
const attempts: string[] = [];
const listingUrls: Array<{ label: string; url: string }> = [];
@@ -241,9 +246,27 @@ async function resolvePhotoReview(
addListingUrl("Discovered HAR Listing", harUrl);
}
return {
attempts,
listingUrls,
zillowUrl,
harUrl,
};
}
async function resolvePhotoReview(
listingCandidates: ResolvedListingCandidates,
extractPhotoDataFn: typeof extractPhotoData,
additionalAttempts: string[] = []
): Promise<{
listingUrls: Array<{ label: string; url: string }>;
photoReview: Record<string, unknown>;
}> {
const attempts: string[] = [...listingCandidates.attempts, ...additionalAttempts];
const listingUrls = [...listingCandidates.listingUrls];
const candidates: Array<{ source: PhotoSource; url: string }> = [];
if (zillowUrl) candidates.push({ source: "zillow", url: zillowUrl });
if (harUrl) candidates.push({ source: "har", url: harUrl });
if (listingCandidates.zillowUrl) candidates.push({ source: "zillow", url: listingCandidates.zillowUrl });
if (listingCandidates.harUrl) candidates.push({ source: "har", url: listingCandidates.harUrl });
let extracted: PhotoExtractionResult | null = null;
for (const candidate of candidates) {
@@ -411,21 +434,52 @@ export async function assessProperty(
const renderReportPdfFn = deps.renderReportPdfFn || renderReportPdf;
const discoverListingSourcesFn = deps.discoverListingSourcesFn || discoverListingSources;
const extractPhotoDataFn = deps.extractPhotoDataFn || extractPhotoData;
const extractZillowIdentifierHintsFn =
deps.extractZillowIdentifierHintsFn || extractZillowIdentifierHints;
const listingCandidates = await resolveListingCandidates(
{ ...options, assessmentPurpose: purpose },
discoverListingSourcesFn
);
const identifierAttempts: string[] = [];
let effectiveParcelId = options.parcelId;
if (!effectiveParcelId && listingCandidates.zillowUrl) {
try {
const hints = await extractZillowIdentifierHintsFn(listingCandidates.zillowUrl);
effectiveParcelId = hints.parcelId || hints.apn || effectiveParcelId;
if (Array.isArray(hints.notes) && hints.notes.length) {
identifierAttempts.push(...hints.notes);
}
} catch (error) {
identifierAttempts.push(
`Zillow parcel/APN extraction failed: ${error instanceof Error ? error.message : String(error)}`
);
}
}
const effectiveListingSourceUrl =
options.listingSourceUrl || listingCandidates.zillowUrl || listingCandidates.harUrl || undefined;
const publicRecords = await resolvePublicRecordsFn(options.address, {
parcelId: options.parcelId,
parcelId: effectiveParcelId,
listingGeoId: options.listingGeoId,
listingSourceUrl: options.listingSourceUrl
listingSourceUrl: effectiveListingSourceUrl
});
const photoResolution = await resolvePhotoReview(
{ ...options, assessmentPurpose: purpose },
discoverListingSourcesFn,
extractPhotoDataFn
listingCandidates,
extractPhotoDataFn,
identifierAttempts
);
const reportPayload = buildAssessmentReportPayload(
{ ...options, assessmentPurpose: purpose },
{
...options,
assessmentPurpose: purpose,
parcelId: effectiveParcelId,
listingSourceUrl: effectiveListingSourceUrl
},
publicRecords,
photoResolution.listingUrls,
photoResolution.photoReview