[object Object]

← back to Ventura Corridor

iter 62: walk-route priority-aware ordering — /api/pitches/walk-route?sort=(geographic|priority); priority sort joins through v_building_roster and orders by inline-computed priority_score (unpitched / GREATEST(miles, 0.05)), groups all doors at same building together so Steve walks highest-leverage building floor-by-floor before moving on; also filters out merged_into businesses; /walk-route.html gets a 'route order' selector at the top of the controls — Geographic (E↔W lap) vs ⚡ Priority (highest-leverage bldgs first); direction selector auto-hides when in priority mode (irrelevant); top 8 priority slots all from 15821 Ventura (score 1183, 556 unpitched doors)

31c424310a98bc175a4e16c230fa36e7cc32abde · 2026-05-06 14:30:20 -0700 · SteveStudio2

Files touched

Diff

commit 31c424310a98bc175a4e16c230fa36e7cc32abde
Author: SteveStudio2 <steve@designerwallcoverings.com>
Date:   Wed May 6 14:30:20 2026 -0700

    iter 62: walk-route priority-aware ordering — /api/pitches/walk-route?sort=(geographic|priority); priority sort joins through v_building_roster and orders by inline-computed priority_score (unpitched / GREATEST(miles, 0.05)), groups all doors at same building together so Steve walks highest-leverage building floor-by-floor before moving on; also filters out merged_into businesses; /walk-route.html gets a 'route order' selector at the top of the controls — Geographic (E↔W lap) vs ⚡ Priority (highest-leverage bldgs first); direction selector auto-hides when in priority mode (irrelevant); top 8 priority slots all from 15821 Ventura (score 1183, 556 unpitched doors)
---
 public/walk-route.html | 13 ++++++++++++-
 src/server/index.ts    | 27 +++++++++++++++++++++++++--
 2 files changed, 37 insertions(+), 3 deletions(-)

diff --git a/public/walk-route.html b/public/walk-route.html
index 147955b..600d39d 100644
--- a/public/walk-route.html
+++ b/public/walk-route.html
@@ -165,6 +165,13 @@
 
 <div class="controls">
   <div class="ctrl-group">
+    <label>route order</label>
+    <select id="sort">
+      <option value="geographic" selected>Geographic (E↔W lap)</option>
+      <option value="priority">⚡ Priority (highest-leverage bldgs first)</option>
+    </select>
+  </div>
+  <div class="ctrl-group" id="direction-group">
     <label>direction</label>
     <select id="direction">
       <option value="east">East (Sherman Oaks → Encino)</option>
@@ -254,10 +261,13 @@ const PITCH_SCRIPT = {
 let allRows = [];
 
 async function load() {
+  const sort = document.getElementById('sort').value;
   const direction = document.getElementById('direction').value;
   const limit = document.getElementById('limit').value;
   const filterStatus = document.getElementById('filter-status').value;
-  const url = `/api/pitches/walk-route?direction=${direction}&limit=${limit}`;
+  // Hide direction selector when sorting by priority — it's irrelevant
+  document.getElementById('direction-group').style.display = sort === 'priority' ? 'none' : '';
+  const url = `/api/pitches/walk-route?sort=${sort}&direction=${direction}&limit=${limit}`;
   const res = await fetch(url);
   if (!res.ok) {
     document.getElementById('route').innerHTML = `<div class="empty"><div class="big">Could not load route.</div><div>${res.status} ${res.statusText}</div></div>`;
@@ -385,6 +395,7 @@ async function markAllSent(stopOrder) {
 
 function reload() { load(); }
 
+document.getElementById('sort').addEventListener('change', load);
 document.getElementById('direction').addEventListener('change', load);
 document.getElementById('limit').addEventListener('change', load);
 document.getElementById('filter-status').addEventListener('change', load);
diff --git a/src/server/index.ts b/src/server/index.ts
index 85ba2fe..7c8d907 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -629,19 +629,42 @@ app.get('/api/pitches/walk-route', async (req, res) => {
   try {
     const limit = Math.min(parseInt(String(req.query.limit ?? '30'), 10) || 30, 80);
     const direction = String(req.query.direction || 'east'); // 'east' | 'west'
+    const sort = String(req.query.sort || 'geographic');     // 'geographic' | 'priority'
+
+    let orderClause: string;
+    let extraSelect = '';
+    if (sort === 'priority') {
+      // Group all doors at the same building together, ordered by that building's priority score.
+      // priority_score is computed inline (same formula as /api/buildings/priority).
+      extraSelect = `,
+        TRIM(regexp_replace(b.address, '\\s*(SUITE|STE|UNIT|#).*$', '', 'i')) AS bldg_address,
+        ROUND((br.unpitched::numeric / GREATEST(br.dw_miles, 0.05)::numeric)::numeric, 1) AS priority_score`;
+      orderClause = `(br.unpitched::numeric / GREATEST(br.dw_miles, 0.05)::numeric) DESC NULLS LAST, br.bldg_address ASC, p.priority ASC`;
+    } else {
+      orderClause = `b.lng ${direction === 'west' ? 'DESC' : 'ASC'}, b.lat ASC`;
+    }
+
+    const joinClause = sort === 'priority'
+      ? `LEFT JOIN v_building_roster br
+           ON br.bldg_address = TRIM(regexp_replace(b.address, '\\s*(SUITE|STE|UNIT|#).*$', '', 'i'))`
+      : '';
+
     const r = await query(`
       SELECT p.id, p.pitch_type, p.priority, p.status, p.dw_proximity, p.li_message,
              b.name, b.address, b.city, b.zip, b.lat, b.lng,
              p.research_links->>'gmaps' AS gmaps_url
+             ${extraSelect}
       FROM pitches p
       JOIN businesses b ON b.id = p.business_id
+      ${joinClause}
       WHERE p.dw_proximity IN ('same_building','same_block','walk_2min','walk_5min','walk_10min')
         AND p.status NOT IN ('skip','sent','won','lost')
         AND b.lat IS NOT NULL AND b.lng IS NOT NULL
-      ORDER BY b.lng ${direction === 'west' ? 'DESC' : 'ASC'}, b.lat ASC
+        AND b.merged_into IS NULL
+      ORDER BY ${orderClause}
       LIMIT $1
     `, [limit]);
-    res.json({ count: r.rowCount, direction, rows: r.rows });
+    res.json({ count: r.rowCount, direction, sort, rows: r.rows });
   } catch (e: any) {
     res.status(500).json({ error: e.message });
   }

← 40a7c21 iter 61: building prioritization 'walk here next' — /api/bui  ·  back to Ventura Corridor  ·  iter 63: 🏇 crawl derby — racetrack-style live progress view 1c3e46f →