Spaces:
Running
Running
Download bidscope/app.js from CyberMax-tools/cybermax: direct link, hf CLI and curl.
- Browser
- Download file 7.48 kB
-
https://huggingface.co/spaces/CyberMax-tools/cybermax/resolve/main/bidscope/app.js
- Command line
-
hf download hf://spaces/CyberMax-tools/cybermax/bidscope/app.js
-
curl -L -o app.js https://huggingface.co/spaces/CyberMax-tools/cybermax/resolve/main/bidscope/app.js
7.48 kB
| (async () => { | |
| const $ = (id) => document.getElementById(id); | |
| const esc = (s) => String(s ?? '').replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c])); | |
| const fmt = (n) => (n == null ? '' : Number(n).toLocaleString('en-US')); | |
| const usd = (n) => (n == null ? '' : n >= 1e9 ? `$${(n / 1e9).toFixed(2)}B` : n >= 1e6 ? `$${(n / 1e6).toFixed(1)}M` : `$${fmt(Math.round(n))}`); | |
| let d; | |
| try { d = await (await fetch('data.json')).json(); } catch (e) { $('count').textContent = 'Could not load data.'; return; } | |
| const idx = (cols) => Object.fromEntries(cols.map((c, i) => [c, i])); | |
| // decode the compact payload: dictionary-encoded columns and URL suffixes (see build.py compact()) | |
| for (const t of [d.contracts, d.opps]) { | |
| const di = Object.entries(t.dicts || {}).map(([c, vals]) => [t.cols.indexOf(c), vals]); | |
| const ui = Object.keys(d.prefix || {}).map((c) => [t.cols.indexOf(c), d.prefix[c], c === 'sam_url' ? '/view' : '/']).filter((x) => x[0] > -1); | |
| for (const r of t.rows) { | |
| for (const [i, vals] of di) if (r[i] != null) r[i] = vals[r[i]]; | |
| for (const [i, pre, suf] of ui) if (r[i]) r[i] = pre + r[i] + suf; | |
| } | |
| } | |
| const C = idx(d.contracts.cols), O = idx(d.opps.cols); | |
| const CR = d.contracts.rows, OR = d.opps.rows; | |
| $('built').textContent = `Built ${d.built.slice(0, 10)} from USAspending.gov and SAM.gov.`; | |
| const total = CR.reduce((a, r) => a + (r[C.obligated] || 0), 0); | |
| $('stats').innerHTML = [ | |
| [fmt(CR.length), 'contracts ≥ $1M ending in 12 months'], | |
| [usd(total), 'obligated on them so far'], | |
| [fmt(CR.filter((r) => r[C.days_to_end] <= 90).length), 'ending within 90 days'], | |
| [fmt(OR.length), 'open bids on SAM.gov'], | |
| ].map(([b, s]) => `<div class="stat"><b>${b}</b><span>${s}</span></div>`).join(''); | |
| let mode = 'c', page = 0, cur = []; | |
| const PER = 50; | |
| const p = new URLSearchParams(location.search); | |
| if (p.get('tab') === 'open') mode = 'o'; | |
| function fillSelects() { | |
| const rows = mode === 'c' ? CR : OR, ai = mode === 'c' ? C.agency : O.department, si = mode === 'c' ? C.place_state : O.place_state; | |
| const reset = (sel, label, vals) => { sel.innerHTML = ''; sel.add(new Option(label, '')); for (const v of vals) sel.add(new Option(v, v)); }; | |
| const count = (i) => { const m = new Map(); for (const r of rows) if (r[i]) m.set(r[i], (m.get(r[i]) || 0) + 1); return [...m.entries()].sort((a, b) => b[1] - a[1]).map((x) => x[0]); }; | |
| reset($('agency'), mode === 'c' ? 'All agencies' : 'All departments', count(ai)); | |
| reset($('state'), 'All states', count(si).sort()); | |
| $('when').parentElement && ($('when').style.display = mode === 'c' ? '' : 'none'); | |
| $('q').placeholder = mode === 'c' ? 'Search incumbent, agency, NAICS title, PIID' : 'Search title, agency, solicitation number'; | |
| $('t').tHead.innerHTML = mode === 'c' | |
| ? '<tr><th>Ends</th><th>Incumbent</th><th>Agency</th><th>NAICS</th><th>Obligated</th><th>Potential value</th><th>State</th><th>Set-aside</th></tr>' | |
| : '<tr><th>Respond by</th><th>Title</th><th>Agency</th><th>NAICS</th><th>Type</th><th>Set-aside</th><th>State</th></tr>'; | |
| $('tab-c').classList.toggle('on', mode === 'c'); $('tab-o').classList.toggle('on', mode === 'o'); | |
| } | |
| function apply() { | |
| const q = $('q').value.trim().toUpperCase(), na = $('naics').value.trim(), ag = $('agency').value, st = $('state').value, w = +$('when').value || 0; | |
| if (mode === 'c') { | |
| cur = CR.filter((r) => (!ag || r[C.agency] === ag) && (!st || r[C.place_state] === st) && (!na || String(r[C.naics] || '').startsWith(na)) && (!w || r[C.days_to_end] <= w) | |
| && (!q || [r[C.incumbent], r[C.agency], r[C.sub_agency], r[C.naics_title], r[C.piid], r[C.naics]].some((v) => v && String(v).toUpperCase().includes(q)))); | |
| } else { | |
| cur = OR.filter((r) => (!ag || r[O.department] === ag) && (!st || r[O.place_state] === st) && (!na || String(r[O.naics] || '').startsWith(na)) | |
| && (!q || [r[O.title], r[O.department], r[O.sub_tier], r[O.solicitation_number], r[O.naics]].some((v) => v && String(v).toUpperCase().includes(q)))); | |
| } | |
| page = 0; render(); | |
| const u = new URLSearchParams(); | |
| if (mode === 'o') u.set('tab', 'open'); | |
| for (const k of ['q', 'naics', 'agency', 'state', 'when']) if ($(k).value) u.set(k, $(k).value); | |
| history.replaceState(null, '', u.toString() ? `?${u}` : location.pathname); | |
| } | |
| function render() { | |
| const pages = Math.max(1, Math.ceil(cur.length / PER)); | |
| const sum = mode === 'c' ? ` · ${usd(cur.reduce((a, r) => a + (r[C.obligated] || 0), 0))} obligated` : ''; | |
| $('count').textContent = `${fmt(cur.length)} ${mode === 'c' ? 'contracts' : 'open notices'} match${sum}.`; | |
| $('page').textContent = `Page ${page + 1} of ${pages}`; | |
| $('prev').disabled = page === 0; $('next').disabled = page >= pages - 1; | |
| const slice = cur.slice(page * PER, page * PER + PER); | |
| $('t').tBodies[0].innerHTML = mode === 'c' ? slice.map((r) => { | |
| const soon = r[C.days_to_end] <= 90 ? ' soon' : ''; | |
| const opt = r[C.options_beyond_current_end] ? ' <span class="pill" title="Potential end date is later: the agency may exercise options instead of re-competing">options</span>' : ''; | |
| return `<tr><td><span class="pill${soon}">${esc(r[C.current_end_date])}</span><div class="sub">${r[C.days_to_end]} days${opt}</div></td>` | |
| + `<td>${r[C.usaspending_url] ? `<a href="${esc(r[C.usaspending_url])}" target="_blank" rel="noopener">${esc(r[C.incumbent])}</a>` : esc(r[C.incumbent])}<div class="sub">${esc(r[C.piid])}</div></td>` | |
| + `<td>${esc(r[C.agency])}<div class="sub">${esc(r[C.sub_agency])}</div></td><td>${esc(r[C.naics])}<div class="sub">${esc(r[C.naics_title])}</div></td>` | |
| + `<td class="num">${usd(r[C.obligated])}</td><td class="num">${usd(r[C.potential_value])}</td><td>${esc(r[C.place_state])}</td><td>${esc(r[C.set_aside] || '')}</td></tr>`; | |
| }).join('') : slice.map((r) => `<tr><td>${esc(r[O.response_deadline])}</td>` | |
| + `<td><a href="${esc(r[O.sam_url])}" target="_blank" rel="noopener">${esc(r[O.title])}</a><div class="sub">${esc(r[O.solicitation_number] || '')}</div></td>` | |
| + `<td>${esc(r[O.department])}<div class="sub">${esc(r[O.sub_tier] || '')}</div></td><td>${esc(r[O.naics] || '')}</td><td>${esc(r[O.notice_type])}</td><td>${esc(r[O.set_aside] || '')}</td><td>${esc(r[O.place_state] || '')}</td></tr>`).join(''); | |
| } | |
| function setMode(m) { mode = m; fillSelects(); apply(); } | |
| for (const k of ['q', 'naics']) $(k).addEventListener('input', apply); | |
| for (const k of ['agency', 'state', 'when']) $(k).addEventListener('change', apply); | |
| $('tab-c').onclick = () => setMode('c'); | |
| $('tab-o').onclick = () => setMode('o'); | |
| $('prev').onclick = () => { page--; render(); }; | |
| $('next').onclick = () => { page++; render(); }; | |
| $('csv').onclick = () => { | |
| const cols = mode === 'c' ? d.contracts.cols : d.opps.cols; | |
| const q = (v) => (v == null ? '' : /[",\n]/.test(String(v)) ? `"${String(v).replaceAll('"', '""')}"` : String(v)); | |
| const a = document.createElement('a'); | |
| a.href = URL.createObjectURL(new Blob([[cols.join(','), ...cur.map((r) => r.map(q).join(','))].join('\n')], { type: 'text/csv' })); | |
| a.download = `awardtide-${mode === 'c' ? 'contracts-ending' : 'open-bids'}-${d.built.slice(0, 10)}.csv`; | |
| a.click(); | |
| }; | |
| fillSelects(); | |
| for (const k of ['q', 'naics', 'agency', 'state', 'when']) if (p.get(k)) $(k).value = p.get(k); | |
| apply(); | |
| })(); | |