Use Zillow parcel hints for CAD lookup
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user