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>
170 lines
8.3 KiB
JavaScript
170 lines
8.3 KiB
JavaScript
"use strict";
|
|
/**
|
|
* Test script: End-to-end Dutchie GraphQL → DB → Dashboard flow
|
|
*
|
|
* This demonstrates the complete data pipeline:
|
|
* 1. Fetch one product from Dutchie GraphQL via Puppeteer
|
|
* 2. Normalize it to our schema
|
|
* 3. Show the mapping
|
|
*/
|
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
}
|
|
Object.defineProperty(o, k2, desc);
|
|
}) : (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
o[k2] = m[k];
|
|
}));
|
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
}) : function(o, v) {
|
|
o["default"] = v;
|
|
});
|
|
var __importStar = (this && this.__importStar) || (function () {
|
|
var ownKeys = function(o) {
|
|
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
var ar = [];
|
|
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
return ar;
|
|
};
|
|
return ownKeys(o);
|
|
};
|
|
return function (mod) {
|
|
if (mod && mod.__esModule) return mod;
|
|
var result = {};
|
|
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
__setModuleDefault(result, mod);
|
|
return result;
|
|
};
|
|
})();
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
const dutchie_graphql_1 = require("../scrapers/dutchie-graphql");
|
|
const fs = __importStar(require("fs"));
|
|
// Load the captured sample product from schema capture
|
|
const capturedData = JSON.parse(fs.readFileSync('/tmp/dutchie-schema-capture.json', 'utf-8'));
|
|
const sampleProduct = capturedData.sampleProduct;
|
|
console.log('='.repeat(80));
|
|
console.log('DUTCHIE GRAPHQL → DATABASE MAPPING DEMONSTRATION');
|
|
console.log('='.repeat(80));
|
|
console.log('\n📥 RAW DUTCHIE GRAPHQL PRODUCT:');
|
|
console.log('-'.repeat(80));
|
|
// Show key fields from raw product
|
|
const keyRawFields = {
|
|
'_id': sampleProduct._id,
|
|
'Name': sampleProduct.Name,
|
|
'cName': sampleProduct.cName,
|
|
'brandName': sampleProduct.brandName,
|
|
'brand.id': sampleProduct.brand?.id,
|
|
'type': sampleProduct.type,
|
|
'subcategory': sampleProduct.subcategory,
|
|
'strainType': sampleProduct.strainType,
|
|
'Prices': sampleProduct.Prices,
|
|
'recPrices': sampleProduct.recPrices,
|
|
'recSpecialPrices': sampleProduct.recSpecialPrices,
|
|
'special': sampleProduct.special,
|
|
'specialData.saleSpecials[0].specialName': sampleProduct.specialData?.saleSpecials?.[0]?.specialName,
|
|
'specialData.saleSpecials[0].discount': sampleProduct.specialData?.saleSpecials?.[0]?.discount,
|
|
'THCContent.range[0]': sampleProduct.THCContent?.range?.[0],
|
|
'CBDContent.range[0]': sampleProduct.CBDContent?.range?.[0],
|
|
'Status': sampleProduct.Status,
|
|
'Image': sampleProduct.Image,
|
|
'POSMetaData.canonicalSKU': sampleProduct.POSMetaData?.canonicalSKU,
|
|
'POSMetaData.children[0].quantity': sampleProduct.POSMetaData?.children?.[0]?.quantity,
|
|
'POSMetaData.children[0].quantityAvailable': sampleProduct.POSMetaData?.children?.[0]?.quantityAvailable,
|
|
};
|
|
Object.entries(keyRawFields).forEach(([key, value]) => {
|
|
console.log(` ${key}: ${JSON.stringify(value)}`);
|
|
});
|
|
console.log('\n📤 NORMALIZED DATABASE ROW:');
|
|
console.log('-'.repeat(80));
|
|
// Normalize the product
|
|
const normalized = (0, dutchie_graphql_1.normalizeDutchieProduct)(sampleProduct);
|
|
// Show the normalized result (excluding raw_data for readability)
|
|
const { raw_data, cannabinoids, special_data, ...displayFields } = normalized;
|
|
Object.entries(displayFields).forEach(([key, value]) => {
|
|
if (value !== undefined && value !== null) {
|
|
console.log(` ${key}: ${JSON.stringify(value)}`);
|
|
}
|
|
});
|
|
console.log('\n🔗 FIELD MAPPING:');
|
|
console.log('-'.repeat(80));
|
|
const fieldMappings = [
|
|
['_id / id', 'external_id', sampleProduct._id, normalized.external_id],
|
|
['Name', 'name', sampleProduct.Name, normalized.name],
|
|
['cName', 'slug', sampleProduct.cName, normalized.slug],
|
|
['brandName', 'brand', sampleProduct.brandName, normalized.brand],
|
|
['brand.id', 'brand_external_id', sampleProduct.brand?.id, normalized.brand_external_id],
|
|
['subcategory', 'subcategory', sampleProduct.subcategory, normalized.subcategory],
|
|
['strainType', 'strain_type', sampleProduct.strainType, normalized.strain_type],
|
|
['recPrices[0]', 'rec_price', sampleProduct.recPrices?.[0], normalized.rec_price],
|
|
['recSpecialPrices[0]', 'rec_special_price', sampleProduct.recSpecialPrices?.[0], normalized.rec_special_price],
|
|
['special', 'is_on_special', sampleProduct.special, normalized.is_on_special],
|
|
['specialData...specialName', 'special_name', sampleProduct.specialData?.saleSpecials?.[0]?.specialName?.substring(0, 40) + '...', normalized.special_name?.substring(0, 40) + '...'],
|
|
['THCContent.range[0]', 'thc_percentage', sampleProduct.THCContent?.range?.[0], normalized.thc_percentage],
|
|
['CBDContent.range[0]', 'cbd_percentage', sampleProduct.CBDContent?.range?.[0], normalized.cbd_percentage],
|
|
['Status', 'status', sampleProduct.Status, normalized.status],
|
|
['Image', 'image_url', sampleProduct.Image?.substring(0, 50) + '...', normalized.image_url?.substring(0, 50) + '...'],
|
|
['POSMetaData.canonicalSKU', 'sku', sampleProduct.POSMetaData?.canonicalSKU, normalized.sku],
|
|
];
|
|
console.log(' GraphQL Field → DB Column | Value');
|
|
console.log(' ' + '-'.repeat(75));
|
|
fieldMappings.forEach(([gqlField, dbCol, gqlVal, dbVal]) => {
|
|
const gqlStr = String(gqlField).padEnd(30);
|
|
const dbStr = String(dbCol).padEnd(20);
|
|
console.log(` ${gqlStr} → ${dbStr} | ${JSON.stringify(dbVal)}`);
|
|
});
|
|
console.log('\n📊 SQL INSERT STATEMENT:');
|
|
console.log('-'.repeat(80));
|
|
// Generate example SQL
|
|
const sqlExample = `
|
|
INSERT INTO products (
|
|
store_id, external_id, slug, name,
|
|
brand, brand_external_id,
|
|
subcategory, strain_type,
|
|
rec_price, rec_special_price,
|
|
is_on_special, special_name, discount_percent,
|
|
thc_percentage, cbd_percentage,
|
|
status, image_url, sku
|
|
) VALUES (
|
|
1, -- store_id (Deeply Rooted)
|
|
'${normalized.external_id}', -- external_id
|
|
'${normalized.slug}', -- slug
|
|
'${normalized.name}', -- name
|
|
'${normalized.brand}', -- brand
|
|
'${normalized.brand_external_id}', -- brand_external_id
|
|
'${normalized.subcategory}', -- subcategory
|
|
'${normalized.strain_type}', -- strain_type
|
|
${normalized.rec_price}, -- rec_price
|
|
${normalized.rec_special_price}, -- rec_special_price
|
|
${normalized.is_on_special}, -- is_on_special
|
|
'${normalized.special_name?.substring(0, 50)}...', -- special_name
|
|
${normalized.discount_percent || 'NULL'}, -- discount_percent
|
|
${normalized.thc_percentage}, -- thc_percentage
|
|
${normalized.cbd_percentage}, -- cbd_percentage
|
|
'${normalized.status}', -- status
|
|
'${normalized.image_url}', -- image_url
|
|
'${normalized.sku}' -- sku
|
|
)
|
|
ON CONFLICT (store_id, slug) DO UPDATE SET ...;
|
|
`;
|
|
console.log(sqlExample);
|
|
console.log('\n✅ SUMMARY:');
|
|
console.log('-'.repeat(80));
|
|
console.log(` Product: ${normalized.name}`);
|
|
console.log(` Brand: ${normalized.brand}`);
|
|
console.log(` Category: ${normalized.subcategory}`);
|
|
console.log(` Price: $${normalized.rec_price} → $${normalized.rec_special_price} (${normalized.discount_percent}% off)`);
|
|
console.log(` THC: ${normalized.thc_percentage}%`);
|
|
console.log(` Status: ${normalized.status}`);
|
|
console.log(` On Special: ${normalized.is_on_special}`);
|
|
console.log(` SKU: ${normalized.sku}`);
|
|
console.log('\n🎯 DERIVED VIEWS (computed from products table):');
|
|
console.log('-'.repeat(80));
|
|
console.log(' - current_specials: Products where is_on_special = true');
|
|
console.log(' - derived_brands: Aggregated by brand name with counts/prices');
|
|
console.log(' - derived_categories: Aggregated by subcategory');
|
|
console.log('\nAll views are computed from the single products table - no separate tables needed!');
|