← back to Nationalrealestate
src/ingest/brokers/ca_dre.ts
56 lines
/**
* CA — Department of Real Estate current licensee master file. FREE bulk download
* (secure.dre.ca.gov/datafile/CurrList.zip -> CurrList.csv, ~431k rows, refreshed
* daily). Columns are snake_case already (lic_type, lastname_primary, ...).
*
* lic_type='Corporation' -> brokerage firm (name lives in lastname_primary)
* lic_type='Broker'|'Salesperson' -> individual licensee; employing firm via
* related_lic_number + related_lastname_primary
* lic_type='Officer' -> a corporation's responsible-officer designation;
* skipped (that person also holds a Broker license
* listed on its own row) to avoid double-counting.
*
* Carries city (+ county_name) so the LA-region broker/firm surface can scope locally.
*/
import { execFileSync } from 'node:child_process';
import { join } from 'node:path';
import { download, DOWNLOADS } from '../run.ts';
import { parseCsvObjects } from './csv.ts';
import type { BrokerRow, FetchedFile, StateAdapter } from './types.ts';
const URL = 'https://secure.dre.ca.gov/datafile/CurrList.zip';
export const caDre: StateAdapter = {
state: 'CA',
source: 'ca_dre',
url: URL,
async fetch(): Promise<FetchedFile[]> {
const { path } = await download(URL, 'ca_dre_CurrList.zip');
execFileSync('unzip', ['-o', '-q', path, '-d', DOWNLOADS]);
return [{ path: join(DOWNLOADS, 'CurrList.csv'), sha256: '' }];
},
*parse(files: FetchedFile[]): Generator<BrokerRow> {
for (const o of parseCsvObjects(files[0].path)) {
if (!o.lic_number) continue;
const type = o.lic_type || '';
const city = o.city || null;
const st = (o.state || '').toUpperCase() || null;
const status = o.lic_status || null;
if (type === 'Corporation') {
const name = o.lastname_primary; // company name lives in the primary-name column
if (!name) continue;
yield { kind: 'firm', name, license_no: o.lic_number, license_type: type, license_status: status, city, state_code: st };
} else if (type === 'Broker' || type === 'Salesperson') {
const name = [o.firstname_secondary, o.lastname_primary].filter(Boolean).join(' ');
if (!name) continue;
yield {
kind: 'broker', name, license_no: o.lic_number, license_type: type, license_status: status,
firm_name: o.related_lastname_primary || null, firm_license_no: o.related_lic_number || null,
city, state_code: st,
};
}
// Officer / Corporation-affiliate / other rows skipped
}
},
};