[object Object]

← back to Commercialrealestate

SFV pool: mls.html ?shortlist=sfv-pool deep-link (lands on main CRCP with items checked off) + repoint email/page links + keep-building-images loop

5f4acf6e49cfcf87605a9f75b31d4b3c21d9653a · 2026-08-26 09:09:27 -0700 · Steve Abrams

Files touched

Diff

commit 5f4acf6e49cfcf87605a9f75b31d4b3c21d9653a
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Aug 26 09:09:27 2026 -0700

    SFV pool: mls.html ?shortlist=sfv-pool deep-link (lands on main CRCP with items checked off) + repoint email/page links + keep-building-images loop
---
 public/mls.html                 | 14 ++++++++++++++
 public/sfv-pool.html            |  1 +
 scripts/keep-building-images.sh | 28 ++++++++++++++++++++++++++++
 scripts/sfv-pool-report.js      |  8 ++++++--
 4 files changed, 49 insertions(+), 2 deletions(-)

diff --git a/public/mls.html b/public/mls.html
index 20a0141..d9aefb3 100644
--- a/public/mls.html
+++ b/public/mls.html
@@ -706,6 +706,20 @@ document.addEventListener('click',e=>{ const cp=e.target.closest('[data-copy]');
 let _qT; $('#q').addEventListener('input',()=>{ clearTimeout(_qT); _qT=setTimeout(()=>{ q=$('#q').value.trim(); render(); },140); });
 // deep-link ?q= (from the global nav search) — pre-fill + filter on load
 (function(){ const _q=new URLSearchParams(location.search).get('q'); if(_q){ const b=$('#q'); if(b){ b.value=_q; q=_q.trim(); } } })();
+// deep-link ?shortlist=sfv-pool — land here with the SFV $700k-$1.2M set already CHECKED OFF (starred) +
+// the Shortlist filter on, so the email / pool-page "open on CRCP" link shows exactly those listings here.
+// Named preset (not ~950 ids in the URL): fetch the current id set from /api/sfv-pool and star them.
+(async function(){
+  const preset=new URLSearchParams(location.search).get('shortlist'); if(preset!=='sfv-pool') return;
+  try{
+    const j=await (await fetch('/api/sfv-pool')).json();
+    const ids=[...(j.withPool||[]),...(j.withoutPool||[]),...(j.unknown||[])].map(r=>r.id);
+    ids.forEach(id=>SHORT.add(id)); saveShort();
+    F.shortlist=true; if(typeof updateShortBtn==='function') updateShortBtn();
+    const sc=$('#f_shortlist'); if(sc){ sc.checked=true; }   // reflect the filter checkbox if present
+    if(typeof render==='function') render();
+  }catch(e){ console.warn('sfv-pool preset failed', e); }
+})();
 function exportCSV(){ const rows=filtered(); const cols=visCols(); const cv=(r,c)=>{let v=cval(r,c); v=(v==null?'':String(v)); return '"'+v.replace(/"/g,'""')+'"';};
   const csv=cols.map(c=>'"'+c.l+'"').join(',')+'\n'+rows.map(r=>cols.map(c=>cv(r,c)).join(',')).join('\n');
   const a=document.createElement('a'); a.href=URL.createObjectURL(new Blob([csv],{type:'text/csv'})); a.download='cre-mls-'+rows.length+'.csv'; a.click(); URL.revokeObjectURL(a.href); }
diff --git a/public/sfv-pool.html b/public/sfv-pool.html
index 445be6a..baabd0d 100644
--- a/public/sfv-pool.html
+++ b/public/sfv-pool.html
@@ -74,6 +74,7 @@
 <div class="wrap">
   <h1>🏊 San Fernando Valley Homes · $700k – $1.2M</h1>
   <div class="sub">Active single-family listings, split by swimming pool ·
+    <a href="/mls.html?shortlist=sfv-pool">open these on CRCP (checked off) →</a> ·
     <a href="/mls.html">all LA listings →</a> · <span id="src"></span></div>
 
   <div class="stats" id="stats"></div>
diff --git a/scripts/keep-building-images.sh b/scripts/keep-building-images.sh
new file mode 100755
index 0000000..090d19e
--- /dev/null
+++ b/scripts/keep-building-images.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+# keep-building-images.sh — repeatedly run the (resumable) SFV pool/photo enrichment until image + pool
+# coverage plateaus. ONE Chrome session at a time (concurrent sessions from one IP just compound Redfin's
+# rate-block). Each pass re-fetches only rows still missing a photo (photo_url IS NULL), so coverage only
+# grows; it also refreshes data/sfr-pools.json each pass so the live page/prod snapshot keep filling in.
+set -uo pipefail
+cd "$HOME/Projects/commercialrealestate" || exit 1
+export NODE_PATH="$HOME/.claude/skills/browserbase/node_modules"
+LOG=tmp/keep-images.log
+mkdir -p tmp
+count_photos(){ psql -h /tmp -d cre -tA -c "SELECT count(*) FILTER (WHERE photo_url IS NOT NULL) FROM sfr_pool p JOIN sfr s ON s.id=p.id WHERE s.status='active' AND s.price::int BETWEEN 700000 AND 1200000 AND s.city IN ('Encino','Tarzana','Woodland Hills','Sherman Oaks','Van Nuys','North Hollywood','Studio City','Reseda','Northridge','Granada Hills','Canoga Park','Winnetka','West Hills','Chatsworth','Panorama City','Sun Valley','Valley Village','Valley Glen','North Hills','Porter Ranch','Arleta','Pacoima','Sylmar','Mission Hills','Lake Balboa','Toluca Lake','Sunland','Tujunga','Shadow Hills');" 2>/dev/null | tr -d ' '; }
+
+echo "===== [$(date '+%F %T')] keep-building-images START =====" >> "$LOG"
+stale=0
+for round in $(seq 1 12); do
+  before=$(count_photos)
+  echo "[$(date '+%T')] round $round: $before photos before" >> "$LOG"
+  CAP=1500 node scripts/enrich-sfr-pools.js >> "$LOG" 2>&1
+  after=$(count_photos)
+  gained=$(( ${after:-0} - ${before:-0} ))
+  echo "[$(date '+%T')] round $round: $after photos after (+$gained)" >> "$LOG"
+  # push the refreshed snapshot to prod so the live page keeps filling in
+  rsync -z data/sfr-pools.json root@45.61.58.125:/root/public-projects/commercialrealestate/data/sfr-pools.json >> "$LOG" 2>&1 || true
+  if [ "${gained:-0}" -lt 5 ]; then stale=$((stale+1)); else stale=0; fi
+  if [ "$stale" -ge 2 ]; then echo "[$(date '+%T')] plateaued ($after photos) — stopping" >> "$LOG"; break; fi
+  sleep 120   # cool-down between passes to ease Redfin's rate limit
+done
+echo "===== [$(date '+%F %T')] keep-building-images DONE — $(count_photos) photos =====" >> "$LOG"
diff --git a/scripts/sfv-pool-report.js b/scripts/sfv-pool-report.js
index d93c653..b688ade 100644
--- a/scripts/sfv-pool-report.js
+++ b/scripts/sfv-pool-report.js
@@ -13,6 +13,9 @@ const SFV_CITIES = ['Encino','Tarzana','Woodland Hills','Sherman Oaks','Van Nuys
   'Pacoima','Sylmar','Mission Hills','Lake Balboa','Toluca Lake','Sunland','Tujunga','Shadow Hills'];
 const PMIN = 700000, PMAX = 1200000;
 const DETAILS_URL = process.env.SFV_POOL_URL || 'https://crcp.agentabrams.com/sfv-pool.html';
+// primary "open on CRCP" link: lands on the MAIN CRCP listings page with exactly these SFV $700k–$1.2M
+// items CHECKED OFF (starred) + the Shortlist filter on (mls.html ?shortlist=sfv-pool deep-link).
+const CRCP_URL = process.env.SFV_CRCP_URL || 'https://crcp.agentabrams.com/mls.html?shortlist=sfv-pool';
 const TOP = +(process.env.TOP || 30);   // rows per section in the email
 const esc = s => String(s ?? '').replace(/[&<>"]/g, c => ({ '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;' }[c]));
 const money = n => '$' + Number(n).toLocaleString();
@@ -57,7 +60,7 @@ function table(rows, kind) {
   <div style="max-width:820px;margin:0 auto">
     <h1 style="font-size:20px;margin:0 0 3px">🏊 SFV Homes $700k–$1.2M <span style="color:#3fb950">· ${D}</span></h1>
     <div style="color:#8b949e;font-size:12px;margin-bottom:16px">San Fernando Valley · active single-family · split by swimming pool ·
-      <a href="${DETAILS_URL}" style="color:#58a6ff">see all + filter →</a></div>
+      <a href="${CRCP_URL}" style="color:#58a6ff">open these on CRCP (checked off) →</a></div>
     <table style="width:100%;border-spacing:8px;margin:-8px 0 18px"><tr>
       ${stat(rows.length, 'Total in band', '#e6edf3')}
       ${stat(withPool.length, '🏊 With Pool', '#3fb950')}
@@ -65,7 +68,8 @@ function table(rows, kind) {
       ${stat(pending.length, 'Pool Pending', '#8b949e')}
     </tr></table>
     <div style="text-align:center;margin:0 0 22px">
-      <a href="${DETAILS_URL}" style="display:inline-block;background:#238636;color:#fff;text-decoration:none;font-weight:600;font-size:14px;padding:11px 22px;border-radius:8px">See every listing + pool / city / price filters →</a>
+      <a href="${CRCP_URL}" style="display:inline-block;background:#238636;color:#fff;text-decoration:none;font-weight:600;font-size:14px;padding:11px 22px;border-radius:8px">Open these ${rows.length} on CRCP — checked off →</a>
+      <div style="margin-top:8px"><a href="${DETAILS_URL}" style="color:#58a6ff;font-size:12px">or the photo view with pool / city filters →</a></div>
     </div>
     <h3 style="font-size:13px;text-transform:uppercase;color:#8b949e;margin:18px 0 6px">🏊 With Pool <span style="color:#3fb950">(${withPool.length})</span> · top ${Math.min(TOP, withPool.length)} by price</h3>
     ${table(withPool, 'pool')}

← 5ed3148 auto-data-snapshot: 2026-08-26T09:01:00 (1 data files) — dat  ·  back to Commercialrealestate  ·  Apply Nav Agent to CRCP (9 pages incl. sfv-pool): field togg 84c2a7c →