Files
avbeat-com/supabase/migrations/20260322240000_sections_15_to_19.sql
2026-05-07 16:39:17 +00:00

600 lines
28 KiB
SQL

-- ============================================================
-- Migration: Sections 15-19
-- Timestamp: 20260322240000
-- Covers: event_calendar, author_style_profiles, show_story_queue,
-- translations, ui_translations, AV Beat seed companies,
-- pen name roster update (authorized rosters only),
-- blocked sources update (remove infocommshow.org),
-- language preferences
-- ============================================================
-- ─── 1. Event Calendar ───────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS public.event_calendar (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
event_name TEXT NOT NULL,
event_type TEXT NOT NULL DEFAULT 'trade_show' CHECK (event_type IN (
'trade_show','conference','regional_expo','awards','virtual'
)),
site_id INTEGER NOT NULL DEFAULT 1 REFERENCES public.sites(id),
city TEXT,
venue TEXT,
country TEXT,
start_date DATE,
end_date DATE,
story_engine_start_date DATE,
story_engine_end_date DATE,
official_site TEXT,
seo_tier INTEGER NOT NULL DEFAULT 3 CHECK (seo_tier BETWEEN 1 AND 4),
status TEXT NOT NULL DEFAULT 'upcoming' CHECK (status IN (
'upcoming','active','post_show','off_season','cancelled','unconfirmed'
)),
year INTEGER NOT NULL DEFAULT EXTRACT(YEAR FROM CURRENT_DATE)::INTEGER,
phase1_volume_min INTEGER NOT NULL DEFAULT 1,
phase1_volume_max INTEGER NOT NULL DEFAULT 3,
phase2_volume_min INTEGER NOT NULL DEFAULT 3,
phase2_volume_max INTEGER NOT NULL DEFAULT 5,
phase3_volume_min INTEGER NOT NULL DEFAULT 5,
phase3_volume_max INTEGER NOT NULL DEFAULT 8,
phase4_volume_min INTEGER NOT NULL DEFAULT 1,
phase4_volume_max INTEGER NOT NULL DEFAULT 3,
phase5_volume_min INTEGER NOT NULL DEFAULT 1,
phase5_volume_max INTEGER NOT NULL DEFAULT 2,
auto_publish_enabled BOOLEAN NOT NULL DEFAULT false,
engine_paused BOOLEAN NOT NULL DEFAULT false,
notes TEXT,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_event_calendar_site_id ON public.event_calendar(site_id);
CREATE INDEX IF NOT EXISTS idx_event_calendar_year ON public.event_calendar(year);
CREATE INDEX IF NOT EXISTS idx_event_calendar_status ON public.event_calendar(status);
CREATE INDEX IF NOT EXISTS idx_event_calendar_start_date ON public.event_calendar(start_date);
ALTER TABLE public.event_calendar ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS "admin_manage_event_calendar" ON public.event_calendar;
CREATE POLICY "admin_manage_event_calendar"
ON public.event_calendar FOR ALL TO authenticated
USING (true) WITH CHECK (true);
DROP POLICY IF EXISTS "public_read_event_calendar" ON public.event_calendar;
CREATE POLICY "public_read_event_calendar"
ON public.event_calendar FOR SELECT TO public USING (true);
-- ─── 2. Seed Broadcast Beat shows (site_id = 1) ──────────────────────────────
INSERT INTO public.event_calendar (
event_name, event_type, site_id, city, venue, country,
start_date, end_date, story_engine_start_date, story_engine_end_date,
official_site, seo_tier, year,
phase1_volume_min, phase1_volume_max,
phase2_volume_min, phase2_volume_max,
phase3_volume_min, phase3_volume_max,
phase4_volume_min, phase4_volume_max,
phase5_volume_min, phase5_volume_max,
notes
) VALUES
-- NAB Show Las Vegas
(
'NAB Show Las Vegas', 'trade_show', 1,
'Las Vegas', 'Las Vegas Convention Center', 'USA',
'2026-04-25', '2026-04-30',
'2026-02-24', '2026-05-14',
'nabshow.com', 1, 2026,
3, 3, 5, 8, 8, 12, 3, 5, 1, 2,
'Primary NAB Show. James Mercer primary byline for pillar content.'
),
-- NAB Show New York
(
'NAB Show New York', 'trade_show', 1,
'New York', 'Javits Center', 'USA',
'2026-10-19', '2026-10-21',
'2026-08-20', '2026-10-28',
'nabshow.com', 2, 2026,
3, 3, 5, 8, 8, 12, 3, 5, 1, 2,
'NAB Show New York. James Mercer primary byline.'
),
-- IBC Show
(
'IBC Show', 'trade_show', 1,
'Amsterdam', 'RAI Amsterdam', 'Netherlands',
'2026-09-11', '2026-09-14',
'2026-07-13', '2026-09-28',
'ibc.org', 1, 2026,
3, 3, 5, 8, 8, 10, 3, 5, 1, 2,
'European equivalent of NAB Las Vegas. Ellen Forsythe primary byline.'
),
-- BroadcastAsia / ATxSG
(
'BroadcastAsia / ATxSG', 'trade_show', 1,
'Singapore', 'Singapore Expo', 'Singapore',
'2026-05-27', '2026-05-29',
'2026-03-28', '2026-06-05',
'broadcastasia.com', 2, 2026,
2, 2, 4, 6, 6, 8, 2, 3, 1, 1,
'Cover as both BroadcastAsia and ATxSG/Asia Tech x Singapore. Ellen Forsythe primary byline.'
),
-- CABSAT
(
'CABSAT', 'trade_show', 1,
'Dubai', 'Dubai World Trade Centre', 'UAE',
'2026-03-10', '2026-03-12',
'2026-01-09', '2026-03-19',
'cabsat.com', 2, 2026,
2, 2, 3, 5, 5, 7, 2, 3, 1, 1,
'Premier broadcast/satellite event for Middle East, Africa, South Asia.'
),
-- MPTS
(
'MPTS — Media Production & Technology Show', 'trade_show', 1,
'London', 'Olympia London', 'UK',
'2026-05-13', '2026-05-14',
'2026-03-14', '2026-05-21',
'thempts.co.uk', 3, 2026,
1, 2, 3, 4, 4, 6, 1, 2, 1, 1,
'UK broadcast and media production technology event.'
),
-- NATPE Global
(
'NATPE Global', 'conference', 1,
'TBD', 'TBD', 'TBD',
NULL, NULL, NULL, NULL,
'natpe.org', 3, 2026,
1, 2, 2, 3, 3, 5, 1, 2, 1, 1,
'Confirm dates annually. Format and location varies. Content/programming focus.'
),
-- SET Expo
(
'SET Expo', 'trade_show', 1,
'São Paulo', 'São Paulo Expo', 'Brazil',
'2026-08-18', '2026-08-20',
'2026-07-04', '2026-08-27',
'setexpo.com.br', 3, 2026,
1, 2, 2, 4, 4, 6, 1, 2, 1, 1,
'Latin America largest broadcast tech event. English stories from Portuguese sources.'
),
-- NewsTECHForum
(
'NewsTECHForum', 'conference', 1,
'New York', 'TBD', 'USA',
'2026-01-14', '2026-01-15',
'2025-12-15', '2026-01-20',
'newstechforum.net', 4, 2026,
1, 1, 2, 3, 3, 4, 1, 1, 1, 1,
'News broadcasting technology. Smaller targeted event.'
),
-- Silicon Valley Video Summit
(
'Silicon Valley Video Summit (SVVS)', 'conference', 1,
'San Francisco Bay Area', 'TBD', 'USA',
NULL, NULL, NULL, NULL,
NULL, 4, 2026,
1, 1, 2, 2, 2, 3, 1, 1, 1, 1,
'Confirm dates annually. Video technology innovation and streaming.'
),
-- TAB Show
(
'TAB Show', 'trade_show', 1,
'Austin', 'Austin Convention Center', 'USA',
'2026-08-12', '2026-08-13',
'2026-07-13', '2026-08-18',
'tab.org', 4, 2026,
1, 1, 2, 2, 3, 3, 1, 1, 1, 1,
'Texas Association of Broadcasters. Regional US broadcast show.'
),
-- Hamburg Open
(
'Hamburg Open', 'conference', 1,
'Hamburg', 'TBD', 'Germany',
NULL, NULL, NULL, NULL,
NULL, 4, 2026,
1, 1, 2, 2, 2, 3, 1, 1, 1, 1,
'Confirm dates annually. European broadcast/media tech. DACH market.'
),
-- AES Convention (Fall NY)
(
'AES Convention — New York', 'trade_show', 1,
'New York', 'Javits Center', 'USA',
'2026-10-28', '2026-10-31',
'2026-09-13', '2026-11-07',
'aes.org/events/conventions/', 2, 2026,
1, 2, 3, 4, 4, 6, 1, 2, 1, 1,
'Fall NY convention — primary AES event. Audio-focused pen names preferred. Thomas Reeves primary byline.'
),
-- AES Convention (Spring Europe)
(
'AES Convention — Europe', 'trade_show', 1,
'TBD', 'TBD', 'Europe',
NULL, NULL, NULL, NULL,
'aes.org/events/conventions/', 2, 2026,
1, 2, 3, 4, 4, 6, 1, 2, 1, 1,
'Spring European convention. Confirm city annually. Thomas Reeves primary byline.'
)
ON CONFLICT DO NOTHING;
-- ─── 3. Seed AV Beat shows (site_id = 2) ─────────────────────────────────────
INSERT INTO public.event_calendar (
event_name, event_type, site_id, city, venue, country,
start_date, end_date, story_engine_start_date, story_engine_end_date,
official_site, seo_tier, year,
phase1_volume_min, phase1_volume_max,
phase2_volume_min, phase2_volume_max,
phase3_volume_min, phase3_volume_max,
phase4_volume_min, phase4_volume_max,
phase5_volume_min, phase5_volume_max,
notes
) VALUES
-- InfoComm
(
'InfoComm', 'trade_show', 2,
'Las Vegas', 'Las Vegas Convention Center', 'USA',
'2026-06-17', '2026-06-19',
'2026-04-18', '2026-07-03',
'infocommshow.org', 1, 2026,
3, 3, 5, 8, 8, 12, 3, 5, 1, 2,
'InfoComm 2026 Las Vegas June 13-19 (exhibits June 17-19). Rex Chandler primary byline. Cover AVIXA XL Awards and FORTÉ Smart Workplace hub.'
),
-- ISE
(
'ISE — Integrated Systems Europe', 'trade_show', 2,
'Barcelona', 'Fira Barcelona Gran Vía', 'Spain',
'2026-02-03', '2026-02-06',
'2025-12-05', '2026-02-20',
'iseurope.org', 1, 2026,
3, 3, 5, 8, 8, 10, 3, 5, 1, 2,
'World largest pro AV show. 92,000+ attendees. Cover ISE Innovation Awards. Rex Chandler / Dana Flux primary bylines.'
),
-- InfoComm EDGE Dubai
(
'InfoComm EDGE Dubai', 'trade_show', 2,
'Dubai', 'Festival Arena', 'UAE',
'2026-10-27', '2026-10-28',
'2026-09-12', '2026-11-04',
'avixa.org', 2, 2026,
2, 2, 3, 4, 4, 5, 1, 2, 1, 1,
'New AVIXA event launching 2026. Experiential format. GCC/Middle East focus. Chip Crosspoint / Derek Wainwright bylines.'
),
-- InfoComm China
(
'InfoComm China — Beijing', 'trade_show', 2,
'Beijing', 'TBD', 'China',
'2026-04-15', '2026-04-17',
'2026-03-16', '2026-04-24',
'infocommchina.com', 2, 2026,
1, 2, 2, 3, 3, 5, 1, 2, 1, 1,
'Asia leading pro AV show. Chinese AV manufacturers and Asia-Pacific market.'
),
-- InfoComm Latin America
(
'InfoComm América Latina', 'trade_show', 2,
'Mexico City', 'WTC Convention Center', 'Mexico',
'2026-10-21', '2026-10-23',
'2026-09-21', '2026-10-30',
'infocommshow.org', 2, 2026,
1, 2, 2, 3, 3, 5, 1, 2, 1, 1,
'Latin America leading pro AV. 5,000+ attendees. Congreso AVIXA co-located. Blair Presenter primary byline.'
),
-- InfoComm India
(
'InfoComm India', 'trade_show', 2,
'Mumbai', 'TBD', 'India',
'2026-09-10', '2026-09-11',
'2026-08-11', '2026-09-16',
'avixa.org', 3, 2026,
1, 1, 2, 2, 2, 3, 1, 1, 1, 1,
'India leading pro AV event. Fastest-growing AV market globally per AVIXA.'
),
-- FORTÉ LIVE
(
'FORTÉ LIVE Regional Expos', 'regional_expo', 2,
'Multiple US Cities', 'Various', 'USA',
NULL, NULL, NULL, NULL,
'ourforte.com/events', 3, 2026,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
'Per-city: 1 preview + 1 recap. Monitor ourforte.com/events for dates. Dallas, Milwaukee, Atlanta, DC area confirmed. FORTÉ is Premier Sponsor of InfoComm 2026.'
),
-- AVIXA Regional Events
(
'AVIXA Regional Events', 'regional_expo', 2,
'Various', 'Various', 'Various',
NULL, NULL, NULL, NULL,
'avixa.org/events', 4, 2026,
1, 1, 1, 1, 1, 2, 1, 1, 1, 1,
'Monitor avixa.org/events. Cover events with exhibition component. Skip pure training events.'
)
ON CONFLICT DO NOTHING;
-- ─── 4. Author Style Profiles ────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS public.author_style_profiles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
author_username TEXT NOT NULL,
event_type TEXT NOT NULL DEFAULT 'general',
event_name TEXT,
style_guide_text TEXT,
source_article_count INTEGER NOT NULL DEFAULT 0,
last_updated TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_author_style_profiles_unique
ON public.author_style_profiles(author_username, event_type, COALESCE(event_name, ''));
CREATE INDEX IF NOT EXISTS idx_author_style_profiles_username ON public.author_style_profiles(author_username);
CREATE INDEX IF NOT EXISTS idx_author_style_profiles_event ON public.author_style_profiles(event_type);
ALTER TABLE public.author_style_profiles ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS "admin_manage_author_style_profiles" ON public.author_style_profiles;
CREATE POLICY "admin_manage_author_style_profiles"
ON public.author_style_profiles FOR ALL TO authenticated
USING (true) WITH CHECK (true);
-- ─── 5. Show Story Queue (extends auto_story_queue for show-specific stories) ─
ALTER TABLE public.auto_story_queue
ADD COLUMN IF NOT EXISTS event_id UUID REFERENCES public.event_calendar(id) ON DELETE SET NULL,
ADD COLUMN IF NOT EXISTS show_phase TEXT CHECK (show_phase IN (
'phase1_buildup','phase2_surge','phase3_show_week','phase4_post_show','phase5_off_season'
)),
ADD COLUMN IF NOT EXISTS show_story_type TEXT CHECK (show_story_type IN (
'exhibitor_preview','award_announcement','show_preview_guide','product_announcement',
'keynote_preview','travel_logistics','breaking_announcement','show_floor_report',
'award_winner','best_of_roundup','recap','product_availability','nab_cross_post'
)),
ADD COLUMN IF NOT EXISTS source_show TEXT,
ADD COLUMN IF NOT EXISTS nab_exhibitor_id UUID,
ADD COLUMN IF NOT EXISTS seo_primary_keyword TEXT,
ADD COLUMN IF NOT EXISTS seo_meta_description TEXT;
CREATE INDEX IF NOT EXISTS idx_auto_story_queue_event_id ON public.auto_story_queue(event_id);
CREATE INDEX IF NOT EXISTS idx_auto_story_queue_show_phase ON public.auto_story_queue(show_phase);
-- ─── 6. NAB Exhibitors ───────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS public.nab_exhibitors (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
company_id UUID REFERENCES public.tracked_companies(id) ON DELETE CASCADE,
company_name TEXT NOT NULL,
event_id UUID REFERENCES public.event_calendar(id) ON DELETE CASCADE,
booth_number TEXT,
preview_story_status TEXT NOT NULL DEFAULT 'pending' CHECK (preview_story_status IN (
'pending','generated','published','skipped'
)),
announcement_count INTEGER NOT NULL DEFAULT 0,
is_av_eligible BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
UNIQUE(company_name, event_id)
);
CREATE INDEX IF NOT EXISTS idx_nab_exhibitors_event_id ON public.nab_exhibitors(event_id);
CREATE INDEX IF NOT EXISTS idx_nab_exhibitors_company_id ON public.nab_exhibitors(company_id);
ALTER TABLE public.nab_exhibitors ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS "admin_manage_nab_exhibitors" ON public.nab_exhibitors;
CREATE POLICY "admin_manage_nab_exhibitors"
ON public.nab_exhibitors FOR ALL TO authenticated
USING (true) WITH CHECK (true);
-- ─── 7. Translations table ───────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS public.translations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
post_id UUID NOT NULL,
post_type TEXT NOT NULL DEFAULT 'native' CHECK (post_type IN ('native','imported')),
language_code TEXT NOT NULL CHECK (language_code IN ('en','es','pt','fr','de','zh','ja','ar','hi')),
title_translated TEXT,
content_translated TEXT,
excerpt_translated TEXT,
meta_description_translated TEXT,
slug_translated TEXT,
generated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
is_stale BOOLEAN NOT NULL DEFAULT false,
word_count INTEGER,
UNIQUE(post_id, language_code)
);
CREATE INDEX IF NOT EXISTS idx_translations_post_id ON public.translations(post_id);
CREATE INDEX IF NOT EXISTS idx_translations_language ON public.translations(language_code);
CREATE INDEX IF NOT EXISTS idx_translations_stale ON public.translations(is_stale) WHERE is_stale = true;
ALTER TABLE public.translations ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS "public_read_translations" ON public.translations;
CREATE POLICY "public_read_translations"
ON public.translations FOR SELECT TO public USING (true);
DROP POLICY IF EXISTS "admin_manage_translations" ON public.translations;
CREATE POLICY "admin_manage_translations"
ON public.translations FOR ALL TO authenticated
USING (true) WITH CHECK (true);
-- ─── 8. UI Translations (admin-generated public labels) ─────────────────────
CREATE TABLE IF NOT EXISTS public.ui_translations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
key TEXT NOT NULL,
language_code TEXT NOT NULL CHECK (language_code IN ('en','es','pt','fr','de','zh','ja','ar','hi')),
value TEXT NOT NULL,
context TEXT,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
UNIQUE(key, language_code)
);
CREATE INDEX IF NOT EXISTS idx_ui_translations_key ON public.ui_translations(key);
CREATE INDEX IF NOT EXISTS idx_ui_translations_lang ON public.ui_translations(language_code);
ALTER TABLE public.ui_translations ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS "public_read_ui_translations" ON public.ui_translations;
CREATE POLICY "public_read_ui_translations"
ON public.ui_translations FOR SELECT TO public USING (true);
DROP POLICY IF EXISTS "admin_manage_ui_translations" ON public.ui_translations;
CREATE POLICY "admin_manage_ui_translations"
ON public.ui_translations FOR ALL TO authenticated
USING (true) WITH CHECK (true);
-- ─── 9. Translation Queue ────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS public.translation_queue (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
post_id UUID NOT NULL,
post_type TEXT NOT NULL DEFAULT 'native',
language_code TEXT NOT NULL,
priority INTEGER NOT NULL DEFAULT 5,
status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','processing','completed','failed')),
error_message TEXT,
queued_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
processed_at TIMESTAMPTZ,
UNIQUE(post_id, language_code, status)
);
CREATE INDEX IF NOT EXISTS idx_translation_queue_status ON public.translation_queue(status);
CREATE INDEX IF NOT EXISTS idx_translation_queue_priority ON public.translation_queue(priority DESC, queued_at ASC);
ALTER TABLE public.translation_queue ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS "admin_manage_translation_queue" ON public.translation_queue;
CREATE POLICY "admin_manage_translation_queue"
ON public.translation_queue FOR ALL TO authenticated
USING (true) WITH CHECK (true);
-- ─── 10. User Language Preferences ──────────────────────────────────────────
ALTER TABLE public.user_profiles
ADD COLUMN IF NOT EXISTS lang_pref TEXT DEFAULT 'en' CHECK (lang_pref IN ('en','es','pt','fr','de','zh','ja','ar','hi'));
-- ─── 11. Update pen_names table with authorized rosters ──────────────────────
-- Remove old placeholder BB pen names and insert authorized roster
DELETE FROM public.pen_names WHERE site_id = 1;
INSERT INTO public.pen_names (name, site_id, beat, bio, is_active) VALUES
('Michael Strand', 1, 'broadcast-technology', 'Senior Technology Editor. Broadcast technology, industry news, NAB Show primary correspondent.', true),
('David Harlow', 1, 'broadcast-engineering', 'Broadcast Engineering Correspondent. Broadcast engineering, transmission, RF, satellite, signal distribution.', true),
('Karen Fielding', 1, 'post-production', 'Post Production Editor. Post production, editing, color grading, VFX, media asset management.', true),
('James Mercer', 1, 'nab-show', 'NAB Show Senior Correspondent. NAB Show Las Vegas, NAB Show New York, show floor coverage, exhibitor news. PRIMARY byline for all major NAB pillar content, day recaps, and best-of roundups.', true),
('Peter Calloway', 1, 'radio-transmission', 'Radio & Transmission Reporter. Radio broadcasting, AM/FM/DAB, transmission infrastructure, tower and antenna technology.', true),
('Sandra Voss', 1, 'production-technology', 'Production Technology Editor. Studio production, cameras, switchers, graphics systems, live production.', true),
('Brian Kowalski', 1, 'streaming-ott', 'Streaming & OTT Correspondent. Streaming technology, OTT platforms, CDN, video delivery, cloud broadcast.', true),
('Laura Pennington', 1, 'industry-news', 'Industry News Editor. Company news, acquisitions, appointments, executive moves, industry business coverage.', true),
('Thomas Reeves', 1, 'audio-technology', 'Audio Technology Correspondent. Broadcast audio, mixing consoles, audio processing, AES Convention coverage.', true),
('Christine Vale', 1, 'business-industry', 'Business & Industry Reporter. Industry analysis, market trends, opinion, business of broadcast.', true),
('Marcus Webb', 1, 'live-production', 'Live Production Correspondent. Live events, sports broadcasting, remote production, REMI and IP production workflows.', true),
('Ellen Forsythe', 1, 'international-markets', 'International Markets Editor. IBC Show, BroadcastAsia, CABSAT, international broadcast markets, European and Asian coverage.', true)
ON CONFLICT (name) DO UPDATE SET
site_id = EXCLUDED.site_id,
beat = EXCLUDED.beat,
bio = EXCLUDED.bio,
is_active = EXCLUDED.is_active;
-- Update AV Beat pen names with corrected roster (replace Mitch Boardroom with Derek Wainwright)
DELETE FROM public.pen_names WHERE site_id = 2;
INSERT INTO public.pen_names (name, site_id, beat, bio, is_active) VALUES
('Rex Chandler', 2, 'av-integration', 'AV Integration Senior Correspondent. AV systems integration, integrator news, company announcements, InfoComm coverage.', true),
('Dana Flux', 2, 'digital-signage', 'Digital Signage & Display Editor. Digital signage, LED walls, display technology, CMS platforms.', true),
('Derek Wainwright', 2, 'unified-communications', 'Unified Communications Correspondent. UC&C, video conferencing, hybrid meeting rooms, Microsoft Teams Rooms, Zoom Rooms, enterprise collaboration technology.', true),
('Sloane Rigging', 2, 'sound-audio', 'Installed AV & Audio Editor. Installed audio, sound reinforcement, houses of worship AV, DSP, loudspeakers, microphone systems.', true),
('Chip Crosspoint', 2, 'av-networking', 'AV Networking & Technology Correspondent. AV-over-IP, Dante, IPMX, control systems, IT/AV convergence, networked AV.', true),
('Blair Presenter', 2, 'education-corporate-av', 'Education & Corporate AV Editor. Education AV, classroom technology, corporate AV, government AV, ISE coverage.', true),
('Jordan Lumen', 2, 'display-visualization', 'Display & Visualization Correspondent. Projectors, large-venue displays, video walls, visualization technology, simulation.', true)
ON CONFLICT (name) DO UPDATE SET
site_id = EXCLUDED.site_id,
beat = EXCLUDED.beat,
bio = EXCLUDED.bio,
is_active = EXCLUDED.is_active;
-- ─── 12. Fix blocked_sources: remove infocommshow.org, add correct list ──────
UPDATE public.company_tracker_settings
SET blocked_sources = ARRAY[
'tvtechnology.com','broadcastingcable.com','sportsvideo.org',
'digitaltveurope.com','sportspromedia.com','nexttv.com',
'ravepubs.com','avnetwork.com','avtechnology.com',
'commercialintegrator.com','systemscontractor.com',
'avinteriors.com','avinteractive.com','avmagazine.net'
]
WHERE blocked_sources IS NOT NULL;
-- ─── 13. Seed AV Beat tracked companies (Section 19F) ────────────────────────
INSERT INTO public.tracked_companies (company_name, company_website, site_scope, auto_story_enabled, crawl_priority, mention_count)
VALUES
('Crestron', 'crestron.com', 'avbeat', true, 'high', 5),
('Extron', 'extron.com', 'avbeat', true, 'high', 5),
('AMX', 'amx.com', 'avbeat', true, 'standard', 3),
('QSC', 'qsc.com', 'avbeat', true, 'high', 5),
('Biamp', 'biamp.com', 'avbeat', true, 'standard', 3),
('Shure', 'shure.com', 'both', true, 'high', 5),
('Sennheiser', 'sennheiser.com', 'both', true, 'standard', 3),
('Audio-Technica', 'audio-technica.com', 'both', true, 'standard', 3),
('Bose Professional', 'pro.bose.com', 'avbeat', true, 'standard', 3),
('JBL Professional', 'jblpro.com', 'avbeat', true, 'standard', 3),
('Crown Audio', 'crownaudio.com', 'avbeat', true, 'standard', 3),
('BSS Audio', 'bssaudio.com', 'avbeat', true, 'standard', 3),
('Audinate', 'audinate.com', 'avbeat', true, 'standard', 3),
('Samsung Professional', 'samsung.com/business', 'avbeat', true, 'high', 5),
('LG Business Solutions', 'lg.com/business', 'avbeat', true, 'high', 5),
('Sony Professional', 'pro.sony', 'both', true, 'high', 5),
('Panasonic Business', 'business.panasonic.com', 'both', true, 'standard', 3),
('Sharp NEC', 'sharpnecdisplays.com', 'avbeat', true, 'standard', 3),
('Barco', 'barco.com', 'avbeat', true, 'high', 5),
('Christie', 'christiedigital.com', 'avbeat', true, 'standard', 3),
('Epson Professional', 'epson.com/professional', 'avbeat', true, 'standard', 3),
('BenQ', 'benq.com', 'avbeat', true, 'standard', 3),
('Optoma', 'optoma.com', 'avbeat', true, 'standard', 3),
('Planar', 'planar.com', 'avbeat', true, 'standard', 3),
('Leyard', 'leyard.com', 'avbeat', true, 'standard', 3),
('Absen', 'absen.com', 'avbeat', true, 'standard', 3),
('Daktronics', 'daktronics.com', 'avbeat', true, 'standard', 3),
('Primeview', 'primeviewglobal.com', 'avbeat', true, 'standard', 3),
('Unilumin', 'unilumin.com', 'avbeat', true, 'standard', 3),
('Peerless-AV', 'peerless-av.com', 'avbeat', true, 'standard', 3),
('Chief', 'chiefmfg.com', 'avbeat', true, 'standard', 3),
('Middle Atlantic', 'middleatlantic.com', 'avbeat', true, 'standard', 3),
('Legrand AV', 'legrandav.com', 'avbeat', true, 'standard', 3),
('Atlona', 'atlona.com', 'avbeat', true, 'standard', 3),
('Kramer', 'kramerav.com', 'avbeat', true, 'standard', 3),
('ATEN', 'aten.com', 'avbeat', true, 'standard', 3),
('Black Box', 'blackbox.com', 'avbeat', true, 'standard', 3),
('Mersive', 'mersive.com', 'avbeat', true, 'standard', 3),
('Logitech Video Collaboration', 'logitech.com/video-collaboration', 'avbeat', true, 'high', 5),
('Poly', 'poly.com', 'avbeat', true, 'standard', 3),
('Jabra', 'jabra.com', 'avbeat', true, 'standard', 3),
('DTEN', 'dten.com', 'avbeat', true, 'standard', 3),
('Neat', 'neat.no', 'avbeat', true, 'standard', 3),
('Yealink', 'yealink.com', 'avbeat', true, 'standard', 3),
('Cisco Webex', 'webex.com', 'avbeat', true, 'high', 5),
('Crestron Flex', 'crestron.com/flex', 'avbeat', true, 'standard', 3),
('AVI-SPL', 'avispl.com', 'avbeat', true, 'standard', 3),
('Diversified', 'diversifiedus.com', 'avbeat', true, 'standard', 3),
('AVI Systems', 'avisystems.com', 'avbeat', true, 'standard', 3),
('FORTÉ', 'ourforte.com', 'avbeat', true, 'high', 5)
ON CONFLICT (company_name) DO UPDATE SET
site_scope = EXCLUDED.site_scope,
auto_story_enabled = EXCLUDED.auto_story_enabled,
crawl_priority = EXCLUDED.crawl_priority;
-- ─── 14. Triggers ────────────────────────────────────────────────────────────
DROP TRIGGER IF EXISTS update_event_calendar_updated_at ON public.event_calendar;
CREATE TRIGGER update_event_calendar_updated_at
BEFORE UPDATE ON public.event_calendar
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at();
DROP TRIGGER IF EXISTS update_ui_translations_updated_at ON public.ui_translations;
CREATE TRIGGER update_ui_translations_updated_at
BEFORE UPDATE ON public.ui_translations
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at();
-- ─── 15. Seed UI translations (English base keys) ────────────────────────────
INSERT INTO public.ui_translations (key, language_code, value, context) VALUES
('nav.home', 'en', 'Home', 'Navigation'),
('nav.forum', 'en', 'Forum', 'Navigation'),
('nav.advertise', 'en', 'Advertise', 'Navigation'),
('nav.about', 'en', 'About', 'Navigation'),
('nav.subscribe', 'en', 'Subscribe', 'Navigation'),
('lang.en', 'en', 'English', 'Language switcher'),
('lang.es', 'en', 'Español', 'Language switcher'),
('lang.pt', 'en', 'Português', 'Language switcher'),
('lang.fr', 'en', 'Français', 'Language switcher'),
('lang.de', 'en', 'Deutsch', 'Language switcher'),
('lang.zh', 'en', '中文', 'Language switcher'),
('lang.ja', 'en', '日本語', 'Language switcher'),
('lang.ar', 'en', 'العربية', 'Language switcher'),
('lang.hi', 'en', 'हिंदी', 'Language switcher')
ON CONFLICT (key, language_code) DO NOTHING;