← back to La Socrata Ingester
yoloforever c4: newsworthy permit deals feed (scripts/deals-feed.js)
909b17169387acbae7904ca0faa782484aaaae56 · 2026-08-12 06:55:49 -0700 · steve
Surfaces top high-value permits (last 90d, >=$1M, issued) as human-readable
headlines + structured JSON feed for RENTV/RE-news synergy. Enriched w/ parcel
value + council-member name. Fixed date formatting + 'built 0'. New local surface.
Files touched
Diff
commit 909b17169387acbae7904ca0faa782484aaaae56
Author: steve <steve@designerwallcoverings.com>
Date: Wed Aug 12 06:55:49 2026 -0700
yoloforever c4: newsworthy permit deals feed (scripts/deals-feed.js)
Surfaces top high-value permits (last 90d, >=$1M, issued) as human-readable
headlines + structured JSON feed for RENTV/RE-news synergy. Enriched w/ parcel
value + council-member name. Fixed date formatting + 'built 0'. New local surface.
---
scripts/deals-feed.js | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 87 insertions(+)
diff --git a/scripts/deals-feed.js b/scripts/deals-feed.js
new file mode 100644
index 0000000..b60ed12
--- /dev/null
+++ b/scripts/deals-feed.js
@@ -0,0 +1,87 @@
+// Newsworthy permit "deals" feed (READ-ONLY, $0 local). Surfaces the biggest recent
+// high-value LA building permits as human-readable headlines + a structured JSON feed
+// for RE-news / RENTV synergy. Enriched with parcel value + council-member name.
+import { q, pool } from '../src/db.js';
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const OUT = path.resolve(__dirname, '../tmp/leads');
+const money = v => v == null ? null : '$' + Math.round(Number(v)).toLocaleString();
+const millions = v => '$' + (Number(v) / 1e6).toFixed(Number(v) >= 1e7 ? 0 : 1) + 'M';
+
+const DAYS = Number(process.env.DEALS_DAYS || 90);
+const MIN_VALUE = Number(process.env.DEALS_MIN || 1_000_000);
+
+// permit_type -> a readable "deal kind"
+const KIND = {
+ 'Bldg-New': 'ground-up construction',
+ 'Bldg-Addition': 'major addition',
+ 'Bldg-Alter/Repair': 'major renovation',
+ 'Swimming-Pool/Spa': 'pool/spa',
+ 'Bldg-Demolition': 'demolition',
+ 'Grading': 'site grading',
+};
+const useKind = (t) => KIND[t] || (t || 'project').replace(/^Bldg-/, '').toLowerCase();
+
+async function cdNames() {
+ const rows = (await q(`SELECT name FROM la_gis_features WHERE layer='council_district' AND name IS NOT NULL`)).rows;
+ const m = {};
+ for (const r of rows) { const x = String(r.name).match(/^\s*(\d+)\s*[-–]\s*(.+)$/); if (x) m[x[1]] = x[2].trim(); }
+ return m;
+}
+
+async function main() {
+ const stamp = (await q(`SELECT to_char(now(),'YYYY-MM-DD') d`)).rows[0].d;
+ const names = await cdNames();
+ fs.mkdirSync(OUT, { recursive: true });
+
+ const rows = (await q(`
+ SELECT p.permit_nbr, to_char(p.issue_date,'YYYY-MM-DD') AS issued, p.primary_address AS address, p.zip_code AS zip,
+ p.council_district AS cd, p.permit_type AS work_type, p.permit_sub_type AS building_type,
+ p.use_desc, p.valuation AS project_value, p.work_desc AS scope, p.lat, p.lon,
+ a.total_value AS assessed_value, a.year_built, a.sqft_main AS sqft,
+ permit_lead_score(p.issue_date,p.valuation,p.permit_type,p.permit_sub_type,p.status_desc) AS lead_score
+ FROM la_building_permits_raw p
+ LEFT JOIN la_assessor_parcels_raw a ON a.ain=p.apn AND a.roll_year='2025'
+ WHERE p.dataset_id='pi9x-tg5x' AND p.status_desc='Issued'
+ AND p.issue_date >= now() - ($1||' days')::interval
+ AND p.valuation >= $2 AND p.primary_address IS NOT NULL
+ ORDER BY p.valuation DESC NULLS LAST
+ LIMIT 60`, [DAYS, MIN_VALUE])).rows;
+
+ const deals = rows.map(r => {
+ const member = names[r.cd];
+ const cdTag = r.cd ? `CD ${r.cd}${member ? ' · ' + member : ''}` : '';
+ const headline = `${millions(r.project_value)} ${useKind(r.work_type)} — ${r.address}${r.zip ? ', ' + r.zip : ''}${cdTag ? ' (' + cdTag + ')' : ''}`;
+ return {
+ headline, permit_nbr: r.permit_nbr, issued: r.issued, address: r.address, zip: r.zip,
+ council_district: r.cd, council_member: member || null, kind: useKind(r.work_type),
+ work_type: r.work_type, building_type: r.building_type, project_value: Number(r.project_value),
+ assessed_value: r.assessed_value == null ? null : Number(r.assessed_value),
+ year_built: (r.year_built && String(r.year_built) !== '0') ? r.year_built : null,
+ sqft: r.sqft == null ? null : Number(r.sqft),
+ lead_score: r.lead_score, scope: r.scope,
+ map: r.lat && r.lon ? `https://www.google.com/maps?q=${r.lat},${r.lon}` : null,
+ };
+ });
+
+ const feed = { generated: stamp, window_days: DAYS, min_value: MIN_VALUE, count: deals.length, deals };
+ fs.writeFileSync(path.join(OUT, `deals-feed-${stamp}.json`), JSON.stringify(feed, null, 2));
+
+ // human digest
+ console.log(`\nLA permit deals — top high-value permits issued in the last ${DAYS} days (>= ${money(MIN_VALUE)})\n`);
+ deals.slice(0, 15).forEach((d, i) => {
+ console.log(` ${String(i + 1).padStart(2)}. ${d.headline}`);
+ const bits = [];
+ if (d.assessed_value) bits.push(`parcel assessed ${millions(d.assessed_value)}`);
+ if (d.year_built) bits.push(`built ${d.year_built}`);
+ if (d.sqft) bits.push(`${Number(d.sqft).toLocaleString()} sqft`);
+ bits.push(`issued ${d.issued}`); bits.push(`lead ${d.lead_score}`);
+ console.log(` ${bits.join(' · ')}`);
+ });
+ console.log(`\n ${deals.length} deals → tmp/leads/deals-feed-${stamp}.json\n`);
+}
+
+main().catch(e => { console.error('deals-feed error:', e.message); process.exitCode = 1; }).finally(() => pool.end());
← 0377c1a yoloforever c3 FIX (Cody): green-able canary + real CSLB loa
·
back to La Socrata Ingester
·
yoloforever c4 FIX (Cody): deals feed ranks by lead_score, d 60f8851 →