The job_run_logs table tracks scheduled job orchestration, not individual worker jobs. Worker info (worker_id, worker_hostname) belongs on dispensary_crawl_jobs, not job_run_logs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
126 lines
5.2 KiB
JavaScript
126 lines
5.2 KiB
JavaScript
"use strict";
|
|
/**
|
|
* Run Dutchie GraphQL Scrape
|
|
*
|
|
* This script demonstrates the full pipeline:
|
|
* 1. Puppeteer navigates to Dutchie menu
|
|
* 2. GraphQL responses are intercepted
|
|
* 3. Products are normalized to our schema
|
|
* 4. Products are upserted to database
|
|
* 5. Derived views (brands, categories, specials) are automatically updated
|
|
*/
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
const pg_1 = require("pg");
|
|
const dutchie_graphql_1 = require("../scrapers/dutchie-graphql");
|
|
const DATABASE_URL = process.env.DATABASE_URL || 'postgresql://dutchie:dutchie_local_pass@localhost:54320/dutchie_menus';
|
|
async function main() {
|
|
const pool = new pg_1.Pool({ connectionString: DATABASE_URL });
|
|
try {
|
|
console.log('='.repeat(80));
|
|
console.log('DUTCHIE GRAPHQL SCRAPER - FULL PIPELINE TEST');
|
|
console.log('='.repeat(80));
|
|
console.log(`Database: ${DATABASE_URL.replace(/:[^:@]+@/, ':***@')}`);
|
|
// Configuration
|
|
const storeId = 1; // Deeply Rooted
|
|
const menuUrl = 'https://dutchie.com/embedded-menu/AZ-Deeply-Rooted';
|
|
console.log(`\nStore ID: ${storeId}`);
|
|
console.log(`Menu URL: ${menuUrl}`);
|
|
console.log('\n' + '-'.repeat(80));
|
|
// Run the scrape
|
|
console.log('\n🚀 Starting scrape...\n');
|
|
const result = await (0, dutchie_graphql_1.scrapeDutchieMenu)(pool, storeId, menuUrl);
|
|
console.log('\n' + '-'.repeat(80));
|
|
console.log('📊 SCRAPE RESULTS:');
|
|
console.log('-'.repeat(80));
|
|
console.log(` Success: ${result.success}`);
|
|
console.log(` Products Found: ${result.productsFound}`);
|
|
console.log(` Inserted: ${result.inserted}`);
|
|
console.log(` Updated: ${result.updated}`);
|
|
if (result.error) {
|
|
console.log(` Error: ${result.error}`);
|
|
}
|
|
// Query derived views to show the result
|
|
if (result.success) {
|
|
console.log('\n' + '-'.repeat(80));
|
|
console.log('📈 DERIVED DATA (from products table):');
|
|
console.log('-'.repeat(80));
|
|
// Brands
|
|
const brandsResult = await pool.query(`
|
|
SELECT brand_name, product_count, min_price, max_price
|
|
FROM derived_brands
|
|
WHERE store_id = $1
|
|
ORDER BY product_count DESC
|
|
LIMIT 5
|
|
`, [storeId]);
|
|
console.log('\nTop 5 Brands:');
|
|
brandsResult.rows.forEach(row => {
|
|
console.log(` - ${row.brand_name}: ${row.product_count} products ($${row.min_price} - $${row.max_price})`);
|
|
});
|
|
// Specials
|
|
const specialsResult = await pool.query(`
|
|
SELECT name, brand, rec_price, rec_special_price, discount_percent
|
|
FROM current_specials
|
|
WHERE store_id = $1
|
|
LIMIT 5
|
|
`, [storeId]);
|
|
console.log('\nTop 5 Specials:');
|
|
if (specialsResult.rows.length === 0) {
|
|
console.log(' (No specials found - is_on_special may not be populated yet)');
|
|
}
|
|
else {
|
|
specialsResult.rows.forEach(row => {
|
|
console.log(` - ${row.name} (${row.brand}): $${row.rec_price} → $${row.rec_special_price} (${row.discount_percent}% off)`);
|
|
});
|
|
}
|
|
// Categories
|
|
const categoriesResult = await pool.query(`
|
|
SELECT category_name, product_count
|
|
FROM derived_categories
|
|
WHERE store_id = $1
|
|
ORDER BY product_count DESC
|
|
LIMIT 5
|
|
`, [storeId]);
|
|
console.log('\nTop 5 Categories:');
|
|
if (categoriesResult.rows.length === 0) {
|
|
console.log(' (No categories found - subcategory may not be populated yet)');
|
|
}
|
|
else {
|
|
categoriesResult.rows.forEach(row => {
|
|
console.log(` - ${row.category_name}: ${row.product_count} products`);
|
|
});
|
|
}
|
|
// Sample product
|
|
const sampleResult = await pool.query(`
|
|
SELECT name, brand, subcategory, rec_price, rec_special_price, is_on_special, thc_percentage, status
|
|
FROM products
|
|
WHERE store_id = $1 AND subcategory IS NOT NULL
|
|
ORDER BY updated_at DESC
|
|
LIMIT 1
|
|
`, [storeId]);
|
|
if (sampleResult.rows.length > 0) {
|
|
const sample = sampleResult.rows[0];
|
|
console.log('\nSample Product (with new fields):');
|
|
console.log(` Name: ${sample.name}`);
|
|
console.log(` Brand: ${sample.brand}`);
|
|
console.log(` Category: ${sample.subcategory}`);
|
|
console.log(` Price: $${sample.rec_price}`);
|
|
console.log(` Sale Price: ${sample.rec_special_price ? `$${sample.rec_special_price}` : 'N/A'}`);
|
|
console.log(` On Special: ${sample.is_on_special}`);
|
|
console.log(` THC: ${sample.thc_percentage}%`);
|
|
console.log(` Status: ${sample.status}`);
|
|
}
|
|
}
|
|
console.log('\n' + '='.repeat(80));
|
|
console.log('✅ SCRAPE COMPLETE');
|
|
console.log('='.repeat(80));
|
|
}
|
|
catch (error) {
|
|
console.error('\n❌ Error:', error.message);
|
|
throw error;
|
|
}
|
|
finally {
|
|
await pool.end();
|
|
}
|
|
}
|
|
main().catch(console.error);
|