#!/usr/bin/env npx tsx /** * Retry resolving platform IDs for Dutchie stores that have menu_url but no platform_dispensary_id * * Usage: * npx tsx src/scripts/retry-platform-ids.ts */ import { Pool } from 'pg'; import dotenv from 'dotenv'; import { resolveDispensaryIdWithDetails } from '../platforms/dutchie/queries'; dotenv.config(); const pool = new Pool({ connectionString: process.env.DATABASE_URL || `postgresql://${process.env.CANNAIQ_DB_USER || 'dutchie'}:${process.env.CANNAIQ_DB_PASS || 'dutchie_local_pass'}@${process.env.CANNAIQ_DB_HOST || 'localhost'}:${process.env.CANNAIQ_DB_PORT || '54320'}/${process.env.CANNAIQ_DB_NAME || 'dutchie_menus'}` }); interface DispensaryRow { id: number; name: string; menu_url: string; } function extractSlugFromUrl(menuUrl: string): string | null { // Extract slug from Dutchie URLs like: // https://dutchie.com/stores/Nirvana-North-Phoenix // https://dutchie.com/dispensary/curaleaf-dispensary-peoria // https://dutchie.com/embedded-menu/some-slug const patterns = [ /dutchie\.com\/stores\/([^/?]+)/i, /dutchie\.com\/dispensary\/([^/?]+)/i, /dutchie\.com\/embedded-menu\/([^/?]+)/i, ]; for (const pattern of patterns) { const match = menuUrl.match(pattern); if (match) { return match[1]; } } return null; } async function main() { console.log('='.repeat(60)); console.log('Retry Platform ID Resolution'); console.log('='.repeat(60)); console.log(''); // Get Dutchie dispensaries with menu_url but no platform_dispensary_id const result = await pool.query(` SELECT id, name, menu_url FROM dispensaries WHERE menu_type = 'dutchie' AND menu_url IS NOT NULL AND menu_url != '' AND (platform_dispensary_id IS NULL OR platform_dispensary_id = '') ORDER BY name `); console.log(`Found ${result.rows.length} stores to retry\n`); if (result.rows.length === 0) { console.log('No stores need platform ID resolution.'); await pool.end(); return; } const successes: { id: number; name: string; platformId: string }[] = []; const failures: { id: number; name: string; slug: string | null; error: string }[] = []; for (const row of result.rows) { console.log(`\n[${row.id}] ${row.name}`); console.log(` URL: ${row.menu_url}`); const slug = extractSlugFromUrl(row.menu_url); if (!slug) { console.log(` āŒ Could not extract slug from URL`); failures.push({ id: row.id, name: row.name, slug: null, error: 'Could not extract slug' }); continue; } console.log(` Slug: ${slug}`); try { const resolveResult = await resolveDispensaryIdWithDetails(slug); if (resolveResult.dispensaryId) { console.log(` āœ… Resolved: ${resolveResult.dispensaryId}`); // Update database await pool.query( 'UPDATE dispensaries SET platform_dispensary_id = $1 WHERE id = $2', [resolveResult.dispensaryId, row.id] ); console.log(` šŸ’¾ Updated database`); successes.push({ id: row.id, name: row.name, platformId: resolveResult.dispensaryId }); } else { const errorMsg = resolveResult.error || 'Unknown error'; console.log(` āŒ Failed: ${errorMsg}`); failures.push({ id: row.id, name: row.name, slug, error: errorMsg }); } } catch (error: any) { console.log(` āŒ Error: ${error.message}`); failures.push({ id: row.id, name: row.name, slug, error: error.message }); } // Small delay between requests await new Promise(r => setTimeout(r, 500)); } console.log('\n' + '='.repeat(60)); console.log('SUMMARY'); console.log('='.repeat(60)); console.log(`\nāœ… Successes (${successes.length}):`); for (const s of successes) { console.log(` [${s.id}] ${s.name} -> ${s.platformId}`); } console.log(`\nāŒ Failures (${failures.length}):`); for (const f of failures) { console.log(` [${f.id}] ${f.name} (slug: ${f.slug || 'N/A'})`); console.log(` ${f.error}`); } await pool.end(); } main().catch(e => { console.error('Fatal error:', e); process.exit(1); });