/** * 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;