2ZHTBPOJQI4FS3OQJXUDBD62HVFPGSKI633IXXKAGPGQLURMKVPAC WA2FN2YR2TDCOLDWCZPYHRHPKJXE7KUB7AFERUCEB3KODVBNCUVQC Y4W4MACPKH3IVO6JIQYGN4V5LR6EM6RPZEJ4RHH6ZBZUDA63AGRQC RGMMX4YQP6NPCPFAGZ5SRITSPJQW4F77S6RJLYJMS3D732LUGDPQC E4HFJ4L4PAGV7R7EXVG2B2CWDGGW7XWU4D37VURZ66HZ3ILHCFUAC NDMM44EV2XD5RP7J4Q25ZX52LUP7WOMTKO3C2PDXW7IWFEVWUW6QC VO5G3FF3NDOKCIF2OU7G257R3XIPTFDCAMDTOVKHKOB5Z3I55KDQC 4WREYORZT3SWUXADWHSS6B4PQSP3QSLH77CGNKRH6IRKUMX4TARAC SE6MCCXTIXSGMAH5EL7EPDXIQKV6THCCY2H4OQJJQOHCUQAWE5VQC WYTMZJFYVKHR4QH7AV5JUNWXT6NAC5NNQNPZCQSDI6LGI7DVXFYAC EGNRBQUMOZHAF5B7KCMTAPMB4H3USK4MK33Q6T5V26D6HOJNW6LQC 2FPZGFF5PCGNV45HOB6TROMJFPURHPXM7YSPWNXCHFJS7EICVKQAC Q4J4JHWWARLZQBYRRR33FRSFZK5BQMWGBGYQ22MLLVUELI7FYHRQC DCYC55MAVFDM43TEEUMAHLPOHNQ3EJ5PEVCA7H6ZTKNQHJZK7XJAC 4ADMC3SGJNATDR6D7WXHNZ6RD5F5BUPLPRMCEUERWRWKVZQVAO3AC TBTYGTE3JUKOX6GDBR7AMMMV4RYCUFAK5N3ATSVCLCGESBSMJUHQC ZI2RJOZ2HXBHX7L54BTSDKESY6NG5KBJLVHCNY7K7ZLGIUOIHQUQC "@google/maps": {"version": "1.0.2","resolved": "https://registry.npmjs.org/@google/maps/-/maps-1.0.2.tgz","integrity": "sha512-iTsvIIgVjIicC+UdLCJOMSBnOmLnmgnOXrjTBUUQr/ffBMfH9GdnlrA9R+vH/kZSFEp9AZUDw0vJ5ES6avfTPQ==",
"@googlemaps/google-maps-services-js": {"version": "2.0.2","resolved": "https://registry.npmjs.org/@googlemaps/google-maps-services-js/-/google-maps-services-js-2.0.2.tgz","integrity": "sha512-AzhUckl3X9QmopPxx/QKUwoUenvnkhTAP6811siaociclxS3vf4GHGVIMAEHVcvjt+2cWeq+603gLBVciS5TKg==",
"agent-base": {"version": "4.3.0","resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz","integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
"agentkeepalive": {"version": "4.1.0","resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.0.tgz","integrity": "sha512-CW/n1wxF8RpEuuiq6Vbn9S8m0VSYDMnZESqaJ6F2cWN9fY8rei2qaxweIaRgq+ek8TqfoFIsUjaGNKGGEHElSg==",
"es6-promisify": "^5.0.0"
"debug": "^4.1.0","depd": "^1.1.2","humanize-ms": "^1.2.1"},"dependencies": {"debug": {"version": "4.1.1","resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz","integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==","requires": {"ms": "^2.1.1"}}
}},"es6-promise": {"version": "4.2.8","resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz","integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="},"es6-promisify": {"version": "5.0.0","resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz","integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=","requires": {"es6-promise": "^4.0.3"
"follow-redirects": {"version": "1.5.10","resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz","integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==","requires": {"debug": "=3.1.0"},"dependencies": {"debug": {"version": "3.1.0","resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz","integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==","requires": {"ms": "2.0.0"}},"ms": {"version": "2.0.0","resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz","integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="}}},
"https-proxy-agent": {"version": "3.0.1","resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz","integrity": "sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg==",
"humanize-ms": {"version": "1.2.1","resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz","integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=",
"version": "7.17.0","resolved": "https://registry.npmjs.org/pg/-/pg-7.17.0.tgz","integrity": "sha512-70Q4ZzIdPgwMPb3zUIzAUwigNJ4v5vsWdMED6OzXMfOECeYTvTm7iSC3FpKizu/R1BHL8Do3bLs6ltGfOTAnqg==",
"version": "7.18.1","resolved": "https://registry.npmjs.org/pg/-/pg-7.18.1.tgz","integrity": "sha512-1KtKBKg/zWrjEEv//klBbVOPGucuc7HHeJf6OEMueVcUeyF3yueHf+DvhVwBjIAe9/97RAydO/lWjkcMwssuEw==",
"version": "2.0.9","resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-2.0.9.tgz","integrity": "sha512-gNiuIEKNCT3OnudQM2kvgSnXsLkSpd6mS/fRnqs6ANtrke6j8OY5l9mnAryf1kgwJMWLg0C1N1cYTZG1xmEYHQ=="
"version": "2.0.10","resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-2.0.10.tgz","integrity": "sha512-qdwzY92bHf3nwzIUcj+zJ0Qo5lpG/YxchahxIN8+ZVmXqkahKXsnl2aiJPHLYN9o5mB/leG+Xh6XKxtP7e0sjg=="
"query-string": {"version": "github:jpoehnelt/query-string#e22cdb49ef848efaed4fe60d63d2504b496027c1","from": "github:jpoehnelt/query-string#e22cdb49ef848efaed4fe60d63d2504b496027c1","requires": {"decode-uri-component": "^0.2.0","split-on-first": "^1.0.0","strict-uri-encode": "^2.0.0"}},
const inBrackets = /\s*\(.*\)\s*/;const specialqty = /((\d+)\s*[Xx]\s*)?(\d*\.?\d+)\s*([A-WYZa-wyz]{1,2})(\b|$)/;function normalize(brand, title, type) {let ot = title.toLowerCase();title = title.replace(brand, '');brand = brand.toLowerCase().replace('.', '').replace(/&|(\b(and|health|therapeutics|co|company|inc|cannabis)\b)/g, '').trim();if(brand.includes('sundial')) {brand = brand.replace(/\b(calm|ease|flow|lift|spark)\b/g, '').trim();title = title.replace(/\b(calm|ease|flow|lift|spark)\b/g, '').trim();} else if(brand === 'alta vie') {brand = 'altavie';} else if(brand === 'twd') {brand = 'tweed';}let ratio = title.match(/\d+:\d+/);let q = title.match(specialqty);title = title.toLowerCase().replace(inBrackets, '').replace(brand, '').replace(/twd(\.?)|"|-|−|(\d+:\d+)|%|&|:/g, '').replace(/\b(liquid|and|oral|drop(s?)|((hard|soft|liquid)(-|−| )?)?(capsule|cap|gel)(s?)|pre(-|−| )?roll(s?)|pack(s?)|joint(s?)|oil|flower|spray)\b/g, '').replace(specialqty, '').trim();if(ot.includes('fiveup') || /5\s*up/.test(ot)) {title = '5up';//early return?} else if(ot.includes('tenup') || /10\s*up/.test(ot)) {title = '10up';//early return?} else if(title.includes('reserve') && brand.includes('edison')) {title = title.replace('reserve', '').trim();brand = 'edison reserve';} else if(title.includes('select') && brand.includes('canaca')) {title = title.replace('select', '').trim();brand = 'canaca select';} else if(brand === 'tantalus labs') {title = title.replace(/bc|sungrown/g, '').trim();
let days_of_week = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];function notmissing(parameters, fields, ws, response_ID) {let sad = fields.filter(field => parameters[field] !== 0 && parameters[field] !== 0n && !parameters[field]).join(', ');if(sad.length) {ws.send(JSON.stringify({response_ID,data: sad}));return false;} else {return true;
if(title.includes('casa') && title.includes('blanca') && brand.includes('edison')) {title = 'casablanca';}if(type === 'oil' || type === 'spray') {title = title.replace('mist', '').trim();}if(title.length === 0) {if(ratio === null) {title = ot;} else {title = ratio[0];}}if(type === 'capsule' && q !== null) {title += ' ' + q[0].replace(/\s+/, '');}title = title.split(/\s+/).sort().join(' ');return [brand, title, type];
try {//empty string should turn to null / undefinedlet fuck = await googleMapsClient.geocode({address: `${parameters.address}, ${parameters.address2}, ${parameters.city}, ${parameters.region}, ${parameters.country}, ${parameters.postal_code}`}).asPromise().then();if(fuck.status === 200 && fuck.json.status === 'OK') {let fuck2 = await googleMapsClient.timezone({location: fuck.json.results[0].geometry.location}).asPromise();if(fuck2.status === 200) {//https://stackoverflow.com/questions/20561254let store = await pool.query('insert into store (name, URL, address, address2, city, region, country, postal_code, coordinate, timezone) values ($1, $2, $3, $4, $5, $6, $7, $8, point($9, $10), $11) returning id', [parameters.name,parameters.URL,parameters.address,parameters.address2 ? parameters.address2 : null,parameters.city,parameters.region,parameters.country,parameters.postal_code,fuck.json.results[0].geometry.location.lat,fuck.json.results[0].geometry.location.lng,fuck2.json.timeZoneId]);if(Array.isArray(parameters.taxes) && parameters.taxes.length) {let query = 'insert into store_tax (store, name, rate) values ';let params = [];for(let i = 0; i < parameters.taxes.length; ++i) {query += `($${i * 3 + 1}, $${i * 3 + 2}, $${i * 3 + 3}),`;params.push(store.rows[0].id);params.push(parameters.taxes[i].name);params.push(parameters.taxes[i].rate);}await pool.query(query.slice(0, -1), params);}if(Array.isArray(parameters.times) && parameters.times.length) {let query = 'insert into store_time (store, open) values ';let params = [];for(let i = 0; i < parameters.times.length; ++i) {query += `($${i * 2 + 1}, $${i * 2 + 2}),`;params.push(store.rows[0].id);let ts = parameters.times[i].time_start.split(':');ts = Number(parameters.times[i].day_start) * 1440 + Number(ts[0]) * 60 + Number(ts[1]);let te = parameters.times[i].time_end.split(':');te = Number(parameters.times[i].day_end) * 1440 + Number(te[0]) * 60 + Number(te[1]);params.push(`[${ts}, ${te})`);}await pool.query(query.slice(0, -1), params);}ws.send(JSON.stringify({response_ID: request_ID}));//ws.publish(store.rows[0].id)
if(notmissing(parameters, ['name', 'URL', 'address', 'city', 'region', 'country', 'postal_code'], ws, request_ID)) {let params = [parameters.name,parameters.URL,parameters.address,parameters.address2 ? parameters.address2 : null,parameters.city,parameters.region,parameters.country,parameters.postal_code];//I went through the effort of delaying gapi as long as I could and folding three inserts into one transaction, only for the unique constraint to goof me up. Likely won't happen often though, so accept the tradeoff.let q = 1;let query = '';if(Array.isArray(parameters.taxes) && parameters.taxes.length) {query += `, insert${++q} as (insert into store_tax (store, name, rate)values ${parameters.taxes.map(tax => `((select id from insert1), $${params.push(tax.name)}, $${params.push(tax.rate)})`).join(', ')})`;}if(Array.isArray(parameters.times) && parameters.times.length) {let times = parameters.times.map((time, idx) => {let ts = time.time_start.split(':');ts = Number(time.day_start) * 1440 + Number(ts[0]) * 60 + Number(ts[1]);let te = time.time_end.split(':');te = Number(time.day_end) * 1440 + Number(te[0]) * 60 + Number(te[1]);if(te <= ts) {//return `time ${idx}: close time(${days_of_week[time.day_end]} ${time.time_end}) must be later than open time(${days_of_week[time.day_start]} ${time.time_start})`;//}//return `((select id from insert1), $${params.push(`[${ts}, ${te})`)})`;});let time_errors = times.filter(time => time.startsWith('time'));//if(time_errors.length) {//ws.send(JSON.stringify({//response_ID: request_ID,//data: time_errors//}));//break;//}//to overcome postgres error shortcomingquery += `, insert${++q} as (insert into store_time (store, open)values ${times.join(', ')})`;}if(Array.isArray(parameters.images) && parameters.images.length) {query += `, insert${++q} as (insert into store_image (store, URL)values ${parameters.images.map(image => `((select id from insert1), $${params.push(image)})`).join(', ')})`;}/*two google maps api error handlers because their api is inconsistent as fuckmissing api key will return 'REQUEST_DENIED', but 200, so (2)wrong parameters will return 'INVALID_REQUEST', so 400 (1) for geocodebut 200 (2) for timezone!! 400 throws automatically, but 200 need to be thrown manually*/let location;try {location = (await googleMapsClient.geocode({params: {address: `${parameters.address}, ${parameters.address2}, ${parameters.city}, ${parameters.region}, ${parameters.country}, ${parameters.postal_code}`, key: process.env.GAPI}})).data;if(location.status === 'OK') {location = location.results[0].geometry.location;
} else {throw 'geocode fuckup';
} catch(e) {if(e.response) {console.error(e.response.data);//google maps api(1)}else if(e.status) {console.error(e);//google maps api(2)}ws.send(JSON.stringify({response_ID: request_ID,data: 'google geocode failure'}));//todo: status: 'ZERO_RESULTS' => "invalid address :("break;
} catch(e) {let error = 'store already exists';if(e.constraint !== 'store_url_key') {console.error(e);error = 'error';
let tz;try {tz = (await googleMapsClient.timezone({params: {location, timestamp: 0, key: process.env.GAPI}})).data;if(tz.status === 'OK') {tz = tz.timeZoneId;} else {throw tz;}} catch(e) {if(e.response) {console.error(e.response.data);//google maps api(1)}else if(e.status) {console.error(e);//google maps api(2)}ws.send(JSON.stringify({response_ID: request_ID,data: 'google timezone failure'}));break;
ws.send(JSON.stringify({response_ID: request_ID,data: error}));
//promise then catch is fine since this is the last thing to do this function. don't need to break;pool.query(`with insert1 as (insert into store (name, URL, address, address2, city, region, country, postal_code, coordinate, timezone)values ($1, $2, $3, $4, $5, $6, $7, $8, point($${params.push(location.lat)}, $${params.push(location.lng)}), $${params.push(tz)})returning id)${query}select id from insert1`,params).then(r => {console.log/*ws.publish*/(r.rows[0].id)ws.send(JSON.stringify({response_ID: request_ID}));}).catch(e => {/* todoClass 23 https://www.postgresql.org/docs/current/errcodes-appendix.html with e.codee.tablehttps://stackoverflow.com/a/4108266use implicit names in initialize.sql. then, can use e.constraint, remove e.table + _ from beginning, remove suffix to see which columns are affectede.detail is fine for 23505, but 23514 just complains about the row as a whole. how can we get the specific details? https://stackoverflow.com/a/47972916 for python...usefulcode: '23505',detail: 'Key (store, name)=(21, hst) already exists.',useless[message]: 'new row for relation "store_tax" violates check constraint "store_tax_rate_check"',name: 'error',length: 266,severity: 'ERROR',code: '23514',detail: 'Failing row contains (15, 28, hst, 2, 2020-02-11 20:20:54.923496-05, infinity).',what I want: rate > 0 and rate < 12 > 0 and 2 < 1true and falsefalse*/console.log(e);ws.send(JSON.stringify({response_ID: request_ID,data: 'db fuckup'}));});