Object.entries(require('./common.js')).forEach(([name, exported]) => global[name] = exported);
const url = "https://ocs.ca";
function reqq(level, category, name) {
return request({
url: 'https://u2ghas8n0v-dsn.algolia.net/1/indexes/ocs_products/query?x-algolia-application-id=U2GHAS8N0V&x-algolia-api-key=0fce38bd280fd213b8a14f7a59602b7d',
method: 'POST',
body: '{"params":"hitsPerPage=1000&distinct=0&filters=(NOT tags:%22subsubcategory--Variety Packs%22) AND (NOT tags:subsubcategory--Topicals) AND (tags:language--en) AND (tags:%22' + level + 'category--' + category + '%22)"}'
}).then(obj => {
obj = JSON.parse(obj);
if(obj.nbHits > obj.hitsPerPage) {
console.log("missing " + (obj.nbHits - obj.hitsPerPage) + ' items for ' + category);
}
for(let i = 0; i < obj.hits.length; ++i) {
obj.hits[i].type = name;
}
return obj.hits;
});
}
function ok() {
for(let i = 0; i < arguments.length; ++i) {
if(arguments[i] === undefined || arguments[i] === null) {
return false;
}
}
return true;
}
(async function() {
let e = Promise.all([
reqq('sub', 'Dried Flower', 'flower'),
reqq('sub', 'Pre-Rolls', 'preroll'),
reqq('sub', 'Oils', 'oil'),
reqq('sub', 'Capsules', 'capsule'),
reqq('subsub', '510 Thread Pens and Kits', 'cartridge'),
reqq('subsub', 'Disposable Pens and Kits', 'disposable'),
reqq('subsub', 'Proprietary Systems Pens and Kits', 'cartridge'),
reqq('subsub', 'Coffees and Teas', 'tea'),
reqq('subsub', 'Soft Chews', 'gummy'),
reqq('subsub', 'Hard Edibles', 'mint'),
reqq('sub', 'Chocolates', 'chocolate'),
reqq('subsub', 'Cookies', 'cookie')
]).then(e => Object.values(groupBy(e.flat(), 'handle')));
await postgres.connect();
await postgres.query('begin');
e = await e;
let storeID = await return_key(
`insert into store (
name,
URL,
region,
country
) values (
$1, $2, $3, $4
) on conflict do nothing
returning ID`,
[
'Ontario Cannabis Store',
url,
'ontario',
'canada'
]
);
if(storeID.length === 0) {
storeID = await return_key(
`select
ID
from
store
where
name = $1 and
URL = $2 and
region = $3 and
country = $4 and
address is null and
address2 is null and
city is null and
postal_code is null and
coordinate is null and
timezone is null`,
[
'Ontario Cannabis Store',
url,
'ontario',
'canada'
]
);
if(storeID.length === 0) {
console.error("why can't I find the store???");
process.exit(1);
}
}
storeID = storeID[0][0];
//image: https://cdn.shopify.com/s/files/1/2636/1928/files/OCS_EN_LOGO_BLK_SM_nav.png?v=1560138074
//what the font
//stupid as fuck though. we can do better
let ii = 0;
let progress = 0;
cliProgressBar.start(e.length, progress);
async function deal() {
while(ii < e.length) {
let i = ii++;
let minTHC, maxTHC, minCBD, maxCBD, unit, UOM;
if(e[i][0].type === 'flower' || e[i][0].type === 'preroll' || e[i][0].type === 'cartridge' || e[i][0].type === 'disposable') {
minTHC = percent2proportion(e[i][0].meta.product.thc_content_min);
maxTHC = percent2proportion(e[i][0].meta.product.thc_content_max);
minCBD = percent2proportion(e[i][0].meta.product.cbd_content_min);
maxCBD = percent2proportion(e[i][0].meta.product.cbd_content_max);
unit = 'proportion';
UOM = 'g';
} else {
minTHC = e[i][0].meta.variant.thc_min_per
maxTHC = e[i][0].meta.variant.thc_max_per;
minCBD = e[i][0].meta.variant.cbd_min_per;
maxCBD = e[i][0].meta.variant.cbd_max_per;
unit = 'mg';
if(e[i][0].type === 'oil') {
UOM = 'ml';
} else if(e[i][0].type === 'tea') {
UOM = 'sachet';
} else if(e[i][0].type === 'chocolate') {
UOM = 'piece';
} else {//capsule, gummy, mint, cookie
UOM = e[i][0].type;
}
if(e[i][0].type === 'chocolate' || e[i][0].type === 'gummy' || e[i][0].type === 'mint') {
let n = new Big(e[i][0].meta.variant.retail_pack_number_of_items);
minTHC = (new Big(minTHC)).div(n).toString();
maxTHC = (new Big(maxTHC)).div(n).toString();
minCBD = (new Big(minCBD)).div(n).toString();
minCBD = (new Big(minCBD)).div(n).toString();
}
}
for(let j = 0; j < e[i][0].tags.length; ++j) {
if(e[i][0].tags[j] === 'subsubcategory--Oral Sprays') {
e[i][0].type = 'spray';
}
}
let [brand, title] = normalize(e[i][0].vendor, e[i][0].title, e[i][0].type);
let fuckoff = e[i][0].meta.product.plant_type;
let f2;
for(let f = 0; f < e[i][0].tags.length; ++f) {
if(e[i][0].tags[f].startsWith('plant_type--')) {
if(f2 !== undefined) {
console.log("I HATE MY LIFE")
}
f2 = e[i][0].tags[f].slice(12);
}
}
if(fuckoff === undefined) {
fuckoff = f2
}
if(f2 !== fuckoff) {
console.log("this is so gay")
console.log(f2)
console.log(fuckoff);
}
let description = e[i][0].body_html_safe;
if(typeof description === 'string') {
description = description.replace(/\s+/g, ' ');
}
let terpenes = [];
for(let j = 0; j < e[i][0].tags.length; ++j) {
let terpene = e[i][0].tags[j].split('terpenes--')[1];
if(terpene) {
terpenes.push(terpene);
}
}
let productID = await return_key(
`insert into product (
joined_brand,
joined_name,
type,
display_brand,
display_name,
strain,
description,
concentration_unit,
measurement_unit,
show,
terpenes
) values (
$1, $2, $3, $4, $5, $6, $7, $8, $9, true, $10
) on conflict do nothing
returning ID`,
[
brand,
title,
e[i][0].type,
e[i][0].vendor,
e[i][0].title,
fuckoff.toLowerCase().split(/\s+/)[0],
description,
unit,
UOM,
terpenes
]
);
if(productID.length === 0) {
productID = await return_key(
`select
ID
from
product
where
joined_brand = $1 and
joined_name = $2 and
type = $3`,
[brand, title, e[i][0].type]
);
//console.log(productID, `${url}/products/${e[i][0].handle}) //on empty db, useful to find products that are actually variants of something we inserted earlier. good job ocs dumbfucks
if(productID.length === 0) {
console.error("why can't I find the product???");
process.exit(1);
}
}
productID = productID[0][0];
let pics = new Set((await request({
url: `${url}/products/${e[i][0].handle}.json`,
headers: {'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36"}
}).then(JSON.parse)).product.images.map(a => a.src.split('?')[0]));
for(let pic of pics) {
let product_images = await return_key('select ID from product_image where product = $1 and source = $2', [productID, pic]);
if(product_images.length === 0) {
let response = (await exec('./image.sh ' + pic)).stdout.trim();
if(response !== '404') {
let ar = await cloudinary.uploader.upload(response, {public_id: response.slice(5), unique_filename: false});
let img_insert = await return_key(
`insert into product_image (
product,
source,
destination
) values (
$1,
$2,
$3
) on conflict do nothing
returning ID`,
[productID, pic, ar.secure_url]
);
if(img_insert.length === 0) {
console.error("how the FUCK", productID, pic, ar.secure_url);
} else if(img_insert.length > 1) {
console.error('kys');
}
}
} else if(product_images.length === 1) {
//console.log('already persisted :)');
//have a flag that once a month, we fetch images anyways just to see if there's a diff? thanks cloudinary :)
} else {
console.error('data integrity issue', product_images);
//product match, source match, but destinations are different, which should be unpossible
}
}
let quicky = (await request({
url: `${url}/products/${e[i][0].handle}?view=quickview`,
headers: {'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36"}
}).then(JSON.parse)).variants;
for(let j = 0; j < e[i].length; ++j) {
let fml = quicky.find(v => v.id == e[i][j].objectID);
let gram_equivalency;
if(fml !== undefined && fml.metafields !== undefined && fml.metafields.variant !== undefined && fml.metafields.variant.gram_equivalency !== undefined) {
gram_equivalency = fml.metafields.variant.gram_equivalency;
} else {
console.log(quicky);
console.log(fml);
console.log(fml.metafields);
console.log(fml.metafields.variant)
console.log(fml.metafields.variant.gram_equivalency);
}
let portions = null;
let quantity;
switch(e[i][j].type) {
case 'capsule':
quantity = e[i][j].meta.variant.retail_pack_number_of_items;
break;
case 'disposable':
case 'cartridge':
case 'flower':
case 'spray':
case 'oil':
if(e[i][j].meta.variant && e[i][j].meta.variant.retail_pack_net_content) {
quantity = e[i][j].meta.variant.retail_pack_net_content;
} else {
quantity = e[i][j].variant_title.match(qty)[3];
}
break;
case 'tea':
case 'gummy':
case 'mint':
case 'preroll':
case 'cookie':
case 'chocolate':
portions = e[i][j].meta.variant.retail_pack_number_of_items;
if(portions === 0) {
[, , portions, quantity] = e[i][j].variant_title.match(qty);
} else {
quantity = (new Big(e[i][j].meta.variant.retail_pack_net_content)).div(new Big(portions)).toString();
}
break;
}
if(portions !== null && portions < 2) {
//console.log(productID, e[i][j].type, portions, quantity);
portions = null;
}
let variantID = await return_key(
`insert into variant (
product,
portions,
quantity,
flavor,
gram_equivalency
) values (
$1, $2, $3, $4, $5
)
on conflict do nothing
returning ID`,
[productID, portions, quantity, e[i][j].options.flavour, gram_equivalency]
);
if(variantID.length === 0) {
let parameters = [productID, quantity];
variantID = await return_key(
`select
ID,
gram_equivalency
from
variant
where
product = $1 and
quantity = $2 and
portions ${portions === null ? 'is null' : '= $' + parameters.push(portions)} and
flavor ${e[i][j].options.flavour === undefined ? 'is null' : '= $' + parameters.push(e[i][j].options.flavour)}`,
parameters
);
if(gram_equivalency !== variantID[0][1]) {
console.log('warning: gram_equivalency does not match', gram_equivalency, variantID[0][1], productID, variantID[0][0], `${url}/products/${e[i][j].handle}?variant=${e[i][j].objectID}`);
}
if(variantID.length === 0) {
console.error("why can't I find the variant???");
process.exit(1);
}
}
variantID = variantID[0][0];
await postgres.query(
`insert into variant_identifier (
variant,
gtin
) values (
$1,
$2
) on conflict do nothing`,
[variantID, e[i][j].sku]
);
if(ok(minCBD, maxCBD, minTHC, maxTHC, e[i][j].price, e[i][j].inventory_quantity)) {
await postgres.query(
`insert into menu_item (
variant,
store,
CBD,
THC,
price,
stock,
path
) values (
$1,
$2,
$3,
$4,
$5,
$6,
$7
) on conflict do nothing`,
[
variantID,
storeID,
`[${minCBD},${maxCBD}]`,
`[${minTHC},${maxTHC}]`,
e[i][j].price,
e[i][j].inventory_quantity,
`/products/${e[i][j].handle}?variant=${e[i][j].objectID}`
]
);
} else {
console.error(`why don't you fix your fucking data ocs?? ${url}/products/${e[i][j].handle}?variant=${e[i][j].objectID}`);
}
}
cliProgressBar.update(++progress);
}
}
await Promise.all([
deal(),
deal(),
deal(),
deal(),
deal(),
deal()
])
await postgres.query('commit');
await postgres.end();
})();