#!/usr/bin/env node /** * Script to update campaign call-to-action text with correct minister titles * Run with: node scripts/update-campaigns.js */ const https = require('https'); // NocoDB Configuration const NOCODB_API_URL = 'https://db.freealberta.org'; const NOCODB_API_TOKEN = 'H3z5PEgvrC25LDRuvRpF1wuzsByG9PCem5DHyjPj'; const PROJECT_ID = 'plc0u50kgobr2xh'; const CAMPAIGNS_TABLE_ID = 'mh98emvhot9gjrg'; // Campaign updates - mapping slug to correct call-to-action text const campaignUpdates = [ // Indigenous Relations - now Rajan Sawhney { slug: 'indigenous-relations', call_to_action: 'Email Minister of Indigenous Relations' }, { slug: 'land-rights', call_to_action: 'Email Minister of Indigenous Relations' }, { slug: 'landback', call_to_action: 'Email Minister Responsible for Landback' }, // Municipal Affairs - now Dan Williams { slug: 'land-use', call_to_action: 'Email Minister of Municipal Affairs' }, { slug: 'municipal-transit', call_to_action: 'Email Minister of Municipal Affairs' }, { slug: 'gathering-spaces-municipal', call_to_action: 'Email Minister of Municipal Affairs' }, // Environment - now Grant Hunter { slug: 'environment-protection', call_to_action: 'Email Minister of Environment and Protected Areas' }, { slug: 'water-protection', call_to_action: 'Email Minister of Environment and Protected Areas' }, { slug: 'air-quality-protection', call_to_action: 'Email Minister of Environment and Protected Areas' }, // Mental Health and Addiction - now Rick Wilson { slug: 'mental-health', call_to_action: 'Email Minister of Mental Health and Addiction' }, { slug: 'mental-health-rest', call_to_action: 'Email Minister of Mental Health and Addiction' }, { slug: 'love-williams', call_to_action: 'Email Minister of Mental Health and Addiction' }, // Advanced Education - now Myles McDougall { slug: 'higher-education', call_to_action: 'Email Minister of Advanced Education' }, { slug: 'higher-learning', call_to_action: 'Email Minister of Advanced Education' }, // Jobs, Economy, Trade and Immigration - now Joseph Schow { slug: 'economic-reform', call_to_action: 'Email Minister of Jobs, Economy, Trade and Immigration' }, { slug: 'career-transition', call_to_action: 'Email Minister of Jobs, Economy, Trade and Immigration' }, { slug: 'work-life', call_to_action: 'Email Minister of Jobs, Economy, Trade and Immigration' }, { slug: 'association-rights-jobs', call_to_action: 'Email Minister of Jobs, Economy, Trade and Immigration' }, // Tourism and Sport - now Andrew Boitchenko { slug: 'corporate-oversight', call_to_action: 'Email Minister of Tourism and Sport' }, { slug: 'public-spaces', call_to_action: 'Email Minister of Tourism and Sport' }, { slug: 'recreation-tourism', call_to_action: 'Email Minister of Tourism and Sport' }, // Assisted Living and Social Services - now Jason Nixon (updated title) { slug: 'social-support', call_to_action: 'Email Minister of Assisted Living and Social Services' }, { slug: 'food-access', call_to_action: 'Email Minister of Assisted Living and Social Services' }, { slug: 'water-access', call_to_action: 'Email Minister of Assisted Living and Social Services' }, { slug: 'seniors-housing', call_to_action: 'Email Minister of Assisted Living and Social Services' }, // Associate Minister of Multiculturalism - Muhammad Yaseen (not full minister) { slug: 'immigration-rights', call_to_action: 'Email Associate Minister of Multiculturalism' }, { slug: 'cultural-association-rights', call_to_action: 'Email Associate Minister of Multiculturalism' }, // Public Safety and Emergency Services - Mike Ellis (updated title) { slug: 'police-accountability', call_to_action: 'Email Minister of Public Safety and Emergency Services' }, { slug: 'anti-corruption', call_to_action: 'Email Minister of Public Safety and Emergency Services' }, { slug: 'assembly-rights', call_to_action: 'Email Minister of Public Safety and Emergency Services' }, // Energy and Minerals - Brian Jean (updated title) { slug: 'clean-energy', call_to_action: 'Email Minister of Energy and Minerals' }, { slug: 'energy-reform', call_to_action: 'Email Minister of Energy and Minerals' }, // Education and Childcare - Demetrios Nicolaides (updated title) { slug: 'education-reform', call_to_action: 'Email Minister of Education and Childcare' }, { slug: 'learning-access', call_to_action: 'Email Minister of Education and Childcare' }, // Transportation and Economic Corridors - Devin Dreeshen (updated title) { slug: 'transport-reform', call_to_action: 'Email Minister of Transportation and Economic Corridors' }, // Agriculture and Irrigation - RJ Sigurdson (updated title) { slug: 'food-security', call_to_action: 'Email Minister of Agriculture and Irrigation' }, // Service Alberta and Red Tape Reduction - Dale Nally (updated title) { slug: 'public-services-reform', call_to_action: 'Email Minister of Service Alberta and Red Tape Reduction' }, { slug: 'telecom-reform', call_to_action: 'Email Minister of Service Alberta and Red Tape Reduction' }, // Affordability and Utilities - Nathan Neudorf { slug: 'utilities-reform', call_to_action: 'Email Minister of Affordability and Utilities' }, ]; // Get campaign by slug async function getCampaignBySlug(slug) { return new Promise((resolve, reject) => { const options = { hostname: 'db.freealberta.org', port: 443, path: `/api/v1/db/data/v1/${PROJECT_ID}/${CAMPAIGNS_TABLE_ID}?where=(Campaign%20Slug,eq,${slug})`, method: 'GET', headers: { 'xc-token': NOCODB_API_TOKEN } }; const req = https.request(options, (res) => { let data = ''; res.on('data', chunk => data += chunk); res.on('end', () => { try { const result = JSON.parse(data); resolve(result.list && result.list.length > 0 ? result.list[0] : null); } catch (e) { reject(e); } }); }); req.on('error', reject); req.end(); }); } // Update campaign async function updateCampaign(id, updates) { return new Promise((resolve, reject) => { const postData = JSON.stringify(updates); const options = { hostname: 'db.freealberta.org', port: 443, path: `/api/v1/db/data/v1/${PROJECT_ID}/${CAMPAIGNS_TABLE_ID}/${id}`, method: 'PATCH', headers: { 'Content-Type': 'application/json', 'xc-token': NOCODB_API_TOKEN, 'Content-Length': Buffer.byteLength(postData) } }; const req = https.request(options, (res) => { let data = ''; res.on('data', chunk => data += chunk); res.on('end', () => { if (res.statusCode >= 200 && res.statusCode < 300) { resolve({ success: true }); } else { reject(new Error(`Failed: ${res.statusCode} - ${data}`)); } }); }); req.on('error', reject); req.write(postData); req.end(); }); } async function main() { console.log(`Updating ${campaignUpdates.length} campaigns with correct minister info...`); console.log('='.repeat(60)); let successCount = 0; let failCount = 0; let notFoundCount = 0; for (const update of campaignUpdates) { try { const campaign = await getCampaignBySlug(update.slug); if (!campaign) { console.log(`⚠ Not found: ${update.slug}`); notFoundCount++; continue; } const id = campaign.Id || campaign.ID || campaign.id; await updateCampaign(id, { 'Call to Action': update.call_to_action }); console.log(`✓ Updated: ${update.slug} → "${update.call_to_action}"`); successCount++; // Small delay await new Promise(resolve => setTimeout(resolve, 100)); } catch (error) { console.error(`✗ Failed: ${update.slug} - ${error.message}`); failCount++; } } console.log('='.repeat(60)); console.log(`Done! Updated: ${successCount}, Failed: ${failCount}, Not found: ${notFoundCount}`); } main().catch(console.error);