Compare commits
No commits in common. "fd5308680ec01b9ce84d49a9bea523abbf42c036" and "7869f098b26bdb5ce8114d7370b9cc4c1c260300" have entirely different histories.
fd5308680e
...
7869f098b2
@ -141,53 +141,6 @@ scraperRouter.get("/status", async (_req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// GET /api/scrapers/jobs — Live pg-boss job queue status
|
|
||||||
scraperRouter.get("/jobs", async (_req: Request, res: Response) => {
|
|
||||||
try {
|
|
||||||
const [active, recent, queues] = await Promise.all([
|
|
||||||
// Currently active (running) jobs
|
|
||||||
pool.query(`
|
|
||||||
SELECT name, id, created_on, started_on, output
|
|
||||||
FROM pgboss.job
|
|
||||||
WHERE state = 'active'
|
|
||||||
ORDER BY started_on DESC
|
|
||||||
LIMIT 20
|
|
||||||
`).catch(() => ({ rows: [] })),
|
|
||||||
|
|
||||||
// Recent completions and failures (last 4 hours)
|
|
||||||
pool.query(`
|
|
||||||
SELECT name, state, created_on, started_on, completed_on,
|
|
||||||
EXTRACT(EPOCH FROM (completed_on - started_on))::int AS duration_sec
|
|
||||||
FROM pgboss.job
|
|
||||||
WHERE state IN ('completed', 'failed', 'cancelled')
|
|
||||||
AND completed_on > NOW() - INTERVAL '4 hours'
|
|
||||||
ORDER BY completed_on DESC
|
|
||||||
LIMIT 50
|
|
||||||
`).catch(() => ({ rows: [] })),
|
|
||||||
|
|
||||||
// Queue summary: count per job name and state (last 24h)
|
|
||||||
pool.query(`
|
|
||||||
SELECT name, state, COUNT(*) as count,
|
|
||||||
MAX(completed_on) as last_completed,
|
|
||||||
MAX(started_on) as last_started
|
|
||||||
FROM pgboss.job
|
|
||||||
WHERE created_on > NOW() - INTERVAL '24 hours'
|
|
||||||
GROUP BY name, state
|
|
||||||
ORDER BY name, state
|
|
||||||
`).catch(() => ({ rows: [] })),
|
|
||||||
]);
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
active: active.rows,
|
|
||||||
recent: recent.rows,
|
|
||||||
queues: queues.rows,
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
res.status(503).json({ success: false, error: String(err) });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// GET /api/scrapers/llm-insights — What the crawler LLM has learned
|
// GET /api/scrapers/llm-insights — What the crawler LLM has learned
|
||||||
scraperRouter.get("/llm-insights", async (_req: Request, res: Response) => {
|
scraperRouter.get("/llm-insights", async (_req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -1384,21 +1384,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Live Job Queue -->
|
|
||||||
<div style="margin-bottom:2rem">
|
|
||||||
<div style="display:flex;align-items:center;gap:0.75rem;margin-bottom:1rem">
|
|
||||||
<h3 style="font-size:0.9rem;font-weight:700;color:var(--text-bright)">⚡ Live Job Queue</h3>
|
|
||||||
<span id="cr-live-dot" style="display:inline-block;width:8px;height:8px;border-radius:50%;background:#22c55e;box-shadow:0 0 6px #22c55e;animation:pulse 2s infinite"></span>
|
|
||||||
<span id="cr-active-jobs-count" style="font-size:0.75rem;color:var(--text-dim)">Loading…</span>
|
|
||||||
<button onclick="loadCrawlerJobs()" style="margin-left:auto;font-size:0.72rem;padding:2px 10px;border-radius:4px;border:1px solid var(--border);background:var(--surface2);color:var(--text-dim);cursor:pointer">↻ Refresh</button>
|
|
||||||
</div>
|
|
||||||
<div id="cr-live-jobs"><div style="color:var(--text-dim)">Loading job queue…</div></div>
|
|
||||||
<div style="margin-top:1rem">
|
|
||||||
<h4 style="font-size:0.8rem;font-weight:700;color:var(--text-dim);margin-bottom:0.6rem">Recent (last 2h)</h4>
|
|
||||||
<div id="cr-recent-jobs"><div style="color:var(--text-dim)">Loading…</div></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Scraper Status List -->
|
<!-- Scraper Status List -->
|
||||||
<div style="margin-bottom:2rem">
|
<div style="margin-bottom:2rem">
|
||||||
<h3 style="font-size:0.9rem;font-weight:700;margin-bottom:1rem;color:var(--text-bright)">Scraper Status</h3>
|
<h3 style="font-size:0.9rem;font-weight:700;margin-bottom:1rem;color:var(--text-bright)">Scraper Status</h3>
|
||||||
@ -4850,7 +4835,7 @@ function renderSignals(filterSig) {
|
|||||||
+ '<div style="display:flex;align-items:flex-start;gap:0.25rem;margin-bottom:0.5rem">'
|
+ '<div style="display:flex;align-items:flex-start;gap:0.25rem;margin-bottom:0.5rem">'
|
||||||
+ imgHtml
|
+ imgHtml
|
||||||
+ '<div style="flex:1;min-width:0">'
|
+ '<div style="flex:1;min-width:0">'
|
||||||
+ '<div style="font-weight:700;font-size:0.82rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">' + esc(productName) + (r.is_demo_data || r.is_demo ? demoBadgeHtml : '') + '</div>'
|
+ '<div style="font-weight:700;font-size:0.82rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">' + esc(productName) + (r.is_demo ? demoBadgeHtml : '') + '</div>'
|
||||||
+ '<div style="font-size:0.7rem;color:var(--text-dim)">' + esc(r.form_factor || '') + (r.speed_gbps ? ' · ' + r.speed_gbps + 'G' : '') + (r.vendor_name ? ' · ' + esc(r.vendor_name) : '') + '</div>'
|
+ '<div style="font-size:0.7rem;color:var(--text-dim)">' + esc(r.form_factor || '') + (r.speed_gbps ? ' · ' + r.speed_gbps + 'G' : '') + (r.vendor_name ? ' · ' + esc(r.vendor_name) : '') + '</div>'
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
@ -5016,7 +5001,6 @@ loadChangelog();
|
|||||||
|
|
||||||
// ── CRAWLER INTELLIGENCE ────────────────────────────────────────────
|
// ── CRAWLER INTELLIGENCE ────────────────────────────────────────────
|
||||||
async function loadCrawlerStatus() {
|
async function loadCrawlerStatus() {
|
||||||
loadCrawlerJobs(); // load live job queue in parallel
|
|
||||||
var token = (window.loadToken ? window.loadToken() : '') || '';
|
var token = (window.loadToken ? window.loadToken() : '') || '';
|
||||||
var status = null;
|
var status = null;
|
||||||
var insights = null;
|
var insights = null;
|
||||||
@ -5124,89 +5108,6 @@ async function loadCrawlerStatus() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ── Crawler Jobs (Live Queue) ──────────────────────────────────────────── */
|
|
||||||
async function loadCrawlerJobs() {
|
|
||||||
var token = (window.loadToken ? window.loadToken() : '') || '';
|
|
||||||
var data = null;
|
|
||||||
try {
|
|
||||||
var r = await fetch('/api/scrapers/jobs', { headers: { 'Authorization': 'Bearer ' + token } });
|
|
||||||
data = await r.json();
|
|
||||||
} catch(e) {}
|
|
||||||
|
|
||||||
var active = (data && data.active) || [];
|
|
||||||
var recent = (data && data.recent) || [];
|
|
||||||
var dotEl = el('cr-live-dot');
|
|
||||||
var countEl = el('cr-active-jobs-count');
|
|
||||||
|
|
||||||
if (active.length > 0) {
|
|
||||||
if (dotEl) { dotEl.style.background = '#22c55e'; dotEl.style.boxShadow = '0 0 8px #22c55e'; }
|
|
||||||
if (countEl) countEl.textContent = active.length + ' job' + (active.length !== 1 ? 's' : '') + ' running';
|
|
||||||
} else {
|
|
||||||
if (dotEl) { dotEl.style.background = '#64748b'; dotEl.style.boxShadow = 'none'; }
|
|
||||||
if (countEl) countEl.textContent = 'Idle — waiting for next schedule';
|
|
||||||
}
|
|
||||||
|
|
||||||
var stateColor = { completed: '#22c55e', failed: '#ef4444', cancelled: '#f59e0b' };
|
|
||||||
var liveEl = el('cr-live-jobs');
|
|
||||||
if (liveEl) {
|
|
||||||
if (active.length > 0) {
|
|
||||||
var liveRows = active.map(function(j) {
|
|
||||||
var since = (j.started_on || j.startedon) ? Math.round((Date.now() - new Date(j.started_on || j.startedon).getTime()) / 1000) + 's' : '—';
|
|
||||||
var row = document.createElement('div');
|
|
||||||
row.style.cssText = 'background:rgba(34,197,94,0.08);border:1px solid rgba(34,197,94,0.3);border-radius:6px;padding:0.6rem 0.9rem;display:flex;align-items:center;gap:0.75rem;margin-bottom:0.3rem';
|
|
||||||
var dot = document.createElement('span');
|
|
||||||
dot.style.cssText = 'width:8px;height:8px;border-radius:50%;background:#22c55e;flex-shrink:0';
|
|
||||||
var name = document.createElement('span');
|
|
||||||
name.style.cssText = 'font-size:0.82rem;font-weight:600;color:var(--text-bright);flex:1';
|
|
||||||
name.textContent = j.name;
|
|
||||||
var dur = document.createElement('span');
|
|
||||||
dur.style.cssText = 'font-size:0.72rem;color:var(--text-dim)';
|
|
||||||
dur.textContent = 'running ' + since;
|
|
||||||
row.appendChild(dot); row.appendChild(name); row.appendChild(dur);
|
|
||||||
return row;
|
|
||||||
});
|
|
||||||
liveEl.replaceChildren.apply(liveEl, liveRows);
|
|
||||||
} else {
|
|
||||||
liveEl.textContent = 'No jobs currently active.';
|
|
||||||
liveEl.style.color = 'var(--text-dim)';
|
|
||||||
liveEl.style.fontSize = '0.82rem';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var recentEl = el('cr-recent-jobs');
|
|
||||||
if (recentEl) {
|
|
||||||
if (recent.length > 0) {
|
|
||||||
var rows = recent.slice(0, 20).map(function(j) {
|
|
||||||
var when = (j.completed_on || j.completedon) ? new Date(j.completed_on || j.completedon).toLocaleTimeString('de-DE') : '—';
|
|
||||||
var color = stateColor[j.state] || '#64748b';
|
|
||||||
var dur = j.duration_sec != null ? j.duration_sec + 's' : '';
|
|
||||||
var row = document.createElement('div');
|
|
||||||
row.style.cssText = 'display:flex;align-items:center;gap:0.6rem;font-size:0.75rem;padding:0.35rem 0.6rem;border-radius:4px;background:var(--surface2);border:1px solid var(--border);margin-bottom:0.25rem';
|
|
||||||
var dot = document.createElement('span');
|
|
||||||
dot.style.cssText = 'width:7px;height:7px;border-radius:50%;background:' + color + ';flex-shrink:0';
|
|
||||||
var name = document.createElement('span');
|
|
||||||
name.style.cssText = 'flex:1;color:var(--text-bright);font-weight:500';
|
|
||||||
name.textContent = j.name;
|
|
||||||
var durSpan = document.createElement('span');
|
|
||||||
durSpan.style.color = 'var(--text-dim)';
|
|
||||||
durSpan.textContent = dur;
|
|
||||||
var state = document.createElement('span');
|
|
||||||
state.style.cssText = 'color:' + color + ';font-weight:600;min-width:70px;text-align:right';
|
|
||||||
state.textContent = j.state;
|
|
||||||
var whenSpan = document.createElement('span');
|
|
||||||
whenSpan.style.cssText = 'color:var(--text-dim);min-width:55px;text-align:right';
|
|
||||||
whenSpan.textContent = when;
|
|
||||||
row.appendChild(dot); row.appendChild(name); row.appendChild(durSpan);
|
|
||||||
row.appendChild(state); row.appendChild(whenSpan);
|
|
||||||
return row;
|
|
||||||
});
|
|
||||||
recentEl.replaceChildren.apply(recentEl, rows);
|
|
||||||
} else {
|
|
||||||
recentEl.textContent = 'No recent completions in the last 2 hours.';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Smart Tooltips ─────────────────────────────────────────────────────── */
|
/* ── Smart Tooltips ─────────────────────────────────────────────────────── */
|
||||||
function initSmartTooltips() {
|
function initSmartTooltips() {
|
||||||
var tip = document.createElement('div');
|
var tip = document.createElement('div');
|
||||||
|
|||||||
@ -1,137 +0,0 @@
|
|||||||
-- Migration 029: Seed IEEE/OIF/MSA Standards
|
|
||||||
-- Authoritative standards for optical transceivers
|
|
||||||
-- Applied: 2026-04-08
|
|
||||||
|
|
||||||
INSERT INTO standards (name, ieee_reference, body, speed, speed_gbps, form_factors, max_reach_meters, fiber_type, wavelength, status, year_ratified, notes)
|
|
||||||
VALUES
|
|
||||||
|
|
||||||
-- 1G
|
|
||||||
('1000BASE-SX', 'IEEE 802.3z', 'IEEE', '1G', 1, '{SFP,SFP+}', 550, 'MMF', '850nm', 'ratified', 1998,
|
|
||||||
'850nm VCSEL, 550m on OM2. Legacy GbE, still dominant in campus LAN.'),
|
|
||||||
|
|
||||||
('1000BASE-LX', 'IEEE 802.3z', 'IEEE', '1G', 1, '{SFP,SFP+}', 10000, 'SMF', '1310nm', 'ratified', 1998,
|
|
||||||
'1310nm DFB, 10km SMF. Mode conditioning patch cable for MMF (550m).'),
|
|
||||||
|
|
||||||
('1000BASE-LX10', 'IEEE 802.3ah', 'IEEE', '1G', 1, '{SFP}', 10000, 'SMF', '1310nm', 'ratified', 2004,
|
|
||||||
'EFM standard. Identical to LX but formally defined for access networks.'),
|
|
||||||
|
|
||||||
('1000BASE-BX10', 'IEEE 802.3ah', 'IEEE', '1G', 1, '{SFP}', 10000, 'SMF', '1490nm', 'ratified', 2004,
|
|
||||||
'BiDi GbE over single SMF strand. Tx 1310/Rx 1490nm or reverse. FTTH/FTTA.'),
|
|
||||||
|
|
||||||
-- 10G
|
|
||||||
('10GBASE-SR', 'IEEE 802.3ae', 'IEEE', '10G', 10, '{SFP+,XFP}', 300, 'MMF', '850nm', 'ratified', 2002,
|
|
||||||
'300m OM3, 400m OM4. Standard short-reach 10G in data centers.'),
|
|
||||||
|
|
||||||
('10GBASE-LR', 'IEEE 802.3ae', 'IEEE', '10G', 10, '{SFP+,XFP}', 10000, 'SMF', '1310nm', 'ratified', 2002,
|
|
||||||
'DFB laser, 10km SMF. Most common long-reach 10G interface.'),
|
|
||||||
|
|
||||||
('10GBASE-ER', 'IEEE 802.3ae', 'IEEE', '10G', 10, '{SFP+,XFP}', 40000, 'SMF', '1550nm', 'ratified', 2002,
|
|
||||||
'EML or DFB + APD receiver, 40km. Extended reach 10G.'),
|
|
||||||
|
|
||||||
('10GBASE-ZR', NULL, 'de_facto', '10G', 10, '{SFP+,XFP}', 80000, 'SMF', '1550nm', 'ratified', 2003,
|
|
||||||
'Vendor de facto, 80km. EDFA-compatible power levels. Not IEEE standardized.'),
|
|
||||||
|
|
||||||
('10GBASE-LRM', 'IEEE 802.3aq', 'IEEE', '10G', 10, '{SFP+,XFP}', 220, 'MMF', '1310nm', 'ratified', 2006,
|
|
||||||
'220m on OM1/OM2 legacy fiber using electronic dispersion compensation.'),
|
|
||||||
|
|
||||||
('10GBASE-T', 'IEEE 802.3an', 'IEEE', '10G', 10, '{RJ45}', 100, 'copper', NULL, 'ratified', 2006,
|
|
||||||
'100m on Cat6A copper. High power vs. optics but leverages existing cabling.'),
|
|
||||||
|
|
||||||
-- 25G
|
|
||||||
('25GBASE-SR', 'IEEE 802.3by', 'IEEE', '25G', 25, '{SFP28}', 100, 'MMF', '850nm', 'ratified', 2016,
|
|
||||||
'70m OM3, 100m OM4. Dominant server NIC interconnect since 2017.'),
|
|
||||||
|
|
||||||
('25GBASE-LR', 'IEEE 802.3cc', 'IEEE', '25G', 25, '{SFP28}', 10000, 'SMF', '1310nm', 'ratified', 2017,
|
|
||||||
'10km SMF. Used for ToR-to-spine where longer reach is needed.'),
|
|
||||||
|
|
||||||
('25GBASE-ER', 'IEEE 802.3cc', 'IEEE', '25G', 25, '{SFP28}', 40000, 'SMF', '1310nm', 'ratified', 2017,
|
|
||||||
'40km SMF extended reach.'),
|
|
||||||
|
|
||||||
-- 40G
|
|
||||||
('40GBASE-SR4', 'IEEE 802.3ba', 'IEEE', '40G', 40, '{QSFP+}', 150, 'MMF', '850nm', 'ratified', 2010,
|
|
||||||
'4x10G, 8-fiber MPO. 100m OM3, 150m OM4. Parallel optics for 40G aggregation.'),
|
|
||||||
|
|
||||||
('40GBASE-LR4', 'IEEE 802.3ba', 'IEEE', '40G', 40, '{QSFP+}', 10000, 'SMF', '1310nm', 'ratified', 2010,
|
|
||||||
'4x10G WDM lanes (1271/1291/1311/1331nm), 10km duplex LC.'),
|
|
||||||
|
|
||||||
('40GBASE-ER4', 'IEEE 802.3ba', 'IEEE', '40G', 40, '{QSFP+}', 40000, 'SMF', '1310nm', 'ratified', 2010,
|
|
||||||
'40km SMF, same 4x WDM as LR4 with higher power EML.'),
|
|
||||||
|
|
||||||
('40GBASE-PLR4', NULL, 'MSA', '40G', 40, '{QSFP+}', 10000, 'SMF', '1310nm', 'ratified', 2012,
|
|
||||||
'PSM4 parallel SMF, 8-fiber MPO. Lower cost than LR4 for parallel fiber runs.'),
|
|
||||||
|
|
||||||
-- 100G
|
|
||||||
('100GBASE-SR4', 'IEEE 802.3bm', 'IEEE', '100G', 100, '{QSFP28}', 100, 'MMF', '850nm', 'ratified', 2015,
|
|
||||||
'4x25G, 8-fiber MPO. 70m OM3, 100m OM4. Standard hyperscaler server-to-ToR.'),
|
|
||||||
|
|
||||||
('100GBASE-LR4', 'IEEE 802.3ba', 'IEEE', '100G', 100, '{QSFP28,CFP,CFP2,CFP4}', 10000, 'SMF', '1295-1310nm', 'ratified', 2010,
|
|
||||||
'4x25G CWDM WDM lanes. 10km duplex LC. Standard DCI and metro link.'),
|
|
||||||
|
|
||||||
('100GBASE-ER4', 'IEEE 802.3ba', 'IEEE', '100G', 100, '{CFP,CFP2,QSFP28}', 40000, 'SMF', '1295-1310nm', 'ratified', 2010,
|
|
||||||
'40km, 4x WDM EML lasers. Higher power than LR4.'),
|
|
||||||
|
|
||||||
('100GBASE-PSM4', NULL, 'MSA', '100G', 100, '{QSFP28}', 500, 'SMF', '1310nm', 'ratified', 2014,
|
|
||||||
'Parallel SMF 4-lane, 500m on 8-fiber MPO. Cost-effective campus/DCI.'),
|
|
||||||
|
|
||||||
('100GBASE-CWDM4', NULL, 'MSA', '100G', 100, '{QSFP28}', 2000, 'SMF', '1310nm', 'ratified', 2015,
|
|
||||||
'4x25G CWDM over duplex SMF, 2km. Popular inter-building DCI alternative to LR4.'),
|
|
||||||
|
|
||||||
('100GBASE-DR', 'IEEE 802.3cu', 'IEEE', '100G', 100, '{QSFP28}', 500, 'SMF', '1310nm', 'ratified', 2021,
|
|
||||||
'Single-lambda PAM4, 500m duplex LC. Simpler than LR4, gaining traction.'),
|
|
||||||
|
|
||||||
('100GBASE-FR', 'IEEE 802.3cu', 'IEEE', '100G', 100, '{QSFP28}', 2000, 'SMF', '1310nm', 'ratified', 2021,
|
|
||||||
'Single-lambda PAM4, 2km. Between DR (500m) and LR (10km).'),
|
|
||||||
|
|
||||||
('100GBASE-LR', 'IEEE 802.3cu', 'IEEE', '100G', 100, '{QSFP28}', 10000, 'SMF', '1310nm', 'ratified', 2021,
|
|
||||||
'Single-lambda PAM4, 10km. Simpler than LR4, no WDM mux needed.'),
|
|
||||||
|
|
||||||
('100G-ZR (OIF)', NULL, 'OIF', '100G', 100, '{CFP,CFP2}', 1000000, 'SMF', '1550nm', 'ratified', 2016,
|
|
||||||
'DP-QPSK coherent. 1000km+ backbone. Soft-decision FEC.'),
|
|
||||||
|
|
||||||
-- 400G
|
|
||||||
('400GBASE-SR8', 'IEEE 802.3cm', 'IEEE', '400G', 400, '{QSFP-DD,OSFP}', 100, 'MMF', '850nm', 'ratified', 2020,
|
|
||||||
'8x50G SR PAM4, 16-fiber MPO. 50m OM3, 100m OM4/OM5.'),
|
|
||||||
|
|
||||||
('400GBASE-SR4.2', 'IEEE 802.3cm', 'IEEE', '400G', 400, '{QSFP-DD,OSFP}', 150, 'MMF', '850/910nm', 'ratified', 2020,
|
|
||||||
'BiDi 850/910nm, 8-fiber MPO. 150m OM5. Cost-effective MMF 400G upgrade.'),
|
|
||||||
|
|
||||||
('400GBASE-DR4', 'IEEE 802.3bs', 'IEEE', '400G', 400, '{QSFP-DD,OSFP,CFP8}', 500, 'SMF', '1310nm', 'ratified', 2018,
|
|
||||||
'4x100G PAM4, 8-fiber MPO SMF. 500m. Hyperscaler cluster fabric.'),
|
|
||||||
|
|
||||||
('400GBASE-LR4', 'IEEE 802.3bs', 'IEEE', '400G', 400, '{QSFP-DD,OSFP}', 10000, 'SMF', '1310nm', 'ratified', 2018,
|
|
||||||
'4x100G WDM, 10km duplex SMF. Requires EML lasers.'),
|
|
||||||
|
|
||||||
('400GBASE-FR4', 'IEEE 802.3bs', 'IEEE', '400G', 400, '{QSFP-DD,OSFP}', 2000, 'SMF', '1310nm', 'ratified', 2018,
|
|
||||||
'4x100G WDM, 2km. Data center interconnect.'),
|
|
||||||
|
|
||||||
('400G-ZR (OIF)', NULL, 'OIF', '400G', 400, '{QSFP-DD,OSFP}', 120000, 'SMF', '1550nm', 'ratified', 2020,
|
|
||||||
'DP-16QAM coherent, 120km DWDM span. Pluggable coherent revolutionized DCI economics.'),
|
|
||||||
|
|
||||||
('400G-ZR+', NULL, 'OIF', '400G', 400, '{QSFP-DD,OSFP}', 3000000, 'SMF', '1550nm', 'ratified', 2022,
|
|
||||||
'Extended coherent, 3000km+. Higher OSNR than ZR. Submarine/long-haul capable.'),
|
|
||||||
|
|
||||||
-- 800G
|
|
||||||
('800GBASE-SR8', 'IEEE 802.3df', 'IEEE', '800G', 800, '{OSFP,QSFP-DD800}', 100, 'MMF', '850nm', 'ratified', 2023,
|
|
||||||
'8x100G SR PAM4, 16-fiber MPO. 100m OM4. First 800G reaching market in 2024.'),
|
|
||||||
|
|
||||||
('800GBASE-DR8', 'IEEE 802.3df', 'IEEE', '800G', 800, '{OSFP,QSFP-DD800}', 500, 'SMF', '1310nm', 'ratified', 2023,
|
|
||||||
'8x100G PAM4, 16-fiber MPO SMF, 500m. GPU cluster interconnect target.'),
|
|
||||||
|
|
||||||
('800GBASE-LR4', 'IEEE 802.3df', 'IEEE', '800G', 800, '{OSFP,QSFP-DD800}', 10000, 'SMF', '1310nm', 'ratified', 2023,
|
|
||||||
'4x200G WDM lanes, 10km duplex SMF. Requires 200G-per-lane DSPs.'),
|
|
||||||
|
|
||||||
('800G-ZR (OIF)', NULL, 'OIF', '800G', 800, '{OSFP,QSFP-DD800}', 120000, 'SMF', '1550nm', 'ratified', 2024,
|
|
||||||
'DP-64QAM or DP-32QAM coherent. 120km DWDM. Production starting 2025.'),
|
|
||||||
|
|
||||||
-- PON
|
|
||||||
('XGS-PON', 'ITU-T G.9807.1', 'IEEE', '10G', 10, '{SFP+}', 20000, 'SMF', '1270/1577nm', 'ratified', 2016,
|
|
||||||
'10G symmetric PON. 1270nm upstream, 1577nm downstream. Dominant FTTH 10G standard.'),
|
|
||||||
|
|
||||||
-- DWDM
|
|
||||||
('100G DWDM Tunable', NULL, 'OIF', '100G', 100, '{CFP,CFP2}', 1000000, 'SMF', 'C-band', 'ratified', 2014,
|
|
||||||
'Tunable coherent 100G. 50GHz ITU C-band grid, 96 channels. Metro/long-haul transport.'),
|
|
||||||
|
|
||||||
('400G DWDM Tunable', NULL, 'OIF', '400G', 400, '{QSFP-DD,OSFP}', 1000000, 'SMF', 'C-band', 'ratified', 2021,
|
|
||||||
'Tunable 400G coherent over ITU C-band DWDM grid. Packet-optical transport.')
|
|
||||||
|
|
||||||
ON CONFLICT (name) DO NOTHING;
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
-- Migration 030: Add LinkedIn post columns to blog_drafts
|
|
||||||
-- Required by fo-blog-pipeline-v5 (linkedin post generation at step 16)
|
|
||||||
|
|
||||||
ALTER TABLE blog_drafts
|
|
||||||
ADD COLUMN IF NOT EXISTS linkedin_post TEXT,
|
|
||||||
ADD COLUMN IF NOT EXISTS linkedin_char_count INTEGER;
|
|
||||||
@ -1,184 +0,0 @@
|
|||||||
/**
|
|
||||||
* Migration 031 — Cisco 8000 Series Accuracy Update
|
|
||||||
*
|
|
||||||
* Fixes:
|
|
||||||
* - 8201-32FH: corrected ASIC Q100→Q200, added full description, features, use_cases, flexbox_notes
|
|
||||||
* - 8608: updated to modular system_type, improved description
|
|
||||||
* - NEW: 8812 (12-slot chassis), 8818 (18-slot chassis), 8800-LC-36FH, 8800-LC-48H line cards
|
|
||||||
*
|
|
||||||
* Key facts added per research:
|
|
||||||
* - Silicon One Q200 (7nm + HBM), NOT Q100
|
|
||||||
* - DCO license required per 100G for ZR/ZR+ coherent optics (all vendors)
|
|
||||||
* - No separate RTU license for grey (non-coherent) 3rd-party optics
|
|
||||||
* - IOS XR PID check: "transceiver permit pid all" per-interface override
|
|
||||||
* - Flexoptix FlexBox "Cisco Systems" mode = native OEM PID, no override needed
|
|
||||||
* - QSFP28/QSFP+ backward-compatible in QSFP-DD cages; SFP/SFP28 need breakout cable
|
|
||||||
* - QSA adapters NOT supported on QSFP-DD ports
|
|
||||||
* - DOM readings may be incorrect with non-Cisco modules
|
|
||||||
* - IOS XR upgrades can break previously working 3rd-party optics
|
|
||||||
*/
|
|
||||||
|
|
||||||
-- ============================================================
|
|
||||||
-- UPDATE 8201-32FH (fixed)
|
|
||||||
-- ============================================================
|
|
||||||
UPDATE switches SET
|
|
||||||
description = '32-port 400GbE Service Provider router powered by Silicon One Q200 ASIC (7nm + HBM). All 32 QSFP-DD ports support 400G grey Ethernet and coherent QSFP-DD ZR/ZR+ / OpenZR+ optics (DCO license required per 100G for coherent mode). QSFP28 and QSFP+ are physically backward-compatible in QSFP-DD cages. SFP and SFP28 require breakout cables — QSA adapters NOT supported. IOS XR validates transceiver EEPROM/PID at insertion; unrecognized modules have TX laser disabled. No special license required for standard grey optics.',
|
|
||||||
features = '["Silicon One Q200 (7nm+HBM)", "SRv6", "Segment Routing MPLS", "FlexAlgo", "Coherent ZR/ZR+ (all 32 ports)", "OpenZR+", "QSFP-DD backward compat QSFP28/QSFP+", "MACsec-256", "EVPN", "BGP-LU", "Telemetry gNMI/gRPC", "IOS XR"]'::jsonb,
|
|
||||||
use_cases = ARRAY['Provider Edge', 'Internet Core', 'Peering Router', 'DCI / Cloud Backbone', '5G Transport', 'Routed Optical Networking (RON)'],
|
|
||||||
flexbox_compat_mode = 'Cisco',
|
|
||||||
flexbox_notes = 'IOS XR validates EEPROM/PID at insertion — unrecognized modules have TX laser disabled immediately. Per-interface override: "transceiver permit pid all" (NOT global; different from IOS "service unsupported-transceiver"). Warning: no TAC support; generates PFM alarm; may fail after IOS XR upgrades/SMUs due to driver changes; some modules never come up regardless of override. DCO License required for ZR/ZR+ coherent optics (billed per 100G — applies to all vendors including Cisco). Grey optics (QSFP28 SR4/LR4, 400G DR4 etc.) need no special license. Flexoptix FlexBox "Cisco Systems" mode writes Cisco OEM PID into transceiver EEPROM — IOS XR accepts it natively without any override command. DOM readings may show incorrect values with non-Cisco modules (not traffic-affecting but impacts monitoring).',
|
|
||||||
category = 'SP',
|
|
||||||
lifecycle_status = 'Active',
|
|
||||||
system_type = 'fixed',
|
|
||||||
is_linecard = false,
|
|
||||||
max_speed_gbps = 400,
|
|
||||||
switching_capacity_tbps = 12.8,
|
|
||||||
total_ports = 32,
|
|
||||||
ports_config = '{"400G_QSFP-DD": 32}'::jsonb
|
|
||||||
WHERE model = '8201-32FH';
|
|
||||||
|
|
||||||
-- ============================================================
|
|
||||||
-- UPDATE 8608 (modular chassis)
|
|
||||||
-- ============================================================
|
|
||||||
UPDATE switches SET
|
|
||||||
description = 'Cisco 8608 — 8-slot modular chassis router for Tier-1 SP core and DCI. Accepts 8800-series line cards (48×100G QSFP28, 36×400G QSFP-DD, etc.). Powered by Silicon One Q200 ASICs per line card. IOS XR validates transceiver EEPROM/PID per line card at insertion.',
|
|
||||||
system_type = 'modular',
|
|
||||||
is_linecard = false,
|
|
||||||
chassis_model = NULL,
|
|
||||||
features = '["8 line card slots", "Silicon One Q200 per LC", "SRv6", "Segment Routing", "Coherent ZR/ZR+", "EVPN", "BGP-LU", "Telemetry", "IOS XR"]'::jsonb,
|
|
||||||
use_cases = ARRAY['Tier-1 SP Core', 'Provider Edge', 'DCI', 'Peering', 'Multi-service Backbone'],
|
|
||||||
flexbox_compat_mode = 'Cisco',
|
|
||||||
flexbox_notes = 'IOS XR validates EEPROM/PID at insertion — unrecognized modules have TX laser disabled immediately. Per-interface override: "transceiver permit pid all" (NOT global; different from IOS "service unsupported-transceiver"). Warning: no TAC support; generates PFM alarm; may fail after IOS XR upgrades/SMUs due to driver changes; some modules never come up regardless of override. DCO License required for ZR/ZR+ coherent optics (billed per 100G — applies to all vendors including Cisco). Grey optics (QSFP28 SR4/LR4, 400G DR4 etc.) need no special license. Flexoptix FlexBox "Cisco Systems" mode writes Cisco OEM PID into transceiver EEPROM — IOS XR accepts it natively without any override command. DOM readings may show incorrect values with non-Cisco modules (not traffic-affecting but impacts monitoring).',
|
|
||||||
category = 'SP',
|
|
||||||
lifecycle_status = 'Active'
|
|
||||||
WHERE model = '8608';
|
|
||||||
|
|
||||||
-- ============================================================
|
|
||||||
-- INSERT 8812 modular chassis
|
|
||||||
-- ============================================================
|
|
||||||
INSERT INTO switches (
|
|
||||||
id, vendor_id, model, series, category, description, max_speed_gbps,
|
|
||||||
switching_capacity_tbps, total_ports, ports_config, system_type, is_linecard,
|
|
||||||
flexbox_compat_mode, flexbox_notes, lifecycle_status, features, use_cases
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
gen_random_uuid(),
|
|
||||||
v.id,
|
|
||||||
'8812',
|
|
||||||
'Cisco 8000',
|
|
||||||
'SP',
|
|
||||||
'Cisco 8812 — 12-slot modular chassis for ultra-high-capacity Tier-1 SP core. Accepts 8800-series line cards (36×400G QSFP-DD or 48×100G QSFP28 per slot). Silicon One Q200 ASICs per LC. IOS XR validates EEPROM/PID per line card. Coherent ZR/ZR+ on compatible LCs (DCO license per 100G required).',
|
|
||||||
400,
|
|
||||||
57.6,
|
|
||||||
432,
|
|
||||||
'{"slots": 12, "max_per_slot_400G_QSFP-DD": 36, "max_per_slot_100G_QSFP28": 48}'::jsonb,
|
|
||||||
'modular',
|
|
||||||
false,
|
|
||||||
'Cisco',
|
|
||||||
'IOS XR validates EEPROM/PID at insertion — unrecognized modules have TX laser disabled immediately. Per-interface override: "transceiver permit pid all" (NOT global; different from IOS "service unsupported-transceiver"). Warning: no TAC support; generates PFM alarm; may fail after IOS XR upgrades/SMUs due to driver changes; some modules never come up regardless of override. DCO License required for ZR/ZR+ coherent optics (billed per 100G — applies to all vendors including Cisco). Grey optics (QSFP28 SR4/LR4, 400G DR4 etc.) need no special license. Flexoptix FlexBox "Cisco Systems" mode writes Cisco OEM PID into transceiver EEPROM — IOS XR accepts it natively without any override command. DOM readings may show incorrect values with non-Cisco modules (not traffic-affecting but impacts monitoring).',
|
|
||||||
'Active',
|
|
||||||
'["12 slots", "Silicon One Q200 per LC", "SRv6", "Segment Routing", "Coherent ZR/ZR+", "OpenZR+", "EVPN", "BGP-LU", "Telemetry gNMI", "MACsec-256", "IOS XR"]'::jsonb,
|
|
||||||
ARRAY['Tier-1 SP Core', 'Multi-Terabit Backbone', 'DCI', 'Peering', 'Internet Exchange Core']
|
|
||||||
FROM vendors v WHERE v.name = 'Cisco'
|
|
||||||
AND NOT EXISTS (SELECT 1 FROM switches WHERE model = '8812');
|
|
||||||
|
|
||||||
-- ============================================================
|
|
||||||
-- INSERT 8818 modular chassis
|
|
||||||
-- ============================================================
|
|
||||||
INSERT INTO switches (
|
|
||||||
id, vendor_id, model, series, category, description, max_speed_gbps,
|
|
||||||
switching_capacity_tbps, total_ports, ports_config, system_type, is_linecard,
|
|
||||||
flexbox_compat_mode, flexbox_notes, lifecycle_status, features, use_cases
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
gen_random_uuid(),
|
|
||||||
v.id,
|
|
||||||
'8818',
|
|
||||||
'Cisco 8000',
|
|
||||||
'SP',
|
|
||||||
'Cisco 8818 — 18-slot modular chassis, largest in the Cisco 8000 family. Designed for hyperscale SP core networks. Accepts 8800-series line cards (36×400G QSFP-DD or 48×100G QSFP28 per slot). Silicon One Q200 ASICs per LC. Up to 18 × 36 = 648 ports of 400G.',
|
|
||||||
400,
|
|
||||||
86.4,
|
|
||||||
648,
|
|
||||||
'{"slots": 18, "max_per_slot_400G_QSFP-DD": 36, "max_per_slot_100G_QSFP28": 48}'::jsonb,
|
|
||||||
'modular',
|
|
||||||
false,
|
|
||||||
'Cisco',
|
|
||||||
'IOS XR validates EEPROM/PID at insertion — unrecognized modules have TX laser disabled immediately. Per-interface override: "transceiver permit pid all" (NOT global; different from IOS "service unsupported-transceiver"). Warning: no TAC support; generates PFM alarm; may fail after IOS XR upgrades/SMUs due to driver changes; some modules never come up regardless of override. DCO License required for ZR/ZR+ coherent optics (billed per 100G — applies to all vendors including Cisco). Grey optics (QSFP28 SR4/LR4, 400G DR4 etc.) need no special license. Flexoptix FlexBox "Cisco Systems" mode writes Cisco OEM PID into transceiver EEPROM — IOS XR accepts it natively without any override command. DOM readings may show incorrect values with non-Cisco modules (not traffic-affecting but impacts monitoring).',
|
|
||||||
'Active',
|
|
||||||
'["18 slots", "Silicon One Q200 per LC", "SRv6", "Segment Routing", "Coherent ZR/ZR+", "OpenZR+", "EVPN", "BGP-LU", "Telemetry gNMI", "MACsec-256", "IOS XR", "Hyperscale"]'::jsonb,
|
|
||||||
ARRAY['Tier-1 SP Core', 'Hyperscale Backbone', 'Multi-Terabit Peering', 'DCI', 'Internet Exchange Core']
|
|
||||||
FROM vendors v WHERE v.name = 'Cisco'
|
|
||||||
AND NOT EXISTS (SELECT 1 FROM switches WHERE model = '8818');
|
|
||||||
|
|
||||||
-- ============================================================
|
|
||||||
-- INSERT 8800-LC-36FH (400G QSFP-DD line card)
|
|
||||||
-- ============================================================
|
|
||||||
INSERT INTO switches (
|
|
||||||
id, vendor_id, model, series, category, description, max_speed_gbps,
|
|
||||||
total_ports, ports_config, system_type, is_linecard,
|
|
||||||
chassis_model, slot_type, flexbox_compat_mode, flexbox_notes, lifecycle_status,
|
|
||||||
features, use_cases
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
gen_random_uuid(),
|
|
||||||
v.id,
|
|
||||||
'8800-LC-36FH',
|
|
||||||
'Cisco 8000',
|
|
||||||
'SP',
|
|
||||||
'Cisco 8800-LC-36FH — 36-port 400GbE line card for 8800-series modular chassis (8608, 8812, 8818). Silicon One Q200 ASIC. 36 × QSFP-DD ports supporting 400G Ethernet and coherent QSFP-DD ZR/ZR+ optics. QSFP28/QSFP+ physically backward-compatible in QSFP-DD cages. SFP/SFP28 require breakout cables — QSA adapters NOT supported.',
|
|
||||||
400,
|
|
||||||
36,
|
|
||||||
'{"400G_QSFP-DD": 36}'::jsonb,
|
|
||||||
'fixed',
|
|
||||||
true,
|
|
||||||
'Cisco 8000',
|
|
||||||
'Cisco 8800 LC Slot',
|
|
||||||
'Cisco',
|
|
||||||
'IOS XR validates EEPROM/PID at insertion — unrecognized modules have TX laser disabled immediately. Per-interface override: "transceiver permit pid all" (NOT global; different from IOS "service unsupported-transceiver"). Warning: no TAC support; generates PFM alarm; may fail after IOS XR upgrades/SMUs due to driver changes; some modules never come up regardless of override. DCO License required for ZR/ZR+ coherent optics (billed per 100G — applies to all vendors including Cisco). Grey optics (QSFP28 SR4/LR4, 400G DR4 etc.) need no special license. Flexoptix FlexBox "Cisco Systems" mode writes Cisco OEM PID into transceiver EEPROM — IOS XR accepts it natively without any override command. DOM readings may show incorrect values with non-Cisco modules (not traffic-affecting but impacts monitoring).',
|
|
||||||
'Active',
|
|
||||||
'["Silicon One Q200", "36×400G QSFP-DD", "Coherent ZR/ZR+", "OpenZR+", "QSFP28/QSFP+ backward compat", "SRv6", "Segment Routing", "MACsec-256", "IOS XR"]'::jsonb,
|
|
||||||
ARRAY['SP Core Line Card', '400G Dense', 'Coherent DCI', 'Routed Optical Networking']
|
|
||||||
FROM vendors v WHERE v.name = 'Cisco'
|
|
||||||
AND NOT EXISTS (SELECT 1 FROM switches WHERE model = '8800-LC-36FH');
|
|
||||||
|
|
||||||
-- ============================================================
|
|
||||||
-- INSERT 8800-LC-48H (100G QSFP28 line card)
|
|
||||||
-- ============================================================
|
|
||||||
INSERT INTO switches (
|
|
||||||
id, vendor_id, model, series, category, description, max_speed_gbps,
|
|
||||||
total_ports, ports_config, system_type, is_linecard,
|
|
||||||
chassis_model, slot_type, flexbox_compat_mode, flexbox_notes, lifecycle_status,
|
|
||||||
features, use_cases
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
gen_random_uuid(),
|
|
||||||
v.id,
|
|
||||||
'8800-LC-48H',
|
|
||||||
'Cisco 8000',
|
|
||||||
'SP',
|
|
||||||
'Cisco 8800-LC-48H — 48-port 100GbE line card for 8800-series modular chassis (8608, 8812, 8818). Silicon One Q200L ASIC. 48 × QSFP28 ports. Supports QSFP+ (40G) backward-compatible. SFP/SFP28 require breakout cables. No QSA adapter support.',
|
|
||||||
100,
|
|
||||||
48,
|
|
||||||
'{"100G_QSFP28": 48}'::jsonb,
|
|
||||||
'fixed',
|
|
||||||
true,
|
|
||||||
'Cisco 8000',
|
|
||||||
'Cisco 8800 LC Slot',
|
|
||||||
'Cisco',
|
|
||||||
'IOS XR validates EEPROM/PID at insertion — unrecognized modules have TX laser disabled immediately. Per-interface override: "transceiver permit pid all" (NOT global; different from IOS "service unsupported-transceiver"). Warning: no TAC support; generates PFM alarm; may fail after IOS XR upgrades/SMUs due to driver changes; some modules never come up regardless of override. DCO License required for ZR/ZR+ coherent optics (billed per 100G — applies to all vendors including Cisco). Grey optics (QSFP28 SR4/LR4, 400G DR4 etc.) need no special license. Flexoptix FlexBox "Cisco Systems" mode writes Cisco OEM PID into transceiver EEPROM — IOS XR accepts it natively without any override command. DOM readings may show incorrect values with non-Cisco modules (not traffic-affecting but impacts monitoring).',
|
|
||||||
'Active',
|
|
||||||
'["Silicon One Q200L", "48×100G QSFP28", "QSFP+ backward compat", "Breakout 4×25G", "SRv6", "Segment Routing", "IOS XR"]'::jsonb,
|
|
||||||
ARRAY['SP Core Line Card', '100G Aggregation', 'High-density 100G']
|
|
||||||
FROM vendors v WHERE v.name = 'Cisco'
|
|
||||||
AND NOT EXISTS (SELECT 1 FROM switches WHERE model = '8800-LC-48H');
|
|
||||||
|
|
||||||
-- Update search_vector for all Cisco 8000 models
|
|
||||||
UPDATE switches SET search_vector =
|
|
||||||
to_tsvector('english',
|
|
||||||
COALESCE(model, '') || ' ' ||
|
|
||||||
COALESCE(series, '') || ' ' ||
|
|
||||||
COALESCE(description, '') || ' ' ||
|
|
||||||
COALESCE(category, '')
|
|
||||||
)
|
|
||||||
WHERE series = 'Cisco 8000';
|
|
||||||
@ -1,179 +0,0 @@
|
|||||||
/**
|
|
||||||
* Migration 032 — Switches column additions + verification fix + demo data flag
|
|
||||||
*
|
|
||||||
* Adds:
|
|
||||||
* - switches: description, features, use_cases, system_type, is_linecard,
|
|
||||||
* chassis_model, slot_type, flexbox_compat_mode, flexbox_notes
|
|
||||||
* - procurement tables: is_demo_data flag for DEMO DATA badge
|
|
||||||
* - Fix compute_transceiver_verification: 'unknown' confidence with populated
|
|
||||||
* core fields counts as details_verified (scraper seeded data is valid)
|
|
||||||
*/
|
|
||||||
|
|
||||||
-- ============================================================
|
|
||||||
-- 1. Add missing columns to switches table
|
|
||||||
-- ============================================================
|
|
||||||
ALTER TABLE switches
|
|
||||||
ADD COLUMN IF NOT EXISTS description text,
|
|
||||||
ADD COLUMN IF NOT EXISTS features jsonb DEFAULT '[]'::jsonb,
|
|
||||||
ADD COLUMN IF NOT EXISTS use_cases text[] DEFAULT '{}'::text[],
|
|
||||||
ADD COLUMN IF NOT EXISTS system_type text DEFAULT 'fixed',
|
|
||||||
ADD COLUMN IF NOT EXISTS is_linecard boolean DEFAULT false,
|
|
||||||
ADD COLUMN IF NOT EXISTS chassis_model text,
|
|
||||||
ADD COLUMN IF NOT EXISTS slot_type text,
|
|
||||||
ADD COLUMN IF NOT EXISTS flexbox_compat_mode text,
|
|
||||||
ADD COLUMN IF NOT EXISTS flexbox_notes text;
|
|
||||||
|
|
||||||
-- Check constraint for system_type
|
|
||||||
DO $$
|
|
||||||
BEGIN
|
|
||||||
IF NOT EXISTS (
|
|
||||||
SELECT 1 FROM pg_constraint WHERE conname = 'switches_system_type_check'
|
|
||||||
) THEN
|
|
||||||
ALTER TABLE switches ADD CONSTRAINT switches_system_type_check
|
|
||||||
CHECK (system_type IN ('fixed', 'modular', 'stackable'));
|
|
||||||
END IF;
|
|
||||||
END $$;
|
|
||||||
|
|
||||||
-- Index for linecard lookups
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_switches_is_linecard ON switches (is_linecard) WHERE is_linecard = true;
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_switches_chassis_model ON switches (chassis_model) WHERE chassis_model IS NOT NULL;
|
|
||||||
|
|
||||||
-- ============================================================
|
|
||||||
-- 2. Add is_demo_data flag to procurement tables
|
|
||||||
-- ============================================================
|
|
||||||
ALTER TABLE reorder_signals
|
|
||||||
ADD COLUMN IF NOT EXISTS is_demo_data boolean DEFAULT false;
|
|
||||||
|
|
||||||
ALTER TABLE abc_classification
|
|
||||||
ADD COLUMN IF NOT EXISTS is_demo_data boolean DEFAULT false;
|
|
||||||
|
|
||||||
ALTER TABLE stock_snapshots
|
|
||||||
ADD COLUMN IF NOT EXISTS is_demo_data boolean DEFAULT false;
|
|
||||||
|
|
||||||
ALTER TABLE market_intelligence
|
|
||||||
ADD COLUMN IF NOT EXISTS is_demo_data boolean DEFAULT false;
|
|
||||||
|
|
||||||
-- Mark existing demo data (seeded from migration 021)
|
|
||||||
-- These were seeded as static demo rows - mark them so frontend can badge them
|
|
||||||
UPDATE reorder_signals SET is_demo_data = true
|
|
||||||
WHERE source IS NULL OR source IN ('demo', 'seed', 'synthetic');
|
|
||||||
|
|
||||||
UPDATE abc_classification SET is_demo_data = true
|
|
||||||
WHERE classification_source IS NULL OR classification_source IN ('demo', 'seed', 'synthetic');
|
|
||||||
|
|
||||||
-- Market intelligence seeded rows (OFC 2026, AWS capex, etc. from migration 019)
|
|
||||||
UPDATE market_intelligence SET is_demo_data = true
|
|
||||||
WHERE source IN ('manual', 'seed', 'OFC 2026', 'demo')
|
|
||||||
OR (source IS NULL AND created_at < '2026-04-09'::date);
|
|
||||||
|
|
||||||
-- ============================================================
|
|
||||||
-- 3. Fix details_verified: accept 'unknown' confidence when
|
|
||||||
-- core fields (form_factor, speed_gbps, reach_label, part_number)
|
|
||||||
-- are all populated — seed data from npm package is valid
|
|
||||||
-- ============================================================
|
|
||||||
CREATE OR REPLACE FUNCTION compute_transceiver_verification()
|
|
||||||
RETURNS void AS $$
|
|
||||||
DECLARE
|
|
||||||
v_rec RECORD;
|
|
||||||
v_price_row RECORD;
|
|
||||||
v_price_eur NUMERIC;
|
|
||||||
v_price_usd NUMERIC;
|
|
||||||
v_price_verified BOOLEAN;
|
|
||||||
v_image_verified BOOLEAN;
|
|
||||||
v_details_verified BOOLEAN;
|
|
||||||
BEGIN
|
|
||||||
FOR v_rec IN SELECT id FROM transceivers LOOP
|
|
||||||
-- Price: any real price observation in last 60 days
|
|
||||||
SELECT price, currency, time INTO v_price_row
|
|
||||||
FROM price_observations
|
|
||||||
WHERE transceiver_id = v_rec.id
|
|
||||||
AND price > 0
|
|
||||||
AND time > NOW() - INTERVAL '60 days'
|
|
||||||
ORDER BY price DESC, time DESC
|
|
||||||
LIMIT 1;
|
|
||||||
|
|
||||||
v_price_verified := v_price_row IS NOT NULL;
|
|
||||||
|
|
||||||
IF v_price_verified THEN
|
|
||||||
CASE v_price_row.currency
|
|
||||||
WHEN 'EUR' THEN
|
|
||||||
v_price_eur := v_price_row.price;
|
|
||||||
v_price_usd := NULL;
|
|
||||||
WHEN 'USD' THEN
|
|
||||||
v_price_usd := v_price_row.price;
|
|
||||||
v_price_eur := NULL;
|
|
||||||
WHEN 'GBP' THEN
|
|
||||||
v_price_eur := v_price_row.price * 1.17;
|
|
||||||
v_price_usd := NULL;
|
|
||||||
ELSE
|
|
||||||
v_price_eur := NULL;
|
|
||||||
v_price_usd := NULL;
|
|
||||||
END CASE;
|
|
||||||
ELSE
|
|
||||||
v_price_eur := NULL;
|
|
||||||
v_price_usd := NULL;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
-- Image: has any image URL
|
|
||||||
v_image_verified := EXISTS (
|
|
||||||
SELECT 1 FROM transceivers
|
|
||||||
WHERE id = v_rec.id
|
|
||||||
AND image_url IS NOT NULL
|
|
||||||
AND image_url != ''
|
|
||||||
);
|
|
||||||
|
|
||||||
-- Details verified:
|
|
||||||
-- EITHER confidence is 'good' (scraped/verified/official) AND has connector or wavelength
|
|
||||||
-- OR all core fields (form_factor, speed_gbps, reach_label, part_number) are populated
|
|
||||||
-- (seed data from npm package counts — 'unknown' confidence with full spec = valid details)
|
|
||||||
v_details_verified := EXISTS (
|
|
||||||
SELECT 1 FROM transceivers t2
|
|
||||||
WHERE t2.id = v_rec.id
|
|
||||||
AND t2.data_confidence NOT IN ('garbage', '')
|
|
||||||
AND t2.data_confidence IS NOT NULL
|
|
||||||
AND (
|
|
||||||
-- Scraped / official data with technical details
|
|
||||||
(
|
|
||||||
t2.data_confidence NOT IN ('unknown')
|
|
||||||
AND (t2.connector IS NOT NULL OR t2.wavelengths IS NOT NULL OR t2.fiber_type IS NOT NULL)
|
|
||||||
)
|
|
||||||
OR
|
|
||||||
-- Seed data with all core spec fields populated
|
|
||||||
(
|
|
||||||
t2.form_factor IS NOT NULL
|
|
||||||
AND t2.speed_gbps IS NOT NULL
|
|
||||||
AND t2.reach_label IS NOT NULL
|
|
||||||
AND t2.part_number IS NOT NULL
|
|
||||||
AND t2.fiber_type IS NOT NULL
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
UPDATE transceivers SET
|
|
||||||
price_verified = v_price_verified,
|
|
||||||
price_verified_eur = v_price_eur,
|
|
||||||
street_price_usd = v_price_usd,
|
|
||||||
image_verified = v_image_verified,
|
|
||||||
details_verified = v_details_verified,
|
|
||||||
fully_verified = v_price_verified AND v_image_verified AND v_details_verified,
|
|
||||||
updated_at = NOW()
|
|
||||||
WHERE id = v_rec.id;
|
|
||||||
END LOOP;
|
|
||||||
END;
|
|
||||||
$$ LANGUAGE plpgsql;
|
|
||||||
|
|
||||||
-- Run verification refresh
|
|
||||||
SELECT compute_transceiver_verification();
|
|
||||||
|
|
||||||
-- ============================================================
|
|
||||||
-- 4. Report
|
|
||||||
-- ============================================================
|
|
||||||
SELECT
|
|
||||||
COUNT(*) AS total,
|
|
||||||
SUM(CASE WHEN price_verified THEN 1 ELSE 0 END) AS price_verified,
|
|
||||||
SUM(CASE WHEN image_verified THEN 1 ELSE 0 END) AS image_verified,
|
|
||||||
SUM(CASE WHEN details_verified THEN 1 ELSE 0 END) AS details_verified,
|
|
||||||
SUM(CASE WHEN fully_verified THEN 1 ELSE 0 END) AS fully_verified,
|
|
||||||
ROUND(100.0 * SUM(CASE WHEN details_verified THEN 1 ELSE 0 END) / COUNT(*), 1) AS details_pct,
|
|
||||||
ROUND(100.0 * SUM(CASE WHEN fully_verified THEN 1 ELSE 0 END) / COUNT(*), 1) AS fully_pct
|
|
||||||
FROM transceivers;
|
|
||||||
Loading…
x
Reference in New Issue
Block a user