Object.entries(require('./common.js')).forEach(([name, exported]) => global[name] = exported);
const parse = require('csv-parse/lib/sync');
const pc = /^([a-z]\d){3}$/i;
const apm = /^(\d{1,2}):(\d{2})\s?(([ap])\.?m\.?)?$/i;
async function alberta(browser, page) {
if(page === undefined) {
page = await browser.newPage();
}
page = await mnmalism(page);
await page.goto('https://aglc.ca/cannabis/retail-cannabis/cannabis-licensee-search');
let ab = (await page.$eval('#cannabis-results > div > table > tbody', e => e.innerText)).split('\n');
page.close();
let idx = [];
let names = [];
let address = [];
let city = [];
let postal_code = [];
for(let i = 0; i < ab.length; ++i) {
ab[i] = ab[i].split('\t');
idx.push(i);
names.push(ab[i][1] = sanitize_name(ab[i][1]));
address.push(ab[i][2]);
postal_code.push(ab[i][3]);
city.push(ab[i][0]);
}
await postgres.query(
`create temporary table alberta as
select *
from unnest (
$1::smallint[],
$2::citext[],
$3::citext[],
$4::citext[],
$5::citext[]
) as t(idx, joined_name, address, city, postal_code)`,
[idx, names, address, city, postal_code]
);
idx = (await postgres.query(`
select idx
from alberta
where not exists (
select
from store_temp
where
joined_name = alberta.joined_name and
address = alberta.address and
address2 = '' and
city = alberta.city and
region = 'alberta' and
country = 'canada' and
postal_code = alberta.postal_code
)
`)).rows.map(x => x.idx);
names = [];
address = [];
let address2 = [];
city = [];
let region = [];
let country = [];
postal_code = [];
let longitude = [];
let latitude = [];
let osrm_hint_foot = [];
let osrm_hint_car = [];
let timezone = [];
let phone = [];
for(let i = 0; i < idx.length; ++i) {
let t = ab[idx[i]];
let r;
try {
r = await g(`${t[2]} ${t[0]} alberta ${t[3]} canada`);
} catch(e) {
console.error(e);
console.log(t);
continue;
}
if(!pc.test(r[0].postal_code)) {
t[3] = t[3].replace(/\s+/g, '');
if(r[0].postal_code.length === 3 && t[3].length === 6) {
r[0].postal_code = t[3];
} else {
console.error('postal_code goofup', r, t);
continue;
}
}
names.push(t[1]);
address.push(r[0].address);
address2.push(r[0].address2);
city.push(r[0].city);
region.push(r[0].region);
country.push(r[0].country);
postal_code.push(r[0].postal_code);
longitude.push(r[1].lng);
latitude.push(r[1].lat);
osrm_hint_foot.push(r[3]);
osrm_hint_car.push(r[4]);
timezone.push(r[2]);
phone.push(t[4]);
}try{
console.log((await postgres.query(`insert into store (
joined_name,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
phone,
partner,
delivery,
pickup,
prepayment,
name
) select i.*, false, false, false, false, i.joined_name from unnest (
$1::citext[],
$2::citext[],
$3::citext[],
$4::citext[],
$5::citext[],
$6::citext[],
$7::citext[],
$8::decimal[],
$9::decimal[],
$10::text[],
$11::text[],
$12::text[],
$13::packed_phone_number[]
) as i(
joined_name,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
phone
) where not exists (select 1 from store_history where
store_history.joined_name = i.joined_name and
store_history.address = i.address and
store_history.address2 is not distinct from i.address2 and
store_history.city = i.city and
store_history.region = i.region and
store_history.country = i.country and
store_history.postal_code = i.postal_code
) or exists (select 1 from store where
store.joined_name = i.joined_name and
store.address = i.address and
store.address2 is not distinct from i.address2 and
store.city = i.city and
store.region = i.region and
store.country = i.country and
store.postal_code = i.postal_code
) on conflict (joined_name, address, address2, city, region, country, postal_code)
where address is not null and address2 is not null and city is not null and postal_code is not null
do update set longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
where store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
names,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
phone
])).rowCount, 'alberta');} catch(e) {console.log(e); console.log('alberta failed')}
}
async function manitoba(browser, page) {
if(page === undefined) {
page = await browser.newPage();
}
page = await mnmalism(page);
await page.goto('https://lgcamb.ca/cannabis/store-list/');
let results = await page.$$eval('.e2272-10.x-text ul', x => {
for(let i = x.length - 1; i >= 0; --i) {
x[i] = x[i].textContent.trim();
if(x[i].length === 0) {
x.splice(i, 1);
} else {
x[i] = x[i].split('\n');
for(let j = 0; j < x[i].length; ++j) {
let idx = x[i][j].indexOf(",");
x[i][j] = [x[i][j].substring(0, idx).trim(), x[i][j].substring(idx + 1).trim()];
}
}
}
return x;
});
let idk = await page.$$eval('.e2272-10.x-text strong', e => e.map(x => x.textContent.replace(':', '').trim()));
page.close();
if(results.length === idk.length) {
let idx = [];
let jdx = [];
let names = [];
let address = [];
let city = [];
for(let i = 0; i < idk.length; ++i) {
for(let j = 0; j < results[i].length; ++j) {
idx.push(i);
jdx.push(j);
names.push(results[i][j][0] = sanitize_name(results[i][j][0]));
address.push(results[i][j][1]);
city.push(idk[i]);
}
}
await postgres.query(
`create temporary table manitoba as
select *
from unnest (
$1::smallint[],
$2::smallint[],
$3::citext[],
$4::citext[],
$5::citext[]
) as t(idx, jdx, joined_name, address, city)`,
[idx, jdx, names, address, city]
);
let temp = (await postgres.query(`
select idx, jdx
from manitoba
where not exists (
select
from store_temp
where
joined_name = manitoba.joined_name and
address = manitoba.address and
address2 = '' and
city = manitoba.city and
region = 'manitoba' and
country = 'canada' and
postal_code = ''
)
`)).rows;
idx = temp.map(x => x.idx);
jdx = temp.map(x => x.jdx);
names = [];
address = [];
let address2 = [];
city = [];
let region = [];
let country = [];
let postal_code = [];
let longitude = [];
let latitude = [];
let osrm_hint_foot = [];
let osrm_hint_car = [];
let timezone = [];
for(let k = 0; k < idx.length; ++k) {
const i = idx[k];
const j = jdx[k];
if(results[i][j][1] === 'Otineka Mall' && results[i][j][0] === 'Meta') {
names.push(results[i][j][0]);
address.push('Otineka Mall Highway 10 North'); address2.push('Unit 128');
city.push('Opaskwayak');
region.push('Manitoba');
country.push('Canada');
postal_code.push('R0B2J0');
longitude.push('-101.261585');
latitude.push('53.834096');
osrm_hint_foot.push(await request(`http://127.0.0.1:5000/nearest/v1/fuck/-101.261585,53.834096`).then(x => JSON.parse(x).waypoints[0].hint));
osrm_hint_car.push(await request(`http://127.0.0.1:5001/nearest/v1/fuck/-101.261585,53.834096`).then(x => JSON.parse(x).waypoints[0].hint));
timezone.push('America/Winnipeg');
} else {
let r;
try {
r = await g(`${results[i][j][1]} ${idk[i]} manitoba canada`);
} catch(e) {
console.error(e);
console.log(results[i][j]);
continue;
}
if(!pc.test(r[0].postal_code)) {
if(results[i][j][1] === '300A North Railway Street') {
r[0].postal_code = 'R6M1S7';
} else {
console.error('postal_code goofup', r, results[i][j], idk[i]);
continue;
}
}
names.push(results[i][j][0]);
address.push(r[0].address);
address2.push(results[i][j][1] === '300A North Railway Street' ? 'A' : r[0].address2);
city.push(r[0].city);
region.push(r[0].region);
country.push(r[0].country);
postal_code.push(r[0].postal_code);
longitude.push(r[1].lng);
latitude.push(r[1].lat);
osrm_hint_foot.push(r[3]);
osrm_hint_car.push(r[4]);
timezone.push(r[2]);
}
}try{
console.log((await postgres.query(`insert into store (
joined_name,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
partner,
delivery,
pickup,
prepayment,
name
) select i.*, false, false, false, false, i.joined_name from unnest (
$1::citext[],
$2::citext[],
$3::citext[],
$4::citext[],
$5::citext[],
$6::citext[],
$7::citext[],
$8::decimal[],
$9::decimal[],
$10::text[],
$11::text[],
$12::text[]
) as i(
joined_name,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone
) where not exists (select 1 from store_history where
store_history.joined_name = i.joined_name and
store_history.address = i.address and
store_history.address2 is not distinct from i.address2 and
store_history.city = i.city and
store_history.region = i.region and
store_history.country = i.country and
store_history.postal_code = i.postal_code
) or exists (select 1 from store where
store.joined_name = i.joined_name and
store.address = i.address and
store.address2 is not distinct from i.address2 and
store.city = i.city and
store.region = i.region and
store.country = i.country and
store.postal_code = i.postal_code
) on conflict (joined_name, address, address2, city, region, country, postal_code)
where address is not null and address2 is not null and city is not null and postal_code is not null
do update set longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
where store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
names,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone
])).rowCount, 'manitoba');} catch(e) {console.log(e); console.log('manitoba failed')}
} else {
console.error("https://lgcamb.ca/cannabis/store-list/ city mismatch")
}
}
async function saskatchewan(browser, page) {
if(page === undefined) {
page = await browser.newPage();
}
page = await mnmalism(page);
await page.goto('https://www.slga.com/permits-and-licences/cannabis-permits/cannabis-retailing/cannabis-retailers-in-saskatchewan');
let results = await page.$$eval('#content h1 + p', e => e.map(x => {
let t = x.textContent.split('\n');
if(t.length === 1) {
while((x = x.nextElementSibling) && x.textContent.trim().toLowerCase() !== 'go to top') {
t.push(x.textContent.trim());
}
}
return t;
}));
page.close();
let idx = [];
let names = [];
let address = [];
let city = [];
for(let i = 0; i < results.length; ++i) {
idx.push(i);
names.push(results[i][0] = sanitize_name(results[i][0]));
address.push(results[i][1]);
city.push(results[i][2]);
}
await postgres.query(
`create temporary table saskatchewan as
select *
from unnest (
$1::smallint[],
$2::citext[],
$3::citext[],
$4::citext[]
) as t(idx, joined_name, address, city)`,
[idx, names, address, city]
);
idx = (await postgres.query(`
select idx
from saskatchewan
where not exists (
select
from store_temp
where
joined_name = saskatchewan.joined_name and
address = saskatchewan.address and
address2 = '' and
city = saskatchewan.city and
region = 'saskatchewan' and
country = 'canada' and
postal_code = ''
)
`)).rows.map(x => x.idx);
names = [];
address = [];
let address2 = [];
city = [];
let region = [];
let country = [];
let postal_code = [];
let longitude = [];
let latitude = [];
let osrm_hint_foot = [];
let osrm_hint_car = [];
let timezone = [];
let url = [];
for(let j = 0; j < idx.length; ++j) {
const i = idx[j];
if(results[i][1] === '82B Battlefords Crossing') {
names.push('Fire & Flower');
address.push('Battleford Crossing'); address2.push('82B');
city.push('Battleford');
region.push('Saskatchewan');
country.push('Canada');
postal_code.push('S0M0E0');
longitude.push('-108.3195177'); latitude.push('52.7406683');
osrm_hint_foot.push(await request(`http://127.0.0.1:5000/nearest/v1/fuck/-108.3195177,52.7406683`).then(x => JSON.parse(x).waypoints[0].hint));
osrm_hint_car.push(await request(`http://127.0.0.1:5001/nearest/v1/fuck/-108.3195177,52.7406683`).then(x => JSON.parse(x).waypoints[0].hint));
timezone.push('America/Regina');
url.push(results[i][3]);
} else if(results[i][0] === 'Vatic') {
names.push('Vatic');
address.push('Building # 1 – Unit #15 SE 6-18-18-Parcel B Plan #101924726'); address2.push('');
city.push('Edenwold No. 158');
region.push('Saskatchewan');
country.push('Canada');
postal_code.push('S0G3Z0');
longitude.push('-104.459866');
latitude.push('50.488856');
osrm_hint_foot.push(await request(`http://127.0.0.1:5000/nearest/v1/fuck/-104.459866,50.488856`).then(x => JSON.parse(x).waypoints[0].hint));
osrm_hint_car.push(await request(`http://127.0.0.1:5001/nearest/v1/fuck/-104.459866,50.488856`).then(x => JSON.parse(x).waypoints[0].hint));
timezone.push('America/Regina');
url.push(results[i][3]);
} else {
results[i][2] = results[i][2].replace(/\bsk\b/i, 'saskatchewan');
if(results[i][2].indexOf('saskatchewan') === -1) {
results[i][2] += 'saskatchewan';
}
let r;
try {
r = await g(`${results[i][1]} ${results[i][2]} canada`);
} catch(e) {
console.error(e);
console.log(results[i]);
continue;
}
if(!pc.test(r[0].postal_code)) {
console.error('postal_code goofup', r, results[i]);
continue;
}
names.push(results[i][0]);
address.push(r[0].address);
address2.push(r[0].address2);
city.push(r[0].city);
region.push(r[0].region);
country.push(r[0].country);
postal_code.push(r[0].postal_code);
longitude.push(r[1].lng);
latitude.push(r[1].lat);
osrm_hint_foot.push(r[3]);
osrm_hint_car.push(r[4]);
timezone.push(r[2]);
url.push(results[i][3]);
}
}try {
console.log((await postgres.query(`insert into store (
joined_name,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
url,
partner,
delivery,
pickup,
prepayment,
name
) select i.*, false, false, false, false, i.joined_name from unnest (
$1::citext[],
$2::citext[],
$3::citext[],
$4::citext[],
$5::citext[],
$6::citext[],
$7::citext[],
$8::decimal[],
$9::decimal[],
$10::text[],
$11::text[],
$12::text[],
$13::text[]
) as i(
joined_name,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
url
) where not exists (select 1 from store_history where
store_history.joined_name = i.joined_name and
store_history.address = i.address and
store_history.address2 is not distinct from i.address2 and
store_history.city = i.city and
store_history.region = i.region and
store_history.country = i.country and
store_history.postal_code = i.postal_code
) or exists (select 1 from store where
store.joined_name = i.joined_name and
store.address = i.address and
store.address2 is not distinct from i.address2 and
store.city = i.city and
store.region = i.region and
store.country = i.country and
store.postal_code = i.postal_code
) on conflict (joined_name, address, address2, city, region, country, postal_code)
where address is not null and address2 is not null and city is not null and postal_code is not null
do update set longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
where store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
names,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
url
])).rowCount, 'saskatchewan');} catch(e) {console.log(e); console.log('saskatchewan failed')}
}
async function newbrunswick(browser, page) {
if(page === undefined) {
page = await browser.newPage();
}
page = await mnmalism(page);
await page.goto('https://www.cannabis-nb.com/stores/');
let results = await page.$$eval('.visible-lg tbody > tr', e => e.map(x => Array.from(x.querySelectorAll('td')).map((y, i) => {
if(i === 2) {
return y.textContent.trim().split('\n').map(yy => {
let temp = yy.trim().replace(/-|,/g, ' ').split(/\s+/);
let d = 0;
switch(temp[0]) {
case 'Tue': d = 1440; break;
case 'Wed': d = 2880; break;
case 'Thu': d = 4320; break;
case 'Fri': d = 5760; break;
case 'Sat': d = 7200; break;
case 'Sun': d = 8640; break;
}
let out = [];
for(let j = 2; j < temp.length; j += 2) {
let beg = temp[j - 1].split(':').map(Number);
beg = beg[0] * 60 + beg[1];
let end = temp[j].split(':').map(Number);
end = end[0] * 60 + end[1];
let rtn = [d + beg, d + end];
if(end < beg) {
if(d === 8640) {
out.push([rtn[0], 10080]);
rtn[0] = 0;
rtn[1] -= 8640;
} else {
rtn[1] += 1440;
}
}
out.push(rtn);
}
return out;
});
} else {
return y.textContent.trim();
}
})));
page.close();
let idx = [];
let address = [];
for(let i = 0; i < results.length; ++i) {
idx.push(i);
address.push(results[i][1]);
}
await postgres.query(
`create temporary table newbrunswick as
select *
from unnest (
$1::smallint[],
$2::citext[]
) as t(idx, address)`,
[idx, address]
);
idx = (await postgres.query(`
select idx
from newbrunswick
where not exists (
select
from store_temp
where
joined_name = '' and
address = newbrunswick.address and
address2 = '' and
city = '' and
region = 'newbrunswick' and
country = 'canada' and
postal_code = ''
)
`)).rows.map(x => x.idx);
address = [];
let address2 = [];
let city = [];
let region = [];
let country = [];
let postal_code = [];
let longitude = [];
let latitude = [];
let osrm_hint_foot = [];
let osrm_hint_car = [];
let timezone = [];
let open = [];
for(let j = 0; j < idx.length; ++j) {
const i = idx[j];
if(results[i][1] === '16 Allee De La Cooperative, Richibucto, NB E4W 5V8, Canada') {
address.push('16 Cooperative Street');
address2.push('');
city.push('Richibucto');
country.push('Canada');
postal_code.push('E4W3W7');
longitude.push('-64.8737465'); latitude.push('46.6731998');
osrm_hint_foot.push(await request(`http://127.0.0.1:5000/nearest/v1/fuck/-64.8737465,46.6731998`).then(x => JSON.parse(x).waypoints[0].hint));
osrm_hint_car.push(await request(`http://127.0.0.1:5001/nearest/v1/fuck/-64.8737465,46.6731998`).then(x => JSON.parse(x).waypoints[0].hint));
timezone.push('America/Halifax');
} else if(results[i][1] === '20 F. Tribe Road, Perth-Andover, NB E7H 3R6, Canada') {
address.push('20 F. Tribe Road');
address2.push('');
city.push('Perth-Andover');
country.push('Canada');
postal_code.push('E7H3R6');
longitude.push('-67.713059'); latitude.push('46.745906');
osrm_hint_foot.push(await request(`http://127.0.0.1:5000/nearest/v1/fuck/-67.713059,46.745906`).then(x => JSON.parse(x).waypoints[0].hint));
osrm_hint_car.push(await request(`http://127.0.0.1:5001/nearest/v1/fuck/-67.713059,46.745906`).then(x => JSON.parse(x).waypoints[0].hint));
timezone.push('America/Halifax');
} else {
let r;
try {
r = await g(results[i][1]);
} catch(e) {
console.error(e);
console.log(results[i][1]);
continue;
}
if(!pc.test(r[0].postal_code)) {
console.error('postal_code goofup', r, results[i][1]);
continue;
}
address.push(r[0].address);
address2.push(r[0].address2);
city.push(r[0].city);
country.push(r[0].country);
postal_code.push(r[0].postal_code);
longitude.push(r[1].lng);
latitude.push(r[1].lat);
osrm_hint_foot.push(r[3]);
osrm_hint_car.push(r[4]);
timezone.push(r[2]);
}
region.push('New Brunswick');
open.push(JSON.stringify(results[i][2].flat()));
} try {
console.log((await postgres.query(`insert into store (
joined_name,
name,
url,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
open,
partner,
delivery,
pickup,
prepayment
) select
'Cannabis NB',
'Cannabis NB',
'cannabis-nb.com',
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
(select range_agg(int4range((x->>0)::int, (x->>1)::int), true, true) from json_array_elements(open) as t(x)),
false,
false,
false,
false
from unnest (
$1::citext[],
$2::citext[],
$3::citext[],
$4::citext[],
$5::citext[],
$6::citext[],
$7::decimal[],
$8::decimal[],
$9::text[],
$10::text[],
$11::text[],
$12::json[]
) as i(
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
open
) where not exists (select 1 from store_history where
store_history.joined_name = 'Cannabis NB' and
store_history.address = i.address and
store_history.address2 is not distinct from i.address2 and
store_history.city = i.city and
store_history.region = i.region and
store_history.country = i.country and
store_history.postal_code = i.postal_code
) or exists (select 1 from store where
store.joined_name = 'Cannabis NB' and
store.address = i.address and
store.address2 is not distinct from i.address2 and
store.city = i.city and
store.region = i.region and
store.country = i.country and
store.postal_code = i.postal_code
) on conflict (joined_name, address, address2, city, region, country, postal_code)
where address is not null and address2 is not null and city is not null and postal_code is not null
do update set open = excluded.open, longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
where store.open is distinct from excluded.open and store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
address, address2, city, region, country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
open
])).rowCount, 'newbrunswick');} catch(e) {console.log(e); console.log('newbrunswick failed')}
}
async function britishcolumbia() {
let bc = await request('https://justice.gov.bc.ca/cannabislicensing/api/establishments/map-json').then(JSON.parse);
let idx = [];
let names = [];
let address = [];
let city = [];
let postal_code = [];
for(let i = 0; i < bc.length; ++i) {
if(bc[i].Status === 'Open') {
idx.push(i);
names.push(bc[i].Name = sanitize_name(bc[i].Name));
address.push(bc[i].Address);
city.push(bc[i].City);
postal_code.push(bc[i].Postal);
}
}
await postgres.query(
`create temporary table britishcolumbia as
select *
from unnest (
$1::smallint[],
$2::citext[],
$3::citext[],
$4::citext[],
$5::citext[]
) as t(idx, joined_name, address, city, postal_code)`,
[idx, names, address, city, postal_code]
);
idx = (await postgres.query(`
select idx
from britishcolumbia
where not exists (
select
from store_temp
where
joined_name = britishcolumbia.joined_name and
address = britishcolumbia.address and
address2 = '' and
city = britishcolumbia.city and
region = 'britishcolumbia' and
country = 'canada' and
postal_code = britishcolumbia.postal_code
)
`)).rows.map(x => x.idx);
names = [];
address = [];
let address2 = [];
city = [];
let region = [];
let country = [];
postal_code = [];
let longitude = [];
let latitude = [];
let osrm_hint_foot = [];
let osrm_hint_car = [];
let timezone = [];
let phone = [];
for(let j = 0; j < idx.length; ++j) {
const i = idx[j];
let r;
try {
r = await g(`${bc[i].Address} ${bc[i].City} british columbia ${bc[i].Postal} canada`);
} catch(e) {
console.error(e);
console.log(bc[i]);
continue;
}
if(!pc.test(r[0].postal_code)) {
bc[i].Postal = bc[i].Postal.replace(/\s+/g, '');
if(r[0].postal_code.length === 3 && bc[i].Postal.length === 6) {
r[0].postal_code = bc[i].Postal
} else {
console.error('postal_code goofup', r, bc[i]);
continue;
}
}
names.push(bc[i].Name);
address.push(r[0].address);
address2.push(r[0].address2);
city.push(r[0].city);
region.push(r[0].region);
country.push(r[0].country);
postal_code.push(r[0].postal_code);
longitude.push(r[1].lng);
latitude.push(r[1].lat);
osrm_hint_foot.push(r[3]);
osrm_hint_car.push(r[4]);
timezone.push(r[2]);
phone.push(bc[i].Phone);
} try{
console.log((await postgres.query(`insert into store (
joined_name,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
phone,
partner,
delivery,
pickup,
prepayment,
name
) select i.*, false, false, false, false, i.joined_name from unnest (
$1::citext[],
$2::citext[],
$3::citext[],
$4::citext[],
$5::citext[],
$6::citext[],
$7::citext[],
$8::decimal[],
$9::decimal[],
$10::text[],
$11::text[],
$12::text[],
$13::packed_phone_number[]
) as i(
joined_name,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
phone
) where not exists (select 1 from store_history where
store_history.joined_name = i.joined_name and
store_history.address = i.address and
store_history.address2 is not distinct from i.address2 and
store_history.city = i.city and
store_history.region = i.region and
store_history.country = i.country and
store_history.postal_code = i.postal_code
) or exists (select 1 from store where
store.joined_name = i.joined_name and
store.address = i.address and
store.address2 is not distinct from i.address2 and
store.city = i.city and
store.region = i.region and
store.country = i.country and
store.postal_code = i.postal_code
) on conflict (joined_name, address, address2, city, region, country, postal_code)
where address is not null and address2 is not null and city is not null and postal_code is not null
do update set longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
where store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
names,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
phone
])).rowCount, 'bc');} catch(e) {console.log(e); console.log('bc failed')}
}
async function newfoundland() {
let nl = (await request('https://stores.boldapps.net/front-end/get_surrounding_stores.php?shop=nlc-production.myshopify.com&latitude=50&longitude=-59&limit=0').then(JSON.parse)).stores;
let idx = [];
let names = [];
let address = [];
let address2 = [];
let city = [];
let postal_code = [];
for(let i = 0; i < nl.length; ++i) {
idx.push(i);
names.push(nl[i].name = sanitize_name(nl[i].name));
address.push(nl[i].address);
address2.push(nl[i].address2);
city.push(nl[i].city);
postal_code.push(nl[i].postal_zip);
}
await postgres.query(
`create temporary table newfoundland as
select *
from unnest (
$1::smallint[],
$2::citext[],
$3::citext[],
$4::citext[],
$5::citext[],
$6::citext[]
) as t(idx, joined_name, address, address2, city, postal_code)`,
[idx, names, address, address2, city, postal_code]
);
idx = (await postgres.query(`
select idx
from newfoundland
where not exists (
select
from store_temp
where
joined_name = newfoundland.joined_name and
address = newfoundland.address and
address2 = newfoundland.address2 and
city = newfoundland.city and
region = 'newfoundland' and
country = 'canada' and
postal_code = newfoundland.postal_code
)
`)).rows.map(x => x.idx);
names = [];
address = [];
address2 = [];
city = [];
let region = [];
let country = [];
postal_code = [];
let longitude = [];
let latitude = [];
let osrm_hint_foot = [];
let osrm_hint_car = [];
let timezone = [];
let phone = [];
let extension = [];
for(let j = 0; j < idx.length; ++j) {
const i = idx[j];
let r;
try {
r = await g(`${nl[i].address} ${nl[i].address2} ${nl[i].city} newfoundland ${nl[i].postal_zip} canada`);
} catch(e) {
console.error(e);
console.log(nl[i]);
continue;
}
if(!pc.test(r[0].postal_code)) {
r[0].postal_code = nl[i].postal_zip.replace(/\s+/, '');
}
names.push(nl[i].name);
address.push(r[0].address);
address2.push(r[0].address2);
city.push(r[0].city);
region.push(r[0].region);
country.push(r[0].country);
postal_code.push(r[0].postal_code);
longitude.push(r[1].lng);
latitude.push(r[1].lat);
osrm_hint_foot.push(r[3]);
osrm_hint_car.push(r[4]);
timezone.push(r[2]);
phone.push(nl[i].phone.trim().replace(/(?<!^)\(.+\)/g, '').trim());
extension.push(nl[i].phone.match(/ext (.+)\)/)?.[1]?.trim());
} try {
console.log((await postgres.query(`insert into store (
joined_name,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
phone,
extension,
partner,
delivery,
pickup,
prepayment,
name
) select i.*, false, false, false, false, i.joined_name from unnest (
$1::citext[],
$2::citext[],
$3::citext[],
$4::citext[],
$5::citext[],
$6::citext[],
$7::citext[],
$8::decimal[],
$9::decimal[],
$10::text[],
$11::text[],
$12::text[],
$13::packed_phone_number[],
$14::citext[]
) as i(
joined_name,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
phone,
extension
) where not exists (select 1 from store_history where
store_history.joined_name = i.joined_name and
store_history.address = i.address and
store_history.address2 is not distinct from i.address2 and
store_history.city = i.city and
store_history.region = i.region and
store_history.country = i.country and
store_history.postal_code = i.postal_code
) or exists (select 1 from store where
store.joined_name = i.joined_name and
store.address = i.address and
store.address2 is not distinct from i.address2 and
store.city = i.city and
store.region = i.region and
store.country = i.country and
store.postal_code = i.postal_code
) on conflict (joined_name, address, address2, city, region, country, postal_code)
where address is not null and address2 is not null and city is not null and postal_code is not null
do update set longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
where store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
names,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
phone,
extension
])).rowCount, 'newfoundland'); } catch(e) {console.log(e); console.log('newfoundland failed')}
}
async function novascotia() {
let x = await request('https://www.mynslc.com/skins/mynslc/scripts/locations.json').then(JSON.parse);
let idx = [];
let address = [];
let address2 = [];
let city = [];
let postal_code = [];
for(let i = 0; i < x.length; ++i) {
for(let j = 0; j < x[i].features.length; ++j) {
if(x[i].features[j].featureId === 'CP') {
idx.push(i);
address.push(x[i].addressInfo.address1);
address2.push(x[i].addressInfo.address2 ?? '');
city.push(x[i].addressInfo.city);
postal_code.push(x[i].addressInfo.postal);
break;
}
}
}
await postgres.query(
`create temporary table novascotia as
select *
from unnest (
$1::smallint[],
$2::citext[],
$3::citext[],
$4::citext[],
$5::citext[]
) as t(idx, address, address2, city, postal_code)`,
[idx, address, address2, city, postal_code]
);
idx = (await postgres.query(`
select idx
from novascotia
where not exists (
select
from store_temp
where
joined_name = '' and
address = novascotia.address and
address2 = novascotia.address2 and
city = novascotia.city and
region = 'novascotia' and
country = 'canada' and
postal_code = novascotia.postal_code
)
`)).rows.map(x => x.idx);
address = [];
address2 = [];
city = [];
let region = [];
let country = [];
postal_code = [];
let longitude = [];
let latitude = [];
let osrm_hint_foot = [];
let osrm_hint_car = [];
let timezone = [];
let open = [];
let phone = [];
for(let j = 0; j < idx.length; ++j) {
const i = idx[j];
let r;
try {
r = await g(`${x[i].addressInfo.address1} ${x[i].addressInfo.address2 ?? ''} ${x[i].addressInfo.city} nova scotia ${x[i].addressInfo.postal} canada`);
} catch(e) {
console.error(e);
console.log(x[i].name);
continue;
}
if(!pc.test(r[0].postal_code)) {
x[i].postal = x[i].postal.replace(/\s+/g, '');
if(r[0].postal_code.length === 3 && x[i].postal.length === 6) {
r[0].postal_code = x[i].postal
} else {
console.error('postal_code goofup', r, x[i]);
continue;
}
}
address.push(r[0].address);
address2.push(r[0].address2);
city.push(r[0].city);
region.push(r[0].region);
country.push(r[0].country);
postal_code.push(r[0].postal_code);
longitude.push(r[1].lng);
latitude.push(r[1].lat);
osrm_hint_foot.push(r[3]);
osrm_hint_car.push(r[4]);
timezone.push(r[2]);
phone.push(x[i].phone);
let tim = [];
for(let k = 0; k < x[i].openingHours.length; ++k) {
let d = 0;
switch(x[i].openingHours[k].dayOfWeek) {
case 'Tuesday': d = 1440; break;
case 'Wednesday': d = 2880; break;
case 'Thursday': d = 4320; break;
case 'Friday': d = 5760; break;
case 'Saturday': d = 7200; break;
case 'Sunday': d = 8640; break;
}
let beg = x[i].openingHours[k].opens.split(':').map(Number);
beg = beg[0] * 60 + beg[1];
let end = x[i].openingHours[k].closes.split(':').map(Number);
end = end[0] * 60 + end[1];
let rtn = [d + beg, d + end];
if(end < beg) {
if(d === 8640) {
tim.push([rtn[0], 10080]);
rtn[0] = 0;
rtn[1] -= 8640;
} else {
rtn[1] += 1440;
}
}
tim.push(rtn);
}
open.push(JSON.stringify(tim));
} try {
console.log((await postgres.query(`insert into store (
joined_name,
name,
url,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
open,
phone,
partner,
delivery,
pickup,
prepayment
) select
'NSLC',
'NSLC',
'cannabis.mynslc.com',
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
(select range_agg(int4range((x->>0)::int, (x->>1)::int), true, true) from json_array_elements(open) as t(x)),
phone,
false,
false,
false,
false
from unnest (
$1::citext[],
$2::citext[],
$3::citext[],
$4::citext[],
$5::citext[],
$6::citext[],
$7::decimal[],
$8::decimal[],
$9::text[],
$10::text[],
$11::text[],
$12::json[],
$13::packed_phone_number[]
) as i(
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
open,
phone
) where not exists (select 1 from store_history where
store_history.joined_name = 'NSLC' and
store_history.address = i.address and
store_history.address2 is not distinct from i.address2 and
store_history.city = i.city and
store_history.region = i.region and
store_history.country = i.country and
store_history.postal_code = i.postal_code
) or exists (select 1 from store where
store.joined_name = 'NSLC' and
store.address = i.address and
store.address2 is not distinct from i.address2 and
store.city = i.city and
store.region = i.region and
store.country = i.country and
store.postal_code = i.postal_code
) on conflict (joined_name, address, address2, city, region, country, postal_code)
where address is not null and address2 is not null and city is not null and postal_code is not null
do update set open = excluded.open, longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
where store.open is distinct from excluded.open and store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
open,
phone
])).rowCount, 'nova scotia');} catch(e) {console.log(e); console.log('novascotia failed')}
}
async function pei(browser, page) {
if(page === undefined) {
page = await browser.newPage();
}
page = await mnmalism(page);
await page.goto('https://peicannabiscorp.com/pages/contact');
let results = await page.$$eval('.location', e => e.map(x => x.innerText.trim().split('\n')));
page.close();
let idx = [];
let address = [];
const dayOfWeek = /^(Mon|Tue|Wed|Thurs|Fri|Sat|Sun)/;
for(let i = 0; i < results.length; ++i) {
let adr = '';
let phone;
let first = false;
let second = true;
let tim = [];
for(let j = 0; j < results[i].length; ++j) {
if(results[i][j]) {
if(first) {
if(second && results[i][j].startsWith('Phone:')) {
phone = results[i][j].replace('Phone:', '');
second = false;
} else if(!second && dayOfWeek.test(results[i][j])) {
let idx = results[i][j].indexOf(":");
let days = results[i][j].substring(0, idx).split('-');
let hours = results[i][j].substring(idx + 1).split('-');
let beg = hours[0].trim().match(apm);
beg = (Number(beg[1]) % 12 + (beg[4]?.toLowerCase() === 'p' ? 12 : 0)) * 60 + Number(beg[2]);
let end = hours[1].trim().match(apm);
end = (Number(end[1]) % 12 + (end[4]?.toLowerCase() === 'p' ? 12 : 0)) * 60 + Number(end[2]);
switch(days[0]) {
case 'Mon': days[0] = 0; break;
case 'Tue': days[0] = 1; break;
case 'Wed': days[0] = 2; break;
case 'Thurs': days[0] = 3; break;
case 'Fri': days[0] = 4; break;
case 'Sat': days[0] = 5; break;
case 'Sun': days[0] = 6; break;
}
if(days[1]) {
switch(days[1]) {
case 'Mon': days[1] = 0; break;
case 'Tue': days[1] = 1; break;
case 'Wed': days[1] = 2; break;
case 'Thurs': days[1] = 3; break;
case 'Fri': days[1] = 4; break;
case 'Sat': days[1] = 5; break;
case 'Sun': days[1] = 6; break;
}
if(days[1] < days[0]) {
days[1] += 7;
}
for(let k = days[0]; k <= days[1]; ++k) {
let d = k % 7 * 1440;
let rtn = [d + beg, d + end];
if(end < beg) {
if(d === 8640) {
tim.push([rtn[0], 10080]);
rtn[0] = 0;
rtn[1] -= 8640;
} else {
rtn[1] += 1440;
}
}
tim.push(rtn);
}
} else {
let d = days[0] * 1440;
let rtn = [d + beg, d + end];
if(end < beg) {
if(d === 8640) {
tim.push([rtn[0], 10080]);
rtn[0] = 0;
rtn[1] -= 8640;
} else {
rtn[1] += 1440;
}
}
tim.push(rtn);
}
} else if(results[i][j] === 'Store Hours') {
second = false;
} else if(second) {
adr += results[i][j];
}
} else {
first = true;
}
}
}
results[i] = {adr, tim, phone};
idx.push(i);
address.push(adr);
}
await postgres.query(
`create temporary table pei as
select *
from unnest (
$1::smallint[],
$2::citext[]
) as t(idx, address)`,
[idx, address]
);
idx = (await postgres.query(`
select idx
from pei
where not exists (
select
from store_temp
where
joined_name = '' and
address = pei.address and
address2 = '' and
city = '' and
region = 'pei' and
country = 'canada' and
postal_code = ''
)
`)).rows.map(x => x.idx);
address = [];
let address2 = [];
let city = [];
let region = [];
let country = [];
let postal_code = [];
let longitude = [];
let latitude = [];
let osrm_hint_foot = [];
let osrm_hint_car = [];
let timezone = [];
let open = [];
let phone = [];
for(let j = 0; j < idx.length; ++j) {
const i = idx[j];
try {
r = await g(`${results[i].adr} canada`);
} catch(e) {
console.error(e);
console.log(results[i].adr);
continue;
}
if(!pc.test(r[0].postal_code)) {
console.error('postal_code goofup', r, results[i].adr);
continue;
}
address.push(r[0].address);
address2.push(r[0].address2);
city.push(r[0].city);
region.push(r[0].region);
country.push(r[0].country);
postal_code.push(r[0].postal_code);
longitude.push(r[1].lng);
latitude.push(r[1].lat);
osrm_hint_foot.push(r[3]);
osrm_hint_car.push(r[4]);
timezone.push(r[2]);
open.push(JSON.stringify(results[i].tim));
phone.push(results[i].phone);
} try {
console.log((await postgres.query(`insert into store (
joined_name,
name,
url,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
open,
phone,
partner,
delivery,
pickup,
prepayment
) select
'PEI',
'PEI',
'peicannabiscorp.com',
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
(select range_agg(int4range((x->>0)::int, (x->>1)::int), true, true) from json_array_elements(open) as t(x)),
phone,
false,
false,
false,
false
from unnest (
$1::citext[],
$2::citext[],
$3::citext[],
$4::citext[],
$5::citext[],
$6::citext[],
$7::decimal[],
$8::decimal[],
$9::text[],
$10::text[],
$11::text[],
$12::json[],
$13::packed_phone_number[]
) as i(
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
open,
phone
) where not exists (select 1 from store_history where
store_history.joined_name = 'PEI' and
store_history.address = i.address and
store_history.address2 is not distinct from i.address2 and
store_history.city = i.city and
store_history.region = i.region and
store_history.country = i.country and
store_history.postal_code = i.postal_code
) or exists (select 1 from store where
store.joined_name = 'PEI' and
store.address = i.address and
store.address2 is not distinct from i.address2 and
store.city = i.city and
store.region = i.region and
store.country = i.country and
store.postal_code = i.postal_code
) on conflict (joined_name, address, address2, city, region, country, postal_code)
where address is not null and address2 is not null and city is not null and postal_code is not null
do update set open = excluded.open, longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
where store.open is distinct from excluded.open and store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
open,
phone
])).rowCount, 'pei');} catch(e) {console.log(e); console.log('pei failed')}
}
async function yukon(browser, page) {
if(page === undefined) {
page = await browser.newPage();
}
page = await mnmalism(page);
await page.goto('https://cannabisyukon.org/store-locations');
let idx = [];
let n = (await page.$$eval('.component-store-location__title', x => x.map(y => y.textContent.trim()))).map(sanitize_name);
let address = [];
let results = await page.$$eval('.component-store-location__address', x => x.map(y => Array.from(y.querySelectorAll('p')).map(z => z.textContent.trim())));
page.close();
for(let i = 0; i < results.length; ++i) {
idx.push(i);
address.push(results[i][0]);
}
await postgres.query(
`create temporary table yukon as
select *
from unnest (
$1::smallint[],
$2::citext[],
$3::citext[]
) as t(idx, joined_name, address)`,
[idx, n, address]
);
idx = (await postgres.query(`
select idx
from yukon
where not exists (
select
from store_temp
where
joined_name = yukon.joined_name and
address = yukon.address and
address2 = '' and
city = '' and
region = 'yukon' and
country = 'canada' and
postal_code = ''
)
`)).rows.map(x => x.idx);
let names = [];
address = [];
let address2 = [];
let city = [];
let region = [];
let country = [];
let postal_code = [];
let longitude = [];
let latitude = [];
let osrm_hint_foot = [];
let osrm_hint_car = [];
let timezone = [];
let phone = [];
let url = [];
for(let j = 0; j < idx.length; ++j) {
const i = idx[j];
let r;
try {
r = await g(`${results[i][0].replace('\n', ',')},canada`);
} catch(e) {
console.error(e);
console.log(n[i], results[i]);
continue;
}
if(!pc.test(r[0].postal_code)) {
console.error('postal_code goofup', r, n[i], results[i]);
continue;
}
names.push(n[i]);
address.push(r[0].address);
address2.push(r[0].address2);
city.push(r[0].city);
region.push(r[0].region);
country.push(r[0].country);
postal_code.push(r[0].postal_code);
longitude.push(r[1].lng);
latitude.push(r[1].lat);
osrm_hint_foot.push(r[3]);
osrm_hint_car.push(r[4]);
timezone.push(r[2]);
let p;
let u;
for(let k = 1; k < results[i].length; ++k) {
if(results[i][k].startsWith('http')) {
u = results[i][k];
} else if(results[i][k].replace(/\D/g, '').length === 10) {
p = results[i][k];
}
if(u && p) {
break;
}
}
phone.push(p);
url.push(u);
} try {
console.log((await postgres.query(`insert into store (
joined_name,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
phone,
url,
partner,
delivery,
pickup,
prepayment,
name
) select i.*, false, false, false, false, i.joined_name from unnest (
$1::citext[],
$2::citext[],
$3::citext[],
$4::citext[],
$5::citext[],
$6::citext[],
$7::citext[],
$8::decimal[],
$9::decimal[],
$10::text[],
$11::text[],
$12::text[],
$13::packed_phone_number[],
$14::text[]
) as i(
joined_name,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
phone,
url
) where not exists (select 1 from store_history where
store_history.joined_name = i.joined_name and
store_history.address = i.address and
store_history.address2 is not distinct from i.address2 and
store_history.city = i.city and
store_history.region = i.region and
store_history.country = i.country and
store_history.postal_code = i.postal_code
) or exists (select 1 from store where
store.joined_name = i.joined_name and
store.address = i.address and
store.address2 is not distinct from i.address2 and
store.city = i.city and
store.region = i.region and
store.country = i.country and
store.postal_code = i.postal_code
) on conflict (joined_name, address, address2, city, region, country, postal_code)
where address is not null and address2 is not null and city is not null and postal_code is not null
do update set longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
where store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
names,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
phone,
url
])).rowCount, 'yukon');} catch(e) {console.log(e); console.log('yukon failed')}
}
async function nwt(browser, page) {
if(page === undefined) {
page = await browser.newPage();
}
page = await mnmalism(page);
await page.goto('https://www.ntlcc.ca/en/where-buy-cannabis');
let results = await page.$$eval('#content h3', x => {
let out = [];
for(let i = 0; i < x.length; ++i) {
let stuff = x[i].textContent.trim();
if(stuff === 'Norman Wells') {
stuff = 'Norman Wells Liquor Agency';
}
let store = [stuff];
x[i] = x[i].nextElementSibling;
if(x[i]?.tagName === 'P') {
store = store.concat(x[i].textContent.split('\n').map(y => y.trim()));
}
if(stuff === 'Norman Wells Liquor Agency' && store[1] === 'Franklin Ave') {
store[1] = '15 Franklin Ave';
}
out.push(store);
}
return out;
});
page.close();
let idx = [];
let names = [];
let address = [];
for(let i = 0; i < results.length; ++i) {
idx.push(i);
names.push(results[i][0] = sanitize_name(results[i][0]));
address.push(results[i][1]);
}
await postgres.query(
`create temporary table nwt as
select *
from unnest (
$1::smallint[],
$2::citext[],
$3::citext[]
) as t(idx, joined_name, address)`,
[idx, names, address]
);
idx = (await postgres.query(`
select idx
from nwt
where not exists (
select
from store_temp
where
joined_name = nwt.joined_name and
address = nwt.address and
address2 = '' and
city = '' and
region = 'nwt' and
country = 'canada' and
postal_code = ''
)
`)).rows.map(x => x.idx);
names = [];
address = [];
let address2 = [];
let city = [];
let region = [];
let country = [];
let postal_code = [];
let longitude = [];
let latitude = [];
let osrm_hint_foot = [];
let osrm_hint_car = [];
let timezone = [];
let phone = [];
for(let j = 0; j < idx.length; ++j) {
const i = idx[j];
let r;
let adr;
let fun;
if(results[i][0] === 'Hay River Liquor') {
adr = '76 Capital Dr, Hay River, X0E 1G2';
fun = '101';
} else {
adr = `${results[i][0]}, ${results[i][1]}`
}
try {
r = await g(`${adr}, northwest territories, canada`);
} catch(e) {
console.error(e);
console.log(results[i]);
continue;
}
if(!pc.test(r[0].postal_code)) {
if(r[0].address === '10021 100 Street') {
r[0].postal_code = 'X0E0N0';
} else {
console.error('postal_code goofup', r, results[i]);
continue;
}
}
names.push(results[i][0]);
address.push(r[0].address);
address2.push(fun ?? r[0].address2);
city.push(r[0].city);
region.push(r[0].region);
country.push(r[0].country);
postal_code.push(r[0].postal_code);
longitude.push(r[1].lng);
latitude.push(r[1].lat);
osrm_hint_foot.push(r[3]);
osrm_hint_car.push(r[4]);
timezone.push(r[2]);
phone.push(results[i][2]);
} try{
console.log((await postgres.query(`insert into store (
joined_name,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
phone,
partner,
delivery,
pickup,
prepayment,
name
) select i.*, false, false, false, false, i.joined_name from unnest (
$1::citext[],
$2::citext[],
$3::citext[],
$4::citext[],
$5::citext[],
$6::citext[],
$7::citext[],
$8::decimal[],
$9::decimal[],
$10::text[],
$11::text[],
$12::text[],
$13::packed_phone_number[]
) as i(
joined_name,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
phone
) where not exists (select 1 from store_history where
store_history.joined_name = i.joined_name and
store_history.address = i.address and
store_history.address2 is not distinct from i.address2 and
store_history.city = i.city and
store_history.region = i.region and
store_history.country = i.country and
store_history.postal_code = i.postal_code
) or exists (select 1 from store where
store.joined_name = i.joined_name and
store.address = i.address and
store.address2 is not distinct from i.address2 and
store.city = i.city and
store.region = i.region and
store.country = i.country and
store.postal_code = i.postal_code
) on conflict (joined_name, address, address2, city, region, country, postal_code)
where address is not null and address2 is not null and city is not null and postal_code is not null
do update set longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
where store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
names,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
phone
])).rowCount, 'nwt');} catch(e) {console.log(e); console.log('nwt failed')}
}
async function ontario() {
let on = parse(await request('https://www.agco.ca/sites/default/files/opendata/AGCOWebSiteCannabisMapData.csv'), {skip_empty_lines: true, from_line: 2});
let idx = [];
let names = [];
let address = [];
for(let i = 0; i < on.length; ++i) {
if(on[i][3] === 'Authorized to Open') {
idx.push(i);
names.push(on[i][6] = sanitize_name(on[i][6])
.replace(new RegExp('(?<!^)' + (on[i][7] + ' (east|west|north|south)').split(/\s+/).map((n, idx) => {
n = n.toLowerCase().replace(/\./g, '');
if(n === 'st') {
n = 'st(reet)?';
} else if(n === 'rd') {
n = 'r(oa)?d';
} else if(n === 'ave') {
n = 'ave(nue)?';
} else if(n === 'dr') {
n = 'dr(ive)?';
}
return `(\\b${n}\\b)${idx !== 1 ? '?' : ''}`;
}).join('\\s?'), 'i'), '')
.replace(new RegExp('(?<!^)\\b' + on[i][8] + '\\b', 'gi'), '')
);
address.push(on[i][11]);
}
}
await postgres.query(
`create temporary table ontario as
select *
from unnest (
$1::smallint[],
$2::citext[],
$3::citext[]
) as t(idx, joined_name, address)`,
[idx, names, address]
);
idx = (await postgres.query(`
select idx
from ontario
where not exists (
select
from store_temp
where
joined_name = ontario.joined_name and
address = ontario.address and
address2 = '' and
city = '' and
region = 'ontario' and
country = 'canada' and
postal_code = ''
)
`)).rows.map(x => x.idx);
names = [];
address = [];
let address2 = [];
let city = [];
let region = [];
let country = [];
let postal_code = [];
let longitude = [];
let latitude = [];
let osrm_hint_foot = [];
let osrm_hint_car = [];
let timezone = [];
for(let j = 0; j < idx.length; ++j) {
const i = idx[j];
let r;
try {
r = await g(on[i][11].replace(/OSPREY MIIKAN RD/i, 'OSPREY MIIKAN'));
} catch(e) {
console.error(e);
console.log(on[i]);
continue;
}
if(!pc.test(r[0].postal_code)) {
on[i][10] = on[i][10].replace(/\s+/g, '');
if(r[0].postal_code.length === 3 && on[i][10].length === 6) {
r[0].postal_code = on[i][10];
} else {
console.error('postal_code goofup', r, on[i]);
continue;
}
}
names.push(on[i][6]);
address.push(r[0].address);
address2.push(r[0].address2);
city.push(r[0].city);
region.push(r[0].region);
country.push(r[0].country);
postal_code.push(r[0].postal_code);
longitude.push(r[1].lng);
latitude.push(r[1].lat);
osrm_hint_foot.push(r[3]);
osrm_hint_car.push(r[4]);
timezone.push(r[2]);
} try {
console.log((await postgres.query(`insert into store (
joined_name,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
partner,
delivery,
pickup,
prepayment,
name
) select i.*, false, false, false, false, i.joined_name from unnest (
$1::citext[],
$2::citext[],
$3::citext[],
$4::citext[],
$5::citext[],
$6::citext[],
$7::citext[],
$8::decimal[],
$9::decimal[],
$10::text[],
$11::text[],
$12::text[]
) as i(
joined_name,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone
) where not exists (select 1 from store_history where
store_history.joined_name = i.joined_name and
store_history.address = i.address and
store_history.address2 is not distinct from i.address2 and
store_history.city = i.city and
store_history.region = i.region and
store_history.country = i.country and
store_history.postal_code = i.postal_code
) or exists (select 1 from store where
store.joined_name = i.joined_name and
store.address = i.address and
store.address2 is not distinct from i.address2 and
store.city = i.city and
store.region = i.region and
store.country = i.country and
store.postal_code = i.postal_code
) on conflict (joined_name, address, address2, city, region, country, postal_code)
where address is not null and address2 is not null and city is not null and postal_code is not null
do update set longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
where store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
names,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone
])).rowCount, 'ontario'); } catch(e) {console.log(e); console.log('ontario failed')}
}
async function quebec() {
let results = (await request({method: 'post', url: 'https://www.sqdc.ca/api/storelocator/stores', headers: {
'X-Requested-With': 'XMLHttpRequest',
'Accept-Language': 'en-CA',
'Content-Type': 'application/json'
}, body: '{"page":1,"pageSize":900}'}).then(JSON.parse)).Stores;
let idx = [];
let address = [];
let address2 = [];
let city = [];
let postal_code = [];
for(let i = 0; i < results.length; ++i) {
idx.push(i);
address.push(results[i].Address.Line1);
address2.push(results[i].Address.Line2 ?? '');
city.push(results[i].Address.City);
postal_code.push(results[i].Address.PostalCode);
}
await postgres.query(
`create temporary table quebec as
select *
from unnest (
$1::smallint[],
$2::citext[],
$3::citext[],
$4::citext[],
$5::citext[]
) as t(idx, address, address2, city, postal_code)`,
[idx, address, address2, city, postal_code]
);
idx = (await postgres.query(`
select idx
from quebec
where not exists (
select
from store_temp
where
joined_name = '' and
address = quebec.address and
address2 = quebec.address2 and
city = quebec.city and
region = 'quebec' and
country = 'canada' and
postal_code = quebec.postal_code
)
`)).rows.map(x => x.idx);
address = [];
address2 = [];
city = [];
let country = [];
postal_code = [];
let longitude = [];
let latitude = [];
let osrm_hint_foot = [];
let osrm_hint_car = [];
let timezone = [];
let phone = [];
let open = [];
for(let j = 0; j < idx.length; ++j) {
const i = idx[j];
try {
r = await g(`${results[i].Address.Line1}${results[i].Address.Line2 ? ',' + results[i].Address.Line2 : ''}, ${results[i].Address.City}, ${results[i].Address.RegionName}, ${results[i].Address.PostalCode}, canada`);
} catch(e) {
console.error(e);
console.log(results[i]);
continue;
}
if(!pc.test(r[0].postal_code)) {
results[i].Address.PostalCode = results[i].Address.PostalCode.replace(/\s+/g, '');
if(r[0].postal_code.length === 3 && results[i].Address.PostalCode.length === 6) {
r[0].postal_code = results[i].Address.PostalCode
} else {
console.error('postal_code goofup', r, results[i]);
continue;
}
}
address.push(r[0].address);
address2.push(results[i].Address.Line1 === '110-1, rue St-Germain Ouest' ? 1 : r[0].address2);
city.push(r[0].city);
country.push(r[0].country);
postal_code.push(r[0].postal_code);
longitude.push(r[1].lng);
latitude.push(r[1].lat);
osrm_hint_foot.push(r[3]);
osrm_hint_car.push(r[4]);
timezone.push(r[2]);
phone.push(results[i].PhoneNumber);
let tim = [];
for(let j = 0; j < results[i].Schedule.OpeningHours.length; ++j) {
let d = 0;
switch(results[i].Schedule.OpeningHours[j].LocalizedDay) {
case 'Tuesday': d = 1440; break;
case 'Wednesday': d = 2880; break;
case 'Thursday': d = 4320; break;
case 'Friday': d = 5760; break;
case 'Saturday': d = 7200; break;
case 'Sunday': d = 8640; break;
}
for(let k = 0; k < results[i].Schedule.OpeningHours[j].OpeningTimes.length; ++k) {
let beg = results[i].Schedule.OpeningHours[j].OpeningTimes[k].BeginTime.match(apm);
beg = (Number(beg[1]) % 12 + (beg[4]?.toLowerCase() === 'p' ? 12 : 0)) * 60 + Number(beg[2]);
let end = results[i].Schedule.OpeningHours[j].OpeningTimes[k].EndTime.match(apm);
end = (Number(end[1]) % 12 + (end[4]?.toLowerCase() === 'p' ? 12 : 0)) * 60 + Number(end[2]);
let rtn = [d + beg, d + end];
if(end < beg) {
if(d === 8640) {
tim.push([rtn[0], 10080]);
rtn[0] = 0;
rtn[1] -= 8640;
} else {
rtn[1] += 1440;
}
}
tim.push(rtn);
}
}
open.push(JSON.stringify(tim));
} try {
console.log((await postgres.query(`insert into store (
joined_name,
name,
url,
address,
address2,
city,
region,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
open,
phone,
partner,
delivery,
pickup,
prepayment
) select
'SQDC',
'SQDC',
'sqdc.ca',
address,
address2,
city,
'Quebec',
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
(select range_agg(int4range((x->>0)::int, (x->>1)::int), true, true) from json_array_elements(open) as t(x)),
phone,
false,
false,
false,
false
from unnest (
$1::citext[],
$2::citext[],
$3::citext[],
$4::citext[],
$5::citext[],
$6::decimal[],
$7::decimal[],
$8::text[],
$9::text[],
$10::text[],
$11::json[],
$12::packed_phone_number[]
) as i(
address,
address2,
city,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
open,
phone
) where not exists (select 1 from store_history where
store_history.joined_name = 'SQDC' and
store_history.address = i.address and
store_history.address2 is not distinct from i.address2 and
store_history.city = i.city and
store_history.region = 'Quebec' and
store_history.country = i.country and
store_history.postal_code = i.postal_code
) or exists (select 1 from store where
store.joined_name = 'SQDC' and
store.address = i.address and
store.address2 is not distinct from i.address2 and
store.city = i.city and
store.region = 'Quebec' and
store.country = i.country and
store.postal_code = i.postal_code
) on conflict (joined_name, address, address2, city, region, country, postal_code)
where address is not null and address2 is not null and city is not null and postal_code is not null
do update set open = excluded.open, longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
where store.open is distinct from excluded.open and store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
address,
address2,
city,
country,
postal_code,
longitude,
latitude,
osrm_hint_foot,
osrm_hint_car,
timezone,
open,
phone
])).rowCount, 'quebec');} catch(e) {console.log(e); console.log('quebec failed')}
}
(async function() {
const [browser,] = await Promise.all([
puppeteer.launch({
args: ["--no-sandbox"],
headless: true
}),
postgres.connect().then(() => postgres.query('begin'))
]);
await Promise.all([
alberta(browser, (await browser.pages())[0]),
britishcolumbia(),
manitoba(browser),
saskatchewan(browser),
novascotia(),
newbrunswick(browser),
newfoundland(),
pei(browser),
quebec(),
yukon(browser),
nwt(browser),
ontario()
]);
await postgres.query(`
create temporary table store_temp_temp as
select
joined_name,
address,
'' as address2,
city,
'alberta' as region,
'canada' as country,
postal_code
from alberta
union all
select
joined_name,
address,
'',
city,
'manitoba',
'canada',
''
from manitoba
union all
select
joined_name,
address,
'',
city,
'saskatchewan',
'canada',
''
from saskatchewan
union all
select
'',
address,
'',
'',
'newbrunswick',
'canada',
''
from newbrunswick
union all
select
joined_name,
address,
'',
city,
'britishcolumbia',
'canada',
postal_code
from britishcolumbia
union all
select
joined_name,
address,
address2,
city,
'newfoundland',
'canada',
postal_code
from newfoundland
union all
select
'',
address,
address2,
city,
'novascotia',
'canada',
postal_code
from novascotia
union all
select
'',
address,
'',
'',
'pei',
'canada',
''
from pei
union all
select
joined_name,
address,
'',
'',
'yukon',
'canada',
''
from yukon
union all
select
joined_name,
address,
'',
'',
'nwt',
'canada',
''
from nwt
union all
select
joined_name,
address,
'',
'',
'ontario',
'canada',
''
from ontario
union all
select
'',
address,
address2,
city,
'quebec',
'canada',
postal_code
from quebec
`);
let removeQ = (await postgres.query(`
select *
from store_temp
where not exists (
select
from store_temp_temp
where
joined_name = store_temp.joined_name and
address = store_temp.address and
address2 = store_temp.address2 and
city = store_temp.city and
region = store_temp.region and
country = store_temp.country and
postal_code = store_temp.postal_code
)
`)).rows;
if(removeQ.length) {
console.log('human, should these be removed?', removeQ);
}
await postgres.query('truncate store_temp');
await postgres.query('insert into store_temp select * from store_temp_temp');
await Promise.all([
browser.close(),
postgres.query('commit').then(() => postgres.end())
]);
})();