clawdbot-workspace/reonomy-to-csv.js

126 lines
3.3 KiB
JavaScript

#!/usr/bin/env node
/**
* Reonomy JSON → CSV converter
* Flattens leads into one row per owner (property info repeated)
* Usage: node reonomy-to-csv.js [input.json] [output.csv]
*/
const fs = require('fs');
const path = require('path');
const inputPath = process.argv[2] || path.join(process.env.HOME, '.clawdbot/workspace/reonomy-leads-v13.json');
const outputPath = process.argv[3] || path.join(process.env.HOME, '.clawdbot/workspace/reonomy-leads.csv');
function escapeCsv(val) {
if (val == null) return '';
const str = String(val);
if (str.includes(',') || str.includes('"') || str.includes('\n')) {
return `"${str.replace(/"/g, '""')}"`;
}
return str;
}
try {
const data = JSON.parse(fs.readFileSync(inputPath, 'utf8'));
const leads = data.leads || [];
if (leads.length === 0) {
console.error('No leads found in', inputPath);
process.exit(1);
}
// Figure out max phones/emails across all owners
let maxPhones = 0;
let maxEmails = 0;
for (const lead of leads) {
for (const owner of (lead.owners || [])) {
maxPhones = Math.max(maxPhones, (owner.phones || []).length);
maxEmails = Math.max(maxEmails, (owner.emails || []).length);
}
}
// Cap at reasonable numbers
maxPhones = Math.min(maxPhones, 5);
maxEmails = Math.min(maxEmails, 5);
// Build header
const headers = [
'Property Address',
'City',
'State',
'ZIP',
'Property Type',
'Square Footage',
'Year Built',
'Units',
'Lot Size',
'Property ID',
'Scrape Date',
'Owner Name',
];
for (let i = 1; i <= maxPhones; i++) {
headers.push(`Phone ${i}`);
headers.push(`Phone ${i} Type`);
}
for (let i = 1; i <= maxEmails; i++) {
headers.push(`Email ${i}`);
}
const rows = [headers.map(escapeCsv).join(',')];
for (const lead of leads) {
const propBase = [
lead.propertyAddress || '',
lead.city || '',
lead.state || '',
lead.zip || '',
lead.propertyType || '',
lead.squareFootage || '',
lead.yearBuilt || '',
lead.units || '',
lead.lotSize || '',
lead.propertyId || '',
lead.scrapeDate ? new Date(lead.scrapeDate).toLocaleDateString() : '',
];
const owners = lead.owners || [];
if (owners.length === 0) {
// Property with no owners — still include it
rows.push([...propBase, ''].map(escapeCsv).join(','));
continue;
}
for (const owner of owners) {
const row = [...propBase, owner.name || ''];
// Phones
const phones = (owner.phones || []).slice(0, maxPhones);
for (let i = 0; i < maxPhones; i++) {
if (i < phones.length) {
row.push(phones[i].number || '');
row.push(phones[i].type || '');
} else {
row.push('');
row.push('');
}
}
// Emails
const emails = (owner.emails || []).slice(0, maxEmails);
for (let i = 0; i < maxEmails; i++) {
row.push(i < emails.length ? emails[i] : '');
}
rows.push(row.map(escapeCsv).join(','));
}
}
fs.writeFileSync(outputPath, rows.join('\n'));
console.log(`CSV written: ${outputPath}`);
console.log(`${leads.length} properties, ${rows.length - 1} owner rows`);
console.log(`Columns: ${headers.length} (${maxPhones} phone cols, ${maxEmails} email cols)`);
} catch (err) {
console.error('Error:', err.message);
process.exit(1);
}