From 48f6fef82e1833509b6642cbbe591437a92e34c9 Mon Sep 17 00:00:00 2001 From: Timo Volkmann Date: Wed, 10 Jun 2020 08:07:15 +0200 Subject: [PATCH 01/20] initial backend, first score calc draft --- .gitignore | 2 + backend/.gitkeep | 0 backend/app.js | 41 +++++ backend/package-lock.json | 374 ++++++++++++++++++++++++++++++++++++++ backend/package.json | 14 ++ backend/sampledata.js | 16 ++ backend/score.js | 31 ++++ 7 files changed, 478 insertions(+) delete mode 100644 backend/.gitkeep create mode 100644 backend/app.js create mode 100644 backend/package-lock.json create mode 100644 backend/package.json create mode 100644 backend/sampledata.js create mode 100644 backend/score.js diff --git a/.gitignore b/.gitignore index b039da1..a6deceb 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ # System Files .DS_Store Thumbs.db + +node_modules \ No newline at end of file diff --git a/backend/.gitkeep b/backend/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/backend/app.js b/backend/app.js new file mode 100644 index 0000000..e9f4a02 --- /dev/null +++ b/backend/app.js @@ -0,0 +1,41 @@ +var express = require('express') +var sampledata = require('./sampledata') + +const app = express() +const port = 3000 + +const sampleRegions = [ + { + id: 29837, + name: "Timbuktu", + country: "Mali" + } +] +const samplePresets = [ + { + id: 29837, + parameter: "temperature", + label: "warm", + values: [22, 25] + } +] + +app.get('/', (req, res) => res.send('Hello Timo!')) +app.get('/v1/regions', (req, res) => res.json(sampleRegions)) +app.get('/v1/presets', (req, res) => res.json(samplePresets)) +app.get('/v1/search', (req, res) => { + // check query params + let response = {} + + response.meta = { + params: req.params, + query: req.query, + headers: req.headers + } + + response.data = sampledata.search_response_model + + res.json(response) +}) + +app.listen(port, () => console.log(`Travopti backend listening at http://localhost:${port}`)) \ No newline at end of file diff --git a/backend/package-lock.json b/backend/package-lock.json new file mode 100644 index 0000000..fd25ecb --- /dev/null +++ b/backend/package-lock.json @@ -0,0 +1,374 @@ +{ + "name": "cc-data-backend", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + } + } +} diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000..87a7cb8 --- /dev/null +++ b/backend/package.json @@ -0,0 +1,14 @@ +{ + "name": "cc-data-backend", + "version": "1.0.0", + "description": "", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "express": "^4.17.1" + } +} diff --git a/backend/sampledata.js b/backend/sampledata.js new file mode 100644 index 0000000..5c5e8a0 --- /dev/null +++ b/backend/sampledata.js @@ -0,0 +1,16 @@ +var sampledata = {} + +sampledata.search_response_model = [ + { + region_id: 1, + name: 'Chicago', + country: 'USA', + avg_score: 0.0, + scores: { + temperature: 0.0, + humidity: 0.0, + } + } +] + +module.exports = sampledata; \ No newline at end of file diff --git a/backend/score.js b/backend/score.js new file mode 100644 index 0000000..4bba82e --- /dev/null +++ b/backend/score.js @@ -0,0 +1,31 @@ +/** + * + */ +const multiplier_temperature = 5; + + +/** + * + * @param {...any} scores expects objects which contains score and their weight + */ +function calculateAvgScore(...scores) { + return avgScore = scores.reduce((total, score) => total += score) / scores.length; +} + +function calculateScoreRange(min, max, multiplier, targetVal, sLowVal, sHighVal) { + // return full score when in range + if (targetVal >= sLowVal && targetVal <= sHighVal) return 10; + // choose value with smallest distance + let sVal = Math.abs(targetVal - sLowVal) < Math.abs(targetVal - sHighVal) ? sLowVal : sHighVal; + + return calculateScore(min, max, multiplier, targetVal, sVal); +} + +function calculateScore(min, max, multiplier, targetVal, searchVal) { + + let score = 1 - (Math.abs(searchVal - targetVal) / (max - min) * multiplier); + return score <= 0 ? 0 : score * 10; +} + +console.log('Calc') +console.log(calculateScoreRange(-15, 45, 5, 24, 15, 22)) \ No newline at end of file From 1cdc7f1bc38d2bb92b5899b8b1d79abdd39c1063 Mon Sep 17 00:00:00 2001 From: Timo Volkmann Date: Wed, 10 Jun 2020 09:20:15 +0200 Subject: [PATCH 02/20] add sql connection, config through ENV --- Scripts/setup.sql | 30 ++++++++--------- backend/.env | 5 +++ backend/climate.js | 0 backend/dbsql.js | 13 ++++++++ backend/package-lock.json | 68 +++++++++++++++++++++++++++++++++++++++ backend/package.json | 4 ++- 6 files changed, 104 insertions(+), 16 deletions(-) create mode 100644 backend/.env create mode 100644 backend/climate.js create mode 100644 backend/dbsql.js diff --git a/Scripts/setup.sql b/Scripts/setup.sql index b4378f3..5984893 100644 --- a/Scripts/setup.sql +++ b/Scripts/setup.sql @@ -14,19 +14,19 @@ -- Exportiere Datenbank Struktur für travopti DROP DATABASE IF EXISTS `travopti`; -CREATE DATABASE IF NOT EXISTS `travopti` /*!40100 DEFAULT CHARACTER SET latin1 COLLATE latin1_german1_ci */; +CREATE DATABASE IF NOT EXISTS `travopti` /*!40100 DEFAULT CHARACTER SET latin1 */; USE `travopti`; -- Exportiere Struktur von Tabelle travopti.countries DROP TABLE IF EXISTS `countries`; CREATE TABLE IF NOT EXISTS `countries` ( `id` int(11) NOT NULL AUTO_INCREMENT, - `country` varchar(255) COLLATE latin1_german1_ci NOT NULL, + `country` varchar(255) NOT NULL, `created_at` timestamp NOT NULL DEFAULT current_timestamp(), `updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), PRIMARY KEY (`id`), UNIQUE KEY `country` (`country`) -) ENGINE=InnoDB AUTO_INCREMENT=64 DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci; +) ENGINE=InnoDB AUTO_INCREMENT=64 DEFAULT CHARSET=utf8mb4; -- Exportiere Daten aus Tabelle travopti.countries: ~37 rows (ungefähr) DELETE FROM `countries`; @@ -75,9 +75,9 @@ INSERT INTO `countries` (`id`, `country`, `created_at`, `updated_at`) VALUES DROP TABLE IF EXISTS `regions`; CREATE TABLE IF NOT EXISTS `regions` ( `id` int(11) NOT NULL AUTO_INCREMENT, - `region` varchar(255) COLLATE latin1_german1_ci NOT NULL, + `region` varchar(255) NOT NULL, `country_id` int(11) DEFAULT NULL, - `meteostat_id` int(11) DEFAULT NULL, + `meteostat_id` varchar(11) DEFAULT NULL, `lon` double(22,0) DEFAULT NULL, `lat` double(22,0) DEFAULT NULL, `created_at` timestamp NOT NULL DEFAULT current_timestamp(), @@ -85,7 +85,7 @@ CREATE TABLE IF NOT EXISTS `regions` ( PRIMARY KEY (`id`) USING BTREE, KEY `FK_regions_countries` (`country_id`) USING BTREE, CONSTRAINT `FK_regions_countries` FOREIGN KEY (`country_id`) REFERENCES `countries` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=127 DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci; +) ENGINE=InnoDB AUTO_INCREMENT=127 DEFAULT CHARSET=utf8mb4; -- Exportiere Daten aus Tabelle travopti.regions: ~47 rows (ungefähr) DELETE FROM `regions`; @@ -160,7 +160,7 @@ CREATE TABLE IF NOT EXISTS `regions_byt` ( PRIMARY KEY (`id`), UNIQUE KEY `region_id` (`region_id`,`travelstyle`) USING BTREE, CONSTRAINT `FK_regions_byt_regions` FOREIGN KEY (`region_id`) REFERENCES `regions` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=191 DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci; +) ENGINE=InnoDB AUTO_INCREMENT=191 DEFAULT CHARSET=utf8mb4; -- Exportiere Daten aus Tabelle travopti.regions_byt: ~141 rows (ungefähr) DELETE FROM `regions_byt`; @@ -323,7 +323,7 @@ CREATE TABLE IF NOT EXISTS `regions_trivago` ( UNIQUE KEY `region_id_year_month` (`region_id`,`year`,`month`), KEY `FK_regions_trivago_regions` (`region_id`), CONSTRAINT `FK_regions_trivago_regions` FOREIGN KEY (`region_id`) REFERENCES `regions` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=1278 DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci; +) ENGINE=InnoDB AUTO_INCREMENT=1278 DEFAULT CHARSET=utf8mb4; -- Exportiere Daten aus Tabelle travopti.regions_trivago: ~940 rows (ungefähr) DELETE FROM `regions_trivago`; @@ -1289,7 +1289,7 @@ CREATE TABLE IF NOT EXISTS `region_climate` ( UNIQUE KEY `region_id_year_month` (`region_id`,`year`,`month`), KEY `FK_region_climate_regions` (`region_id`), CONSTRAINT `FK_region_climate_regions` FOREIGN KEY (`region_id`) REFERENCES `regions` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- Exportiere Daten aus Tabelle travopti.region_climate: ~0 rows (ungefähr) DELETE FROM `region_climate`; @@ -1300,14 +1300,14 @@ DELETE FROM `region_climate`; DROP TABLE IF EXISTS `search_presets`; CREATE TABLE IF NOT EXISTS `search_presets` ( `id` int(11) NOT NULL AUTO_INCREMENT, - `parameter` varchar(255) COLLATE latin1_german1_ci NOT NULL, - `name` varchar(255) COLLATE latin1_german1_ci NOT NULL, + `parameter` varchar(255) NOT NULL, + `name` varchar(255) NOT NULL, `value_1` int(11) NOT NULL, `value_2` int(11) DEFAULT NULL, `created_at` timestamp NOT NULL DEFAULT current_timestamp(), `updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- Exportiere Daten aus Tabelle travopti.search_presets: ~0 rows (ungefähr) DELETE FROM `search_presets`; @@ -1318,11 +1318,11 @@ DELETE FROM `search_presets`; DROP TABLE IF EXISTS `search_tags`; CREATE TABLE IF NOT EXISTS `search_tags` ( `id` int(11) NOT NULL AUTO_INCREMENT, - `searchtag` varchar(255) COLLATE latin1_german1_ci NOT NULL, + `searchtag` varchar(255) NOT NULL, `created_at` timestamp NOT NULL DEFAULT current_timestamp(), `updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- Exportiere Daten aus Tabelle travopti.search_tags: ~0 rows (ungefähr) DELETE FROM `search_tags`; @@ -1343,7 +1343,7 @@ CREATE TABLE IF NOT EXISTS `user_feedback` ( KEY `FK_user_feedback_search_tags` (`tag_id`), CONSTRAINT `FK_user_feedback_regions` FOREIGN KEY (`region_id`) REFERENCES `regions` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `FK_user_feedback_search_tags` FOREIGN KEY (`tag_id`) REFERENCES `search_tags` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- Exportiere Daten aus Tabelle travopti.user_feedback: ~0 rows (ungefähr) DELETE FROM `user_feedback`; diff --git a/backend/.env b/backend/.env new file mode 100644 index 0000000..afb8d07 --- /dev/null +++ b/backend/.env @@ -0,0 +1,5 @@ +METEOSTAT_API_KEY=LMlDskju +DB_HOST=192.168.43.52 +DB_USER=root +DB_PASSWORD=devtest +DB_PORT=3307 \ No newline at end of file diff --git a/backend/climate.js b/backend/climate.js new file mode 100644 index 0000000..e69de29 diff --git a/backend/dbsql.js b/backend/dbsql.js new file mode 100644 index 0000000..d40689e --- /dev/null +++ b/backend/dbsql.js @@ -0,0 +1,13 @@ +var mysql = require('mysql'); +require('dotenv').config() +console.log(process.env); + +var connection = mysql.createConnection({ + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + port: process.env.DB_PORT +}); + +connection.connect(); + \ No newline at end of file diff --git a/backend/package-lock.json b/backend/package-lock.json index fd25ecb..a4a6d28 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -18,6 +18,11 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "bignumber.js": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", + "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" + }, "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -63,6 +68,11 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -81,6 +91,11 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -192,6 +207,11 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -230,6 +250,17 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "mysql": { + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", + "integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==", + "requires": { + "bignumber.js": "9.0.0", + "readable-stream": "2.3.7", + "safe-buffer": "5.1.2", + "sqlstring": "2.3.1" + } + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -253,6 +284,11 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -283,6 +319,20 @@ "unpipe": "1.0.0" } }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -336,11 +386,24 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, + "sqlstring": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", + "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=" + }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", @@ -360,6 +423,11 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", diff --git a/backend/package.json b/backend/package.json index 87a7cb8..4ff4bc6 100644 --- a/backend/package.json +++ b/backend/package.json @@ -9,6 +9,8 @@ "author": "", "license": "ISC", "dependencies": { - "express": "^4.17.1" + "dotenv": "^8.2.0", + "express": "^4.17.1", + "mysql": "^2.18.1" } } From 1f17d761510e1e1236e1e90174f771f3aa589c6b Mon Sep 17 00:00:00 2001 From: Timo Volkmann Date: Fri, 12 Jun 2020 08:32:01 +0200 Subject: [PATCH 03/20] climate api implementation 1 --- Scripts/setup.sql | 34 +- backend/.env | 4 +- backend/climate.js | 64 +++ backend/dbsql.js | 68 ++- backend/package-lock.json | 1129 +++++++++++++++++++++++++++++++++++++ backend/package.json | 7 +- 6 files changed, 1282 insertions(+), 24 deletions(-) diff --git a/Scripts/setup.sql b/Scripts/setup.sql index 5984893..45c5ffe 100644 --- a/Scripts/setup.sql +++ b/Scripts/setup.sql @@ -91,30 +91,30 @@ CREATE TABLE IF NOT EXISTS `regions` ( DELETE FROM `regions`; /*!40000 ALTER TABLE `regions` DISABLE KEYS */; INSERT INTO `regions` (`id`, `region`, `country_id`, `meteostat_id`, `lon`, `lat`, `created_at`, `updated_at`) VALUES - (1, 'Buenos Aires', 1, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (2, 'Melbourne', 2, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (3, 'Sydney', 2, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (4, 'Bruessel', 3, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (5, 'Rio de Janei..', 4, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (6, 'Sao Paolo', 4, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (7, 'Toronto', 5, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (8, 'Santiago de ..', 6, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (9, 'Peking', 7, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (10, 'Shanghai', 7, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (11, 'Bogota', 8, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (12, 'Kairo', 9, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (13, 'London', 10, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (1, 'Buenos Aires', 1, '87585', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (2, 'Melbourne', 2, '94866', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (3, 'Sydney', 2, '94767', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (4, 'Bruessel', 3, '06458', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (5, 'Rio de Janeiro', 4, '83755', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (6, 'Sao Paolo', 4, '83780', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (7, 'Toronto', 5, '71624', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (8, 'Santiago de Chile', 6, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (9, 'Peking', 7, '54511', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (10, 'Shanghai', 7, '58362', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (11, 'Bogota', 8, '80222', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (12, 'Kairo', 9, '62366', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (13, 'London', 10, '03772', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (14, 'Paris', 11, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (15, 'Berlin', 12, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (16, 'Athen', 13, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (17, 'Hongkong', 14, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (18, 'Reykjavik', 15, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (18, 'Reykjavik', 15, '04030', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (19, 'Delhi', 16, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (20, 'Mumbai', 16, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (20, 'Mumbai', 16, '43003', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (21, 'Dublin', 17, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (22, 'Rom', 18, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (23, 'Tokio', 19, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (24, 'Kuala Lumpur', 20, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (24, 'Kuala Lumpur', 20, '48647', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (25, 'Mexico City', 21, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (26, 'Marrakesch', 22, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (27, 'Amsterdam', 23, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), @@ -136,7 +136,7 @@ INSERT INTO `regions` (`id`, `region`, `country_id`, `meteostat_id`, `lon`, `lat (43, 'Las Vegas', 36, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (44, 'Miami', 36, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (45, 'New York', 36, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (46, 'San Francis ..', 36, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (46, 'San Francisco', 36, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (47, 'Dubai', 37, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'); /*!40000 ALTER TABLE `regions` ENABLE KEYS */; diff --git a/backend/.env b/backend/.env index afb8d07..b90e137 100644 --- a/backend/.env +++ b/backend/.env @@ -1,5 +1,5 @@ METEOSTAT_API_KEY=LMlDskju -DB_HOST=192.168.43.52 +DB_HOST=localhost DB_USER=root DB_PASSWORD=devtest -DB_PORT=3307 \ No newline at end of file +DB_PORT=3306 \ No newline at end of file diff --git a/backend/climate.js b/backend/climate.js index e69de29..ab6a290 100644 --- a/backend/climate.js +++ b/backend/climate.js @@ -0,0 +1,64 @@ +require('dotenv').config() +const mysql = require('mysql2/promise'); +const axios = require('axios') + +const startDate = '2018-01' +const endDate = '2018-12' + +async function main() { + const connection = await mysql.createConnection({ + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + port: process.env.DB_PORT, + database: 'travopti' + }); + const [result, fields] = await connection.execute(`SELECT * FROM regions WHERE meteostat_id IS NOT NULL`) + + Promise.all(result.map(res => { + console.log(res) + return axios.get(`https://api.meteostat.net/v1/climate/normals?station=${res.meteostat_id}&key=${process.env.METEOSTAT_API_KEY}`) + })).then(responses => { + responses.forEach(response => { + //console.log(response.data) + }) + //console.log(responses[0]) + }) + + connection.end(); +} + +async function createClimateObject(src) { + let response = await axios.get(`https://api.meteostat.net/v1/climate/normals?station=${res.meteostat_id}&key=${process.env.METEOSTAT_API_KEY}`) + let temperatures = response.data.data.temperature + let precipitations = response.data.data.precipitation + let sunshines = response.data.data.sunshine + + console.log(Object.entries(temperatures)[0]) + let results = [] + for (let index = 1; index <= 12; index++) { + //const element = array[index]; + let result = { + region: src.region, + region_id: src.id, + month: index, + temperature: Object.values(temperatures)[index], + precipitation: Object.values(precipitations)[index], + sunshine: Object.values(sunshines)[index], + } + results.push(result) + } + return results +} + +async function createClimateObjectFromLastYear(src) { + let response = await axios.get(`https://api.meteostat.net/v1/history/monthly?station=${res.meteostat_id}&start=${startDate}&end=${endDate}&key=${process.env.METEOSTAT_API_KEY}`) + + + let result = { + region_id: src.id, + + } +} + +main() \ No newline at end of file diff --git a/backend/dbsql.js b/backend/dbsql.js index d40689e..a5d7b72 100644 --- a/backend/dbsql.js +++ b/backend/dbsql.js @@ -1,13 +1,73 @@ var mysql = require('mysql'); require('dotenv').config() -console.log(process.env); var connection = mysql.createConnection({ host: process.env.DB_HOST, user: process.env.DB_USER, password: process.env.DB_PASSWORD, - port: process.env.DB_PORT + port: process.env.DB_PORT, + database: 'travopti' }); -connection.connect(); - \ No newline at end of file +// var pool = mysql.createPool({ +// connectionLimit: 10, +// host: process.env.DB_HOST, +// user: process.env.DB_USER, +// password: process.env.DB_PASSWORD, +// port: process.env.DB_PORT, +// database: 'travopti' +// }); + +// let travoptidb = {} +// travoptidb.all = () => { +// return new Promise((resolve, reject) => { +// pool.query(`SELECT * FROM regions`, (err, results) => { +// if (err) { +// return reject(err) +// } +// return resolve(results) +// }) +// }) +// } + +connection.connect((err) => { + if (err) throw err; + console.log('Database connected!') +}); + + +exports.getRegions = () => { + let sql = `SELECT * FROM regions`; + console.log(connection.state) + if (connection.state === 'disconnected') { + setTimeout(() => console.log('waiting...'), 1000); + } + console.log('executed') + let res = {} + connection.query(sql, (error, results, fields) => { + if (error) { + return console.error(error.message); + } + console.log('innercallback(1)') + res = results[0] + }); + console.log('outsidecallback(2)') + return res; +} + +exports.getBYTdata = () => { + connection.query(`SELECT * FROM regions_byt`, (error, results, fields) => { + if (error) { + return console.error(error.message); + } + console.log(results[0]) + nres = results.map((obj) => { + return obj.region + }) + //console.log(nres); + }); +} + +exports.end = () => connection.end(); + +// module.exports = connection; diff --git a/backend/package-lock.json b/backend/package-lock.json index a4a6d28..9a19ff0 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -4,6 +4,33 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -13,16 +40,89 @@ "negotiator": "0.6.2" } }, + "ansi-align": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", + "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "dev": true, + "requires": { + "string-width": "^3.0.0" + }, + "dependencies": { + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "ansicolors": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", + "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=" + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "axios": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "requires": { + "follow-redirects": "1.5.10" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, "bignumber.js": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" }, + "binary-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", + "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", + "dev": true + }, "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -40,11 +140,192 @@ "type-is": "~1.6.17" } }, + "boxen": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", + "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", + "dev": true, + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "cli-boxes": "^2.2.0", + "string-width": "^4.1.0", + "term-size": "^2.1.0", + "type-fest": "^0.8.1", + "widest-line": "^3.1.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + } + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "cardinal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", + "integrity": "sha1-fMEFXYItISlU0HsIXeolHMe8VQU=", + "requires": { + "ansicolors": "~0.3.2", + "redeyed": "~2.1.0" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "chokidar": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz", + "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.4.0" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "cli-boxes": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.0.tgz", + "integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==", + "dev": true + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + } + }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -73,6 +354,12 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -81,6 +368,32 @@ "ms": "2.0.0" } }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true + }, + "denque": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", + "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -91,26 +404,67 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "dot-prop": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", + "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, "dotenv": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "dev": true + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -153,6 +507,15 @@ "vary": "~1.1.2" } }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -167,6 +530,24 @@ "unpipe": "~1.0.0" } }, + "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" + } + } + } + }, "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", @@ -177,6 +558,91 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + }, + "generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "requires": { + "is-property": "^1.0.2" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "global-dirs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", + "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==", + "dev": true, + "requires": { + "ini": "^1.3.5" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "dev": true + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -197,21 +663,195 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", + "dev": true + }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-installed-globally": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", + "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", + "dev": true, + "requires": { + "global-dirs": "^2.0.1", + "is-path-inside": "^3.0.1" + } + }, + "is-npm": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", + "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", + "dev": true + }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", + "dev": true + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dev": true, + "requires": { + "json-buffer": "3.0.0" + } + }, + "latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "dev": true, + "requires": { + "package-json": "^6.3.0" + } + }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -245,6 +885,27 @@ "mime-db": "1.44.0" } }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -261,11 +922,117 @@ "sqlstring": "2.3.1" } }, + "mysql2": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.1.0.tgz", + "integrity": "sha512-9kGVyi930rG2KaHrz3sHwtc6K+GY9d8wWk1XRSYxQiunvGcn4DwuZxOwmK11ftuhhwrYDwGx9Ta4VBwznJn36A==", + "requires": { + "cardinal": "^2.1.1", + "denque": "^1.4.1", + "generate-function": "^2.3.1", + "iconv-lite": "^0.5.0", + "long": "^4.0.0", + "lru-cache": "^5.1.1", + "named-placeholders": "^1.1.2", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.1" + }, + "dependencies": { + "iconv-lite": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", + "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "named-placeholders": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.2.tgz", + "integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==", + "requires": { + "lru-cache": "^4.1.3" + }, + "dependencies": { + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + } + } + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, + "nodemon": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.4.tgz", + "integrity": "sha512-Ltced+hIfTmaS28Zjv1BM552oQ3dbwPqI4+zI0SLgq+wpJhSyqgYude/aZa/3i31VCQWMfXJVxvu86abcam3uQ==", + "dev": true, + "requires": { + "chokidar": "^3.2.2", + "debug": "^3.2.6", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.0.4", + "pstree.remy": "^1.1.7", + "semver": "^5.7.1", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.2", + "update-notifier": "^4.0.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "dev": true + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -274,6 +1041,41 @@ "ee-first": "1.1.1" } }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "dev": true + }, + "package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "dev": true, + "requires": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -284,6 +1086,18 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -298,6 +1112,36 @@ "ipaddr.js": "1.9.1" } }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pupa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.0.1.tgz", + "integrity": "sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==", + "dev": true, + "requires": { + "escape-goat": "^2.0.0" + } + }, "qs": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", @@ -319,6 +1163,18 @@ "unpipe": "1.0.0" } }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -333,6 +1189,50 @@ "util-deprecate": "~1.0.1" } }, + "readdirp": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", + "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "redeyed": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", + "integrity": "sha1-iYS1gV2ZyyIEacme7v/jiRPmzAs=", + "requires": { + "esprima": "~4.0.0" + } + }, + "registry-auth-token": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.1.1.tgz", + "integrity": "sha512-9bKS7nTl9+/A1s7tnPeGrUpRcVY+LUh7bfFgzpndALdPfXQBfQV77rQVtqgUV3ti4vc/Ik81Ex8UJDWDQ12zQA==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, + "registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -343,6 +1243,29 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dev": true, + "requires": { + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "send": { "version": "0.17.1", "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", @@ -370,6 +1293,11 @@ } } }, + "seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4=" + }, "serve-static": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", @@ -386,6 +1314,12 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, "sqlstring": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", @@ -396,6 +1330,46 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -404,11 +1378,71 @@ "safe-buffer": "~5.1.0" } }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "term-size": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz", + "integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==", + "dev": true + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, + "touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "requires": { + "nopt": "~1.0.10" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -418,11 +1452,68 @@ "mime-types": "~2.1.24" } }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "undefsafe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", + "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==", + "dev": true, + "requires": { + "debug": "^2.2.0" + } + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "requires": { + "crypto-random-string": "^2.0.0" + } + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "update-notifier": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.0.tgz", + "integrity": "sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew==", + "dev": true, + "requires": { + "boxen": "^4.2.0", + "chalk": "^3.0.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.3.1", + "is-npm": "^4.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.0.0", + "pupa": "^2.0.1", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + } + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dev": true, + "requires": { + "prepend-http": "^2.0.0" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -437,6 +1528,44 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "requires": { + "string-width": "^4.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" } } } diff --git a/backend/package.json b/backend/package.json index 4ff4bc6..9ffa857 100644 --- a/backend/package.json +++ b/backend/package.json @@ -9,8 +9,13 @@ "author": "", "license": "ISC", "dependencies": { + "axios": "^0.19.2", "dotenv": "^8.2.0", "express": "^4.17.1", - "mysql": "^2.18.1" + "mysql": "^2.18.1", + "mysql2": "^2.1.0" + }, + "devDependencies": { + "nodemon": "^2.0.4" } } From 75bb43a3bd3bca25696ac7e3d7ba845179469e77 Mon Sep 17 00:00:00 2001 From: Timo Volkmann Date: Fri, 12 Jun 2020 10:48:59 +0200 Subject: [PATCH 04/20] first draft for database population (climate) --- Scripts/setup.sql | 6 +++-- backend/climate.js | 58 ++++++++++++++++++++++++++++++---------------- 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/Scripts/setup.sql b/Scripts/setup.sql index 45c5ffe..12b48c7 100644 --- a/Scripts/setup.sql +++ b/Scripts/setup.sql @@ -110,7 +110,7 @@ INSERT INTO `regions` (`id`, `region`, `country_id`, `meteostat_id`, `lon`, `lat (17, 'Hongkong', 14, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (18, 'Reykjavik', 15, '04030', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (19, 'Delhi', 16, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (20, 'Mumbai', 16, '43003', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (20, 'Mumbai', 16, '43002', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (21, 'Dublin', 17, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (22, 'Rom', 18, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (23, 'Tokio', 19, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), @@ -1278,7 +1278,9 @@ CREATE TABLE IF NOT EXISTS `region_climate` ( `region_id` int(11) NOT NULL, `year` int(11) NOT NULL, `month` int(11) NOT NULL, - `temperature` float DEFAULT NULL, + `temperature_mean` float DEFAULT NULL, + `temperature_mean_min` float DEFAULT NULL, + `temperature_mean_max` float DEFAULT NULL, `percipitation` float DEFAULT NULL, `raindays` int(11) DEFAULT NULL, `sunshine` float DEFAULT NULL, diff --git a/backend/climate.js b/backend/climate.js index ab6a290..41ea1e9 100644 --- a/backend/climate.js +++ b/backend/climate.js @@ -15,38 +15,47 @@ async function main() { }); const [result, fields] = await connection.execute(`SELECT * FROM regions WHERE meteostat_id IS NOT NULL`) - Promise.all(result.map(res => { - console.log(res) - return axios.get(`https://api.meteostat.net/v1/climate/normals?station=${res.meteostat_id}&key=${process.env.METEOSTAT_API_KEY}`) - })).then(responses => { - responses.forEach(response => { - //console.log(response.data) - }) - //console.log(responses[0]) - }) + // Promise.all(result.map(res => { + // return createClimateObject(res) + // })).then(results => { + // let flat = results.reduce((total, element) => total.concat(element), []) + // console.log(flat) + // }) + let temp = await Promise.all(result.map(x => createClimateObject(x))) + let finalresults = temp.reduce((total, element) => total.concat(element), []) + + await writeToDatabase(connection, finalresults) + connection.end(); } async function createClimateObject(src) { - let response = await axios.get(`https://api.meteostat.net/v1/climate/normals?station=${res.meteostat_id}&key=${process.env.METEOSTAT_API_KEY}`) - let temperatures = response.data.data.temperature - let precipitations = response.data.data.precipitation - let sunshines = response.data.data.sunshine - - console.log(Object.entries(temperatures)[0]) + let response + try { + response = await axios.get(`https://api.meteostat.net/v1/climate/normals?station=${src.meteostat_id}&key=${process.env.METEOSTAT_API_KEY}`) + } catch (error) { + console.log("skipping: couldn't find results for following region: ") + console.log(src.region + " with meteostat_id " + src.meteostat_id) + return [] + } + if (!response.data.data) { + console.log("skipping: no data for station meteostat_id " + src.meteostat_id + " (" + src.region + ")") + return [] + } let results = [] for (let index = 1; index <= 12; index++) { - //const element = array[index]; let result = { region: src.region, region_id: src.id, month: index, - temperature: Object.values(temperatures)[index], - precipitation: Object.values(precipitations)[index], - sunshine: Object.values(sunshines)[index], + temperature: Object.values(response.data.data.temperature)[index - 1] ? Object.values(response.data.data.temperature)[index - 1] : null, + temperature_min: Object.values(response.data.data.temperature_min)[index - 1] ? Object.values(response.data.data.temperature_min)[index - 1] : null, + temperature_max: Object.values(response.data.data.temperature_max)[index - 1] ? Object.values(response.data.data.temperature_max)[index - 1] : null, + precipitation: Object.values(response.data.data.precipitation)[index - 1] ? Object.values(response.data.data.precipitation)[index - 1] : null, + sunshine: Object.values(response.data.data.sunshine)[index - 1] ? Object.values(response.data.data.sunshine)[index - 1] : null, } - results.push(result) + results.push(result) } return results } @@ -61,4 +70,13 @@ async function createClimateObjectFromLastYear(src) { } } +async function writeToDatabase(dbConnection, src) { + src.forEach(async (element) => { + //console.log(element) + await dbConnection.execute(` + INSERT INTO region_climate (region_id, year, month, temperature_mean, temperature_mean_min, temperature_mean_max, percipitation, sunshine) + VALUES (${element.region_id}, 0, ${element.month}, ${element.temperature}, ${element.temperature_min}, ${element.temperature_max}, ${element.precipitation}, ${element.sunshine});`) + }); +} + main() \ No newline at end of file From 84b9a8abc978c27d2fae03ecf0bf25109e57633c Mon Sep 17 00:00:00 2001 From: Timo Volkmann Date: Fri, 12 Jun 2020 11:30:40 +0200 Subject: [PATCH 05/20] finished climate importer --- backend/climate.js | 74 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 14 deletions(-) diff --git a/backend/climate.js b/backend/climate.js index 41ea1e9..d45f167 100644 --- a/backend/climate.js +++ b/backend/climate.js @@ -2,8 +2,8 @@ require('dotenv').config() const mysql = require('mysql2/promise'); const axios = require('axios') -const startDate = '2018-01' -const endDate = '2018-12' +const rangeStartDate = '2018-01' +const rangeEndDate = '2018-12' async function main() { const connection = await mysql.createConnection({ @@ -23,10 +23,15 @@ async function main() { // }) let temp = await Promise.all(result.map(x => createClimateObject(x))) - let finalresults = temp.reduce((total, element) => total.concat(element), []) + let final = temp.reduce((total, element) => total.concat(element), []) - await writeToDatabase(connection, finalresults) + //await writeToDatabase(connection, final) + let temp2 = await Promise.all(result.map(x => createClimateObjectFrom(x))) + let final2 = temp2.reduce((total, element) => total.concat(element), []) + + await writeToDatabase(connection, final2) + connection.end(); } @@ -60,22 +65,63 @@ async function createClimateObject(src) { return results } -async function createClimateObjectFromLastYear(src) { - let response = await axios.get(`https://api.meteostat.net/v1/history/monthly?station=${res.meteostat_id}&start=${startDate}&end=${endDate}&key=${process.env.METEOSTAT_API_KEY}`) - - - let result = { - region_id: src.id, - +async function createClimateObjectFrom(src, startDate = '2010-01', endDate = '2018-12') { + let response + try { + response = await axios.get(`https://api.meteostat.net/v1/history/monthly?station=${src.meteostat_id}&start=${startDate}&end=${endDate}&key=${process.env.METEOSTAT_API_KEY}`) + } catch (error) { + console.log("skipping createClimateObjectFrom: couldn't find results for following region: ") + console.log(src.region + " with meteostat_id " + src.meteostat_id) + console.log(error) + return [] } + if (!response.data.data) { + console.log("skipping: no data for station meteostat_id " + src.meteostat_id + " (" + src.region + ")") + return [] + } + let results = response.data.data.map(element => { + let result = { + region: src.region, + region_id: src.id, + year: element.month.split("-")[0], + month: element.month.split("-")[1], + temperature: element.temperature_mean, + temperature_min: element.temperature_mean_min, + temperature_max: element.temperature_mean_max, + precipitation: element.precipitation, + raindays: element.raindays, + sunshine: element.sunshine, + humidity: element.humidity ? element.humidity : null + } + //console.log(result) + return result + }) + return results } async function writeToDatabase(dbConnection, src) { src.forEach(async (element) => { //console.log(element) - await dbConnection.execute(` - INSERT INTO region_climate (region_id, year, month, temperature_mean, temperature_mean_min, temperature_mean_max, percipitation, sunshine) - VALUES (${element.region_id}, 0, ${element.month}, ${element.temperature}, ${element.temperature_min}, ${element.temperature_max}, ${element.precipitation}, ${element.sunshine});`) + try { + if (!element.year) { + await dbConnection.execute(` + INSERT INTO region_climate (region_id, year, month, temperature_mean, temperature_mean_min, temperature_mean_max, percipitation, sunshine) + VALUES (${element.region_id}, 0, ${element.month}, ${element.temperature}, ${element.temperature_min}, ${element.temperature_max}, ${element.precipitation}, ${element.sunshine});`) + } else { + await dbConnection.execute(` + INSERT INTO region_climate (region_id, year, month, temperature_mean, temperature_mean_min, temperature_mean_max, percipitation, sunshine, humidity, raindays) + VALUES (${element.region_id}, ${element.year}, ${element.month}, ${element.temperature}, ${element.temperature_min}, ${element.temperature_max}, ${element.precipitation}, ${element.sunshine}, ${element.humidity}, ${element.raindays});`) + } + } catch (error) { + if (error.code !== 'ER_DUP_ENTRY') { + console.log("element which causes problems: ") + console.log(element) + console.log("query which causes problems: ") + console.log(error) + } else { + console.log(element.region + ": " + error.sqlMessage) + } + } }); } From e56634409805ca896d9bfb3f6b5bf096ee516ec7 Mon Sep 17 00:00:00 2001 From: Timo Volkmann Date: Fri, 12 Jun 2020 18:43:09 +0200 Subject: [PATCH 06/20] first score search draft working --- backend/app.js | 151 +++++++++++++++++++++++++++++++++++++++++-- backend/climate.js | 9 +-- backend/dbsql.js | 73 --------------------- backend/mysql.js | 84 ++++++++++++++++++++++++ backend/package.json | 6 +- backend/score.js | 17 +++-- 6 files changed, 243 insertions(+), 97 deletions(-) delete mode 100644 backend/dbsql.js create mode 100644 backend/mysql.js diff --git a/backend/app.js b/backend/app.js index e9f4a02..c15d23d 100644 --- a/backend/app.js +++ b/backend/app.js @@ -1,8 +1,11 @@ var express = require('express') var sampledata = require('./sampledata') +var db = require('./mysql') +var score = require('./score') const app = express() const port = 3000 +const multiplier_temp = 5 const sampleRegions = [ { @@ -20,6 +23,11 @@ const samplePresets = [ } ] +// db.connect((err) => { +// if (err) throw err; +// console.log('Database connected!') +// }); + app.get('/', (req, res) => res.send('Hello Timo!')) app.get('/v1/regions', (req, res) => res.json(sampleRegions)) app.get('/v1/presets', (req, res) => res.json(samplePresets)) @@ -32,10 +40,145 @@ app.get('/v1/search', (req, res) => { query: req.query, headers: req.headers } + + console.log('log params') + console.log(req.query.from) + console.log(req.query.to) + console.log(req.query.temperature) - response.data = sampledata.search_response_model - - res.json(response) + search(req.query.from, req.query.to, { temperature: req.query.temperature }).then(searchResults => { + response.data = searchResults + res.json(response) + }) }) -app.listen(port, () => console.log(`Travopti backend listening at http://localhost:${port}`)) \ No newline at end of file +app.listen(port, () => console.log(`Travopti backend listening at http://localhost:${port}`)) + +async function search(from, to, queries) { + console.log('search') + // validate regex: YYYY-MM-DD + let re = /([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/i; + if (!from.match(re) || !to.match(re)) throw new Error("invalid parameter: " + from + " " + to) + + // -- Prepare search -- + // calculate average if traveldates are in more than one month + let monthFrom = Number(from.split("-")[1]) + let monthTo = Number(to.split("-")[1]) + let values = [] + let virtDays = 0 + if (monthFrom === monthTo) { + let element = { + month: monthFrom, + days: Number(to.split("-")[2]) - Number(from.split("-")[2]) + } + virtDays = element.days + values.push(element) + } else { + for (let index = monthFrom; index <= monthTo; index++) { + let element = {} + if (index === monthFrom) { + element = { + month: index, + days: 31 - Number(from.split("-")[2]) + } + } else if (index === monthTo) { + element = { + month: index, + days: Number(to.split("-")[2]) + } + } else { + element = { + month: index, + days: 30 + } + } + virtDays += element.days + values.push(element) + } + } + // retrieve data from database + const minMax = await getClimateMinMax() + const data = await Promise.all(values.map(async (val) => { + return await getAllClimatePerMonth(val.month) + })) + console.log("data from getAllClimatePerMonth") + //console.log(data) + // calculate score + // TODO: calculate score for all months + let scores_temp + if (queries.temperature) { + scores_temp = calculateTempScores(data[0], queries.temperature.split(',')[0], queries.temperature.split(',')[1], minMax) + } + // TODO: calculate ratio + return { TODO___temperature_scores: scores_temp } +} + +function calculateTempScores(regionDataRows, searchLowParam, searchMaxParam, minMax) { + console.log('calculateTempScores') + console.log(searchLowParam) + console.log(searchMaxParam) + console.log(minMax) + //console.log(regionDataRows) + let result = regionDataRows.map(x => { + console.log(x.temperature_mean) + return { + region_id: x.region_id, + value: x.temperature_mean, + score: Math.round(score.calculateScoreRange(minMax.min.temperature_mean, minMax.max.temperature_mean, multiplier_temp, x.temperature_mean, searchLowParam, searchMaxParam)*100)/100 + } + + }) + return result +} + +async function getClimateMinMax() { + console.log('getClimateMinMax') + const sqlMin = `SELECT + MIN(temperature_mean) AS temperature_mean, + MIN(temperature_mean_min) AS temperature_mean_min, + MIN(temperature_mean_max) AS temperature_mean_max, + MIN(percipitation) AS percipitation, + MIN(sunshine) AS sunshine + FROM region_climate` + const sqlMax = `SELECT + MAX(temperature_mean) AS temperature_mean, + MAX(temperature_mean_min) AS temperature_mean_min, + MAX(temperature_mean_max) AS temperature_mean_max, + MAX(percipitation) AS percipitation, + MAX(sunshine) AS sunshine + FROM region_climate` + const [qResMin, qResMax] = await Promise.all([getQueryRows(sqlMin), getQueryRows(sqlMax)]) + console.log(qResMin) + return { min: qResMin[0], max: qResMax[0] } +} + +async function getQueryRows(sql) { + console.log('getQueryRows') + const [rows, fields] = await db.execute(sql) + return rows +} + +async function getRegionIdsWithMeteostatData() { + console.log('getRegionIdsWithMeteostatData') + //return await getQueryRows(`SELECT * FROM regions WHERE meteostat_id IS NOT NULL`) + return await getQueryRows(`SELECT region_id FROM region_climate GROUP BY region_id`) +} + +async function getClimatePerRegionAndMonth(regionId, month) { + console.log('getClimatePerRegionAndMonth') + const sql = `SELECT region_id, AVG(temperature_mean), AVG(temperature_mean_min), AVG(temperature_mean_max), AVG(percipitation), AVG(sunshine) FROM region_climate WHERE month = ${month} AND region_id = ${regionId}` + return await getQueryRows(sql) +} + +async function getAllClimatePerMonth(month) { + console.log('getAllClimatePerMonth') + const sql = `SELECT + region_id, + ROUND(AVG(temperature_mean), 1) AS temperature_mean, + ROUND(AVG(temperature_mean_min), 1) AS temperature_mean_min, + ROUND(AVG(temperature_mean_max), 1) AS temperature_mean_max, + ROUND(AVG(percipitation), 1) AS percipitation, + ROUND(AVG(sunshine), 1) AS sunshine + FROM region_climate WHERE month = ${month} GROUP BY region_id` + return await getQueryRows(sql) +} \ No newline at end of file diff --git a/backend/climate.js b/backend/climate.js index d45f167..2b2c5ea 100644 --- a/backend/climate.js +++ b/backend/climate.js @@ -15,17 +15,10 @@ async function main() { }); const [result, fields] = await connection.execute(`SELECT * FROM regions WHERE meteostat_id IS NOT NULL`) - // Promise.all(result.map(res => { - // return createClimateObject(res) - // })).then(results => { - // let flat = results.reduce((total, element) => total.concat(element), []) - // console.log(flat) - // }) - let temp = await Promise.all(result.map(x => createClimateObject(x))) let final = temp.reduce((total, element) => total.concat(element), []) - //await writeToDatabase(connection, final) + await writeToDatabase(connection, final) let temp2 = await Promise.all(result.map(x => createClimateObjectFrom(x))) let final2 = temp2.reduce((total, element) => total.concat(element), []) diff --git a/backend/dbsql.js b/backend/dbsql.js deleted file mode 100644 index a5d7b72..0000000 --- a/backend/dbsql.js +++ /dev/null @@ -1,73 +0,0 @@ -var mysql = require('mysql'); -require('dotenv').config() - -var connection = mysql.createConnection({ - host: process.env.DB_HOST, - user: process.env.DB_USER, - password: process.env.DB_PASSWORD, - port: process.env.DB_PORT, - database: 'travopti' -}); - -// var pool = mysql.createPool({ -// connectionLimit: 10, -// host: process.env.DB_HOST, -// user: process.env.DB_USER, -// password: process.env.DB_PASSWORD, -// port: process.env.DB_PORT, -// database: 'travopti' -// }); - -// let travoptidb = {} -// travoptidb.all = () => { -// return new Promise((resolve, reject) => { -// pool.query(`SELECT * FROM regions`, (err, results) => { -// if (err) { -// return reject(err) -// } -// return resolve(results) -// }) -// }) -// } - -connection.connect((err) => { - if (err) throw err; - console.log('Database connected!') -}); - - -exports.getRegions = () => { - let sql = `SELECT * FROM regions`; - console.log(connection.state) - if (connection.state === 'disconnected') { - setTimeout(() => console.log('waiting...'), 1000); - } - console.log('executed') - let res = {} - connection.query(sql, (error, results, fields) => { - if (error) { - return console.error(error.message); - } - console.log('innercallback(1)') - res = results[0] - }); - console.log('outsidecallback(2)') - return res; -} - -exports.getBYTdata = () => { - connection.query(`SELECT * FROM regions_byt`, (error, results, fields) => { - if (error) { - return console.error(error.message); - } - console.log(results[0]) - nres = results.map((obj) => { - return obj.region - }) - //console.log(nres); - }); -} - -exports.end = () => connection.end(); - -// module.exports = connection; diff --git a/backend/mysql.js b/backend/mysql.js new file mode 100644 index 0000000..c7114d5 --- /dev/null +++ b/backend/mysql.js @@ -0,0 +1,84 @@ +var mysql = require('mysql2/promise'); +require('dotenv').config() + +// var connection = mysql.createConnection({ +// host: process.env.DB_HOST, +// user: process.env.DB_USER, +// password: process.env.DB_PASSWORD, +// port: process.env.DB_PORT, +// database: 'travopti' +// }); + +const pool = mysql.createPool({ + connectionLimit: 10, + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + port: process.env.DB_PORT, + database: 'travopti' +}); + +pool.getConnection() + .then(function (connection) { + console.log(`Connected to database: ${process.env.DB_HOST}`); + //pool.releaseConnection(connection) + }) + .catch(function (error) { + console.error(error.message); + }); + +module.exports = pool; + +// let travoptidb = {} +// travoptidb.all = () => { +// return new Promise((resolve, reject) => { +// pool.query(`SELECT * FROM regions`, (err, results) => { +// if (err) { +// return reject(err) +// } +// return resolve(results) +// }) +// }) +// } + +// connection.connect((err) => { +// if (err) throw err; +// console.log('Database connected!') +// }); + + +// exports.getRegions = () => { +// let sql = `SELECT * FROM regions`; +// console.log(connection.state) +// if (connection.state === 'disconnected') { +// setTimeout(() => console.log('waiting...'), 1000); +// } +// console.log('executed') +// let res = {} +// connection.query(sql, (error, results, fields) => { +// if (error) { +// return console.error(error.message); +// } +// console.log('innercallback(1)') +// res = results[0] +// }); +// console.log('outsidecallback(2)') +// return res; +// } + +// exports.getBYTdata = () => { +// connection.query(`SELECT * FROM regions_byt`, (error, results, fields) => { +// if (error) { +// return console.error(error.message); +// } +// console.log(results[0]) +// nres = results.map((obj) => { +// return obj.region +// }) +// //console.log(nres); +// }); +// } + +// exports.end = () => connection.end(); + +// module.exports = connection; diff --git a/backend/package.json b/backend/package.json index 9ffa857..30dcacc 100644 --- a/backend/package.json +++ b/backend/package.json @@ -3,9 +3,9 @@ "version": "1.0.0", "description": "", "main": "app.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, +"scripts": { + "start": "nodemon ./app.js" +}, "author": "", "license": "ISC", "dependencies": { diff --git a/backend/score.js b/backend/score.js index 4bba82e..feec62a 100644 --- a/backend/score.js +++ b/backend/score.js @@ -8,24 +8,23 @@ const multiplier_temperature = 5; * * @param {...any} scores expects objects which contains score and their weight */ -function calculateAvgScore(...scores) { +exports.calculateAvgScore = (...scores) => { return avgScore = scores.reduce((total, score) => total += score) / scores.length; } -function calculateScoreRange(min, max, multiplier, targetVal, sLowVal, sHighVal) { +exports.calculateScoreRange = (min, max, multiplier, regionVal, sLowVal, sHighVal) => { // return full score when in range - if (targetVal >= sLowVal && targetVal <= sHighVal) return 10; + if (regionVal >= sLowVal && regionVal <= sHighVal) return 10; // choose value with smallest distance - let sVal = Math.abs(targetVal - sLowVal) < Math.abs(targetVal - sHighVal) ? sLowVal : sHighVal; + let sVal = Math.abs(regionVal - sLowVal) < Math.abs(regionVal - sHighVal) ? sLowVal : sHighVal; - return calculateScore(min, max, multiplier, targetVal, sVal); + return this.calculateScore(min, max, multiplier, regionVal, sVal); } -function calculateScore(min, max, multiplier, targetVal, searchVal) { +exports.calculateScore = (min, max, multiplier, regionVal, searchVal) => { - let score = 1 - (Math.abs(searchVal - targetVal) / (max - min) * multiplier); + let score = 1 - (Math.abs(searchVal - regionVal) / (max - min) * multiplier); return score <= 0 ? 0 : score * 10; } -console.log('Calc') -console.log(calculateScoreRange(-15, 45, 5, 24, 15, 22)) \ No newline at end of file +console.log('test score calculation. result: ' + this.calculateScoreRange(-15, 45, 5, 24, 15, 22)) \ No newline at end of file From b5c5abdd4fba5b610d1c88c9e1ac8e7fce3fb210 Mon Sep 17 00:00:00 2001 From: Timo Volkmann Date: Sun, 14 Jun 2020 01:14:04 +0200 Subject: [PATCH 07/20] First working search (climate params, randomizing) --- backend/app.js | 170 +++-- backend/climate.js | 10 +- backend/mysql.js | 10 +- backend/package-lock.json | 5 + backend/package.json | 7 +- backend/score.js | 3 +- backend/transformer-test.json | 1205 +++++++++++++++++++++++++++++++++ backend/transformer.js | 77 +++ 8 files changed, 1426 insertions(+), 61 deletions(-) create mode 100644 backend/transformer-test.json create mode 100644 backend/transformer.js diff --git a/backend/app.js b/backend/app.js index c15d23d..6b83f4e 100644 --- a/backend/app.js +++ b/backend/app.js @@ -1,11 +1,19 @@ var express = require('express') +var _ = require('lodash') var sampledata = require('./sampledata') var db = require('./mysql') var score = require('./score') +var transformer = require('./transformer') const app = express() const port = 3000 -const multiplier_temp = 5 +//const multiplier_temp = 5 +const multiplier = { + temperature_mean: 5, + percipitation: 0.5, + raindays: 3, + sunhours: 2.5, +} const sampleRegions = [ { @@ -23,16 +31,10 @@ const samplePresets = [ } ] -// db.connect((err) => { -// if (err) throw err; -// console.log('Database connected!') -// }); - app.get('/', (req, res) => res.send('Hello Timo!')) app.get('/v1/regions', (req, res) => res.json(sampleRegions)) app.get('/v1/presets', (req, res) => res.json(samplePresets)) app.get('/v1/search', (req, res) => { - // check query params let response = {} response.meta = { @@ -46,7 +48,13 @@ app.get('/v1/search', (req, res) => { console.log(req.query.to) console.log(req.query.temperature) - search(req.query.from, req.query.to, { temperature: req.query.temperature }).then(searchResults => { + let validQueries = {} + if (req.query.temperature) validQueries['temperature_mean'] = req.query.temperature + if (req.query.percipitation) validQueries['percipitation'] = req.query.percipitation + if (req.query.raindays) validQueries['raindays'] = req.query.raindays + if (req.query.sunhours) validQueries['sunhours'] = req.query.sunhours + + search(req.query.from, req.query.to, validQueries).then(searchResults => { response.data = searchResults res.json(response) }) @@ -54,7 +62,23 @@ app.get('/v1/search', (req, res) => { app.listen(port, () => console.log(`Travopti backend listening at http://localhost:${port}`)) + + + async function search(from, to, queries) { + // get Min and Max values for each Parameter + const minMax = await getClimateMinMax() + // randomize if empty queries + if (_.isEmpty(queries)) { + let t = _.random(minMax.min.temperature_mean, minMax.max.temperature_mean-5) + let p = _.random(minMax.min.percipitation, minMax.max.percipitation-50) + let r = _.random(minMax.min.raindays, minMax.max.raindays-5) + let s = _.random(minMax.min.sunhours, minMax.max.sunhours-50) + queries.temperature_mean = `${t},${t + 5}` + queries.percipitation = `${p},${p + 50}` + queries.raindays = `${r},${r + 5}` + queries.sunhours = `${s},${s + 50}` + } console.log('search') // validate regex: YYYY-MM-DD let re = /([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/i; @@ -64,7 +88,7 @@ async function search(from, to, queries) { // calculate average if traveldates are in more than one month let monthFrom = Number(from.split("-")[1]) let monthTo = Number(to.split("-")[1]) - let values = [] + let travelPeriods = [] let virtDays = 0 if (monthFrom === monthTo) { let element = { @@ -72,14 +96,14 @@ async function search(from, to, queries) { days: Number(to.split("-")[2]) - Number(from.split("-")[2]) } virtDays = element.days - values.push(element) + travelPeriods.push(element) } else { for (let index = monthFrom; index <= monthTo; index++) { let element = {} if (index === monthFrom) { element = { month: index, - days: 31 - Number(from.split("-")[2]) + days: 32 - Number(from.split("-")[2]) } } else if (index === monthTo) { element = { @@ -93,38 +117,77 @@ async function search(from, to, queries) { } } virtDays += element.days - values.push(element) + travelPeriods.push(element) } } - // retrieve data from database - const minMax = await getClimateMinMax() - const data = await Promise.all(values.map(async (val) => { - return await getAllClimatePerMonth(val.month) - })) - console.log("data from getAllClimatePerMonth") - //console.log(data) + // calculate score - // TODO: calculate score for all months - let scores_temp - if (queries.temperature) { - scores_temp = calculateTempScores(data[0], queries.temperature.split(',')[0], queries.temperature.split(',')[1], minMax) - } + let detailScores = await Promise.all(travelPeriods.map(async period => { + period.climate = await getAllRegionsWithClimatePerMonth(period.month) + period.scores = {} + Object.entries(queries).forEach(([key, value]) => { + // console.log('key',key) + // console.log('val', value) + period.scores[key] = calculateScores(key, period.climate, value.split(',')[0], value.split(',')[1], minMax) + }); + return period + })); + + // TODO: calculate ratio - return { TODO___temperature_scores: scores_temp } + return transformer.transform(detailScores) + // return { TODO___scores: detailScores } + // return { TODO___scores: calculateAverageScore(detailScores, Object.keys(queries)) } } -function calculateTempScores(regionDataRows, searchLowParam, searchMaxParam, minMax) { - console.log('calculateTempScores') - console.log(searchLowParam) - console.log(searchMaxParam) - console.log(minMax) +function calculateAverageScore(scorePeriods, types) { + const days = scorePeriods.reduce((total, period) => total += period.days) + let totalScores = {} + + let finalRegionObj = { + + } + + scorePeriods.forEach(element => { + + types.forEach(type => { + element.scores[type].forEach(regionScore => { + + }) + }) + }) +} + +function transformToRegionObjects(scorePeriods) { + let response = [] + // create region objects + scorePeriods[0].climate.forEach(element => { + let obj = { + region_id: element.region_id, + country_id: element.country_id, + name: element.name, + } + response.push(obj) + }) + // + +} + +function calculateScores(type, regionDataRows, searchLowParam, searchMaxParam, minMax) { + console.log('calculateScores for', type) + // console.log(searchLowParam) + // console.log(searchMaxParam) + // console.log(minMax) //console.log(regionDataRows) let result = regionDataRows.map(x => { - console.log(x.temperature_mean) + // console.log(x.temperature_mean) + const sc = Math.round(score.calculateScoreRange(minMax.min[type], minMax.max[type], multiplier[type], x[type], searchLowParam, searchMaxParam) * 100) / 100 + return { region_id: x.region_id, - value: x.temperature_mean, - score: Math.round(score.calculateScoreRange(minMax.min.temperature_mean, minMax.max.temperature_mean, multiplier_temp, x.temperature_mean, searchLowParam, searchMaxParam)*100)/100 + type: type, + value: x[type], + score: x[type] === null ? null : sc } }) @@ -138,47 +201,52 @@ async function getClimateMinMax() { MIN(temperature_mean_min) AS temperature_mean_min, MIN(temperature_mean_max) AS temperature_mean_max, MIN(percipitation) AS percipitation, - MIN(sunshine) AS sunshine + MIN(raindays) AS raindays, + MIN(sunshine) AS sunhours FROM region_climate` const sqlMax = `SELECT MAX(temperature_mean) AS temperature_mean, MAX(temperature_mean_min) AS temperature_mean_min, MAX(temperature_mean_max) AS temperature_mean_max, MAX(percipitation) AS percipitation, - MAX(sunshine) AS sunshine + MAX(raindays) AS raindays, + MAX(sunshine) AS sunhours FROM region_climate` const [qResMin, qResMax] = await Promise.all([getQueryRows(sqlMin), getQueryRows(sqlMax)]) - console.log(qResMin) + //console.log(qResMin) return { min: qResMin[0], max: qResMax[0] } } async function getQueryRows(sql) { - console.log('getQueryRows') + //console.log('getQueryRows') const [rows, fields] = await db.execute(sql) return rows } -async function getRegionIdsWithMeteostatData() { +function getRegionIdsWithMeteostatData() { console.log('getRegionIdsWithMeteostatData') //return await getQueryRows(`SELECT * FROM regions WHERE meteostat_id IS NOT NULL`) - return await getQueryRows(`SELECT region_id FROM region_climate GROUP BY region_id`) + return getQueryRows(`SELECT region_id FROM region_climate GROUP BY region_id`) } -async function getClimatePerRegionAndMonth(regionId, month) { +function getClimatePerRegionAndMonth(regionId, month) { console.log('getClimatePerRegionAndMonth') const sql = `SELECT region_id, AVG(temperature_mean), AVG(temperature_mean_min), AVG(temperature_mean_max), AVG(percipitation), AVG(sunshine) FROM region_climate WHERE month = ${month} AND region_id = ${regionId}` - return await getQueryRows(sql) + return getQueryRows(sql) } -async function getAllClimatePerMonth(month) { - console.log('getAllClimatePerMonth') +function getAllRegionsWithClimatePerMonth(month) { + console.log('getAllRegionsWithClimatePerMonth') const sql = `SELECT - region_id, - ROUND(AVG(temperature_mean), 1) AS temperature_mean, - ROUND(AVG(temperature_mean_min), 1) AS temperature_mean_min, - ROUND(AVG(temperature_mean_max), 1) AS temperature_mean_max, - ROUND(AVG(percipitation), 1) AS percipitation, - ROUND(AVG(sunshine), 1) AS sunshine - FROM region_climate WHERE month = ${month} GROUP BY region_id` - return await getQueryRows(sql) + region_climate.region_id AS region_id, + regions.country_id AS country_id, + regions.region AS name, + ROUND(AVG(region_climate.temperature_mean), 1) AS temperature_mean, + ROUND(AVG(region_climate.temperature_mean_min), 1) AS temperature_mean_min, + ROUND(AVG(region_climate.temperature_mean_max), 1) AS temperature_mean_max, + ROUND(AVG(region_climate.percipitation), 1) AS percipitation, + ROUND(AVG(region_climate.raindays), 1) AS raindays, + ROUND(AVG(region_climate.sunshine), 1) AS sunhours + FROM region_climate JOIN regions ON region_climate.region_id = regions.id WHERE region_climate.month = ${month} GROUP BY region_id` + return getQueryRows(sql) } \ No newline at end of file diff --git a/backend/climate.js b/backend/climate.js index 2b2c5ea..96ff87c 100644 --- a/backend/climate.js +++ b/backend/climate.js @@ -2,7 +2,7 @@ require('dotenv').config() const mysql = require('mysql2/promise'); const axios = require('axios') -const rangeStartDate = '2018-01' +const rangeStartDate = '2010-01' const rangeEndDate = '2018-12' async function main() { @@ -15,12 +15,12 @@ async function main() { }); const [result, fields] = await connection.execute(`SELECT * FROM regions WHERE meteostat_id IS NOT NULL`) - let temp = await Promise.all(result.map(x => createClimateObject(x))) - let final = temp.reduce((total, element) => total.concat(element), []) + // let temp = await Promise.all(result.map(x => createClimateObject(x))) + // let final = temp.reduce((total, element) => total.concat(element), []) - await writeToDatabase(connection, final) + // await writeToDatabase(connection, final) - let temp2 = await Promise.all(result.map(x => createClimateObjectFrom(x))) + let temp2 = await Promise.all(result.map(x => createClimateObjectFrom(x, startDate, endDate))) let final2 = temp2.reduce((total, element) => total.concat(element), []) await writeToDatabase(connection, final2) diff --git a/backend/mysql.js b/backend/mysql.js index c7114d5..7d954a0 100644 --- a/backend/mysql.js +++ b/backend/mysql.js @@ -15,7 +15,15 @@ const pool = mysql.createPool({ user: process.env.DB_USER, password: process.env.DB_PASSWORD, port: process.env.DB_PORT, - database: 'travopti' + database: 'travopti', + // typeCast: function (field, next) { + // if (field.type == "INT") { + // var value = field.string(); + // return (value === null) ? null : Number(value); + // } + // return next(); + // } + decimalNumbers: true }); pool.getConnection() diff --git a/backend/package-lock.json b/backend/package-lock.json index 9a19ff0..249085f 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -816,6 +816,11 @@ "package-json": "^6.3.0" } }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, "long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", diff --git a/backend/package.json b/backend/package.json index 30dcacc..c07ce33 100644 --- a/backend/package.json +++ b/backend/package.json @@ -3,15 +3,16 @@ "version": "1.0.0", "description": "", "main": "app.js", -"scripts": { - "start": "nodemon ./app.js" -}, + "scripts": { + "start": "nodemon ./app.js" + }, "author": "", "license": "ISC", "dependencies": { "axios": "^0.19.2", "dotenv": "^8.2.0", "express": "^4.17.1", + "lodash": "^4.17.15", "mysql": "^2.18.1", "mysql2": "^2.1.0" }, diff --git a/backend/score.js b/backend/score.js index feec62a..4096aed 100644 --- a/backend/score.js +++ b/backend/score.js @@ -13,11 +13,12 @@ exports.calculateAvgScore = (...scores) => { } exports.calculateScoreRange = (min, max, multiplier, regionVal, sLowVal, sHighVal) => { + //console.log('scores.calculateScoreRange:', min, max, multiplier, regionVal, sLowVal, sHighVal) // return full score when in range if (regionVal >= sLowVal && regionVal <= sHighVal) return 10; // choose value with smallest distance let sVal = Math.abs(regionVal - sLowVal) < Math.abs(regionVal - sHighVal) ? sLowVal : sHighVal; - + //console.log('nearest value',sVal, regionVal) return this.calculateScore(min, max, multiplier, regionVal, sVal); } diff --git a/backend/transformer-test.json b/backend/transformer-test.json new file mode 100644 index 0000000..cb444bd --- /dev/null +++ b/backend/transformer-test.json @@ -0,0 +1,1205 @@ +[ + { + "month": 5, + "days": 31, + "climate": [ + { + "region_id": 2, + "country_id": 2, + "name": "Melbourne", + "temperature_mean": 12.6, + "temperature_mean_min": 8.3, + "temperature_mean_max": 17, + "percipitation": 35.4, + "raindays": 7.8, + "sunhours": 139 + }, + { + "region_id": 3, + "country_id": 2, + "name": "Sydney", + "temperature_mean": 16.6, + "temperature_mean_min": 12, + "temperature_mean_max": 21.2, + "percipitation": 59.2, + "raindays": 5.6, + "sunhours": 218.4 + }, + { + "region_id": 6, + "country_id": 4, + "name": "Sao Paolo", + "temperature_mean": 19, + "temperature_mean_min": 16.1, + "temperature_mean_max": 22.8, + "percipitation": 42.3, + "raindays": 5, + "sunhours": null + }, + { + "region_id": 7, + "country_id": 5, + "name": "Toronto", + "temperature_mean": 16.1, + "temperature_mean_min": 10.8, + "temperature_mean_max": 21.5, + "percipitation": 52, + "raindays": 11, + "sunhours": 268 + }, + { + "region_id": 9, + "country_id": 7, + "name": "Peking", + "temperature_mean": 22.1, + "temperature_mean_min": 15.8, + "temperature_mean_max": 28, + "percipitation": 26.7, + "raindays": 3.3, + "sunhours": 261.5 + }, + { + "region_id": 10, + "country_id": 7, + "name": "Shanghai", + "temperature_mean": 21.3, + "temperature_mean_min": 17.7, + "temperature_mean_max": 25.5, + "percipitation": 105.9, + "raindays": 8.6, + "sunhours": 165.9 + }, + { + "region_id": 11, + "country_id": 8, + "name": "Bogota", + "temperature_mean": 14.3, + "temperature_mean_min": 10.5, + "temperature_mean_max": 19.5, + "percipitation": 108, + "raindays": 13.3, + "sunhours": 103.6 + }, + { + "region_id": 12, + "country_id": 9, + "name": "Kairo", + "temperature_mean": 26.4, + "temperature_mean_min": 20.7, + "temperature_mean_max": 32.5, + "percipitation": 0.1, + "raindays": 0, + "sunhours": null + }, + { + "region_id": 13, + "country_id": 10, + "name": "London", + "temperature_mean": 13.9, + "temperature_mean_min": 9.2, + "temperature_mean_max": 18.4, + "percipitation": 47, + "raindays": 8, + "sunhours": 188 + }, + { + "region_id": 18, + "country_id": 15, + "name": "Reykjavik", + "temperature_mean": 6.8, + "temperature_mean_min": 4.1, + "temperature_mean_max": 10.3, + "percipitation": 53.4, + "raindays": 10.4, + "sunhours": 208.9 + }, + { + "region_id": 24, + "country_id": 20, + "name": "Kuala Lumpur", + "temperature_mean": 28.7, + "temperature_mean_min": 25.3, + "temperature_mean_max": 33.8, + "percipitation": 301.9, + "raindays": 15.4, + "sunhours": null + } + ], + "scores": { + "temperature_mean": [ + { + "region_id": 2, + "type": "temperature_mean", + "value": 12.6, + "score": 0 + }, + { + "region_id": 3, + "type": "temperature_mean", + "value": 16.6, + "score": 2.97 + }, + { + "region_id": 6, + "type": "temperature_mean", + "value": 19, + "score": 6.09 + }, + { + "region_id": 7, + "type": "temperature_mean", + "value": 16.1, + "score": 2.32 + }, + { + "region_id": 9, + "type": "temperature_mean", + "value": 22.1, + "score": 10 + }, + { + "region_id": 10, + "type": "temperature_mean", + "value": 21.3, + "score": 9.09 + }, + { + "region_id": 11, + "type": "temperature_mean", + "value": 14.3, + "score": 0 + }, + { + "region_id": 12, + "type": "temperature_mean", + "value": 26.4, + "score": 5.57 + }, + { + "region_id": 13, + "type": "temperature_mean", + "value": 13.9, + "score": 0 + }, + { + "region_id": 18, + "type": "temperature_mean", + "value": 6.8, + "score": 0 + }, + { + "region_id": 24, + "type": "temperature_mean", + "value": 28.7, + "score": 2.58 + } + ], + "percipitation": [ + { + "region_id": 2, + "type": "percipitation", + "value": 35.4, + "score": 10 + }, + { + "region_id": 3, + "type": "percipitation", + "value": 59.2, + "score": 10 + }, + { + "region_id": 6, + "type": "percipitation", + "value": 42.3, + "score": 10 + }, + { + "region_id": 7, + "type": "percipitation", + "value": 52, + "score": 10 + }, + { + "region_id": 9, + "type": "percipitation", + "value": 26.7, + "score": 10 + }, + { + "region_id": 10, + "type": "percipitation", + "value": 105.9, + "score": 9.95 + }, + { + "region_id": 11, + "type": "percipitation", + "value": 108, + "score": 9.93 + }, + { + "region_id": 12, + "type": "percipitation", + "value": 0.1, + "score": 10 + }, + { + "region_id": 13, + "type": "percipitation", + "value": 47, + "score": 10 + }, + { + "region_id": 18, + "type": "percipitation", + "value": 53.4, + "score": 10 + }, + { + "region_id": 24, + "type": "percipitation", + "value": 301.9, + "score": 8.14 + } + ], + "raindays": [ + { + "region_id": 2, + "type": "raindays", + "value": 7.8, + "score": 5.78 + }, + { + "region_id": 3, + "type": "raindays", + "value": 5.6, + "score": 8.22 + }, + { + "region_id": 6, + "type": "raindays", + "value": 5, + "score": 8.89 + }, + { + "region_id": 7, + "type": "raindays", + "value": 11, + "score": 2.22 + }, + { + "region_id": 9, + "type": "raindays", + "value": 3.3, + "score": 10 + }, + { + "region_id": 10, + "type": "raindays", + "value": 8.6, + "score": 4.89 + }, + { + "region_id": 11, + "type": "raindays", + "value": 13.3, + "score": 0 + }, + { + "region_id": 12, + "type": "raindays", + "value": 0, + "score": 8.89 + }, + { + "region_id": 13, + "type": "raindays", + "value": 8, + "score": 5.56 + }, + { + "region_id": 18, + "type": "raindays", + "value": 10.4, + "score": 2.89 + }, + { + "region_id": 24, + "type": "raindays", + "value": 15.4, + "score": 0 + } + ], + "sunhours": [ + { + "region_id": 2, + "type": "sunhours", + "value": 139, + "score": 1.27 + }, + { + "region_id": 3, + "type": "sunhours", + "value": 218.4, + "score": 7.52 + }, + { + "region_id": 6, + "type": "sunhours", + "value": null, + "score": null + }, + { + "region_id": 7, + "type": "sunhours", + "value": 268, + "score": 10 + }, + { + "region_id": 9, + "type": "sunhours", + "value": 261.5, + "score": 10 + }, + { + "region_id": 10, + "type": "sunhours", + "value": 165.9, + "score": 3.39 + }, + { + "region_id": 11, + "type": "sunhours", + "value": 103.6, + "score": 0 + }, + { + "region_id": 12, + "type": "sunhours", + "value": null, + "score": null + }, + { + "region_id": 13, + "type": "sunhours", + "value": 188, + "score": 5.13 + }, + { + "region_id": 18, + "type": "sunhours", + "value": 208.9, + "score": 6.77 + }, + { + "region_id": 24, + "type": "sunhours", + "value": null, + "score": null + } + ] + } + }, + { + "month": 6, + "days": 30, + "climate": [ + { + "region_id": 2, + "country_id": 2, + "name": "Melbourne", + "temperature_mean": 10, + "temperature_mean_min": 6.1, + "temperature_mean_max": 14, + "percipitation": 51.4, + "raindays": 8, + "sunhours": 122.9 + }, + { + "region_id": 3, + "country_id": 2, + "name": "Sydney", + "temperature_mean": 14.2, + "temperature_mean_min": 10.2, + "temperature_mean_max": 18.3, + "percipitation": 154.7, + "raindays": 9.4, + "sunhours": 160 + }, + { + "region_id": 6, + "country_id": 4, + "name": "Sao Paolo", + "temperature_mean": 17.5, + "temperature_mean_min": 14.6, + "temperature_mean_max": 21.8, + "percipitation": 48.8, + "raindays": 4.5, + "sunhours": null + }, + { + "region_id": 7, + "country_id": 5, + "name": "Toronto", + "temperature_mean": 19.3, + "temperature_mean_min": 15.1, + "temperature_mean_max": 23.8, + "percipitation": 192, + "raindays": 14, + "sunhours": 253 + }, + { + "region_id": 9, + "country_id": 7, + "name": "Peking", + "temperature_mean": 25.4, + "temperature_mean_min": 20.1, + "temperature_mean_max": 30.9, + "percipitation": 85, + "raindays": 7.9, + "sunhours": 209.2 + }, + { + "region_id": 10, + "country_id": 7, + "name": "Shanghai", + "temperature_mean": 24.2, + "temperature_mean_min": 21.6, + "temperature_mean_max": 27.5, + "percipitation": 199.4, + "raindays": 10.6, + "sunhours": 94.1 + }, + { + "region_id": 11, + "country_id": 8, + "name": "Bogota", + "temperature_mean": 14, + "temperature_mean_min": 9.4, + "temperature_mean_max": 18.6, + "percipitation": 57.8, + "raindays": 9.8, + "sunhours": 121.6 + }, + { + "region_id": 12, + "country_id": 9, + "name": "Kairo", + "temperature_mean": 28.6, + "temperature_mean_min": 23.1, + "temperature_mean_max": 34.6, + "percipitation": 0, + "raindays": 0, + "sunhours": null + }, + { + "region_id": 13, + "country_id": 10, + "name": "London", + "temperature_mean": 17, + "temperature_mean_min": 12.2, + "temperature_mean_max": 21.8, + "percipitation": 45.7, + "raindays": 7.6, + "sunhours": 180.8 + }, + { + "region_id": 18, + "country_id": 15, + "name": "Reykjavik", + "temperature_mean": 10.1, + "temperature_mean_min": 7.6, + "temperature_mean_max": 13.5, + "percipitation": 49.3, + "raindays": 9.8, + "sunhours": 171.3 + }, + { + "region_id": 24, + "country_id": 20, + "name": "Kuala Lumpur", + "temperature_mean": 29, + "temperature_mean_min": 25.5, + "temperature_mean_max": 33.8, + "percipitation": 137.9, + "raindays": 7.4, + "sunhours": null + } + ], + "scores": { + "temperature_mean": [ + { + "region_id": 2, + "type": "temperature_mean", + "value": 10, + "score": 0 + }, + { + "region_id": 3, + "type": "temperature_mean", + "value": 14.2, + "score": 0 + }, + { + "region_id": 6, + "type": "temperature_mean", + "value": 17.5, + "score": 4.14 + }, + { + "region_id": 7, + "type": "temperature_mean", + "value": 19.3, + "score": 6.48 + }, + { + "region_id": 9, + "type": "temperature_mean", + "value": 25.4, + "score": 6.88 + }, + { + "region_id": 10, + "type": "temperature_mean", + "value": 24.2, + "score": 8.44 + }, + { + "region_id": 11, + "type": "temperature_mean", + "value": 14, + "score": 0 + }, + { + "region_id": 12, + "type": "temperature_mean", + "value": 28.6, + "score": 2.71 + }, + { + "region_id": 13, + "type": "temperature_mean", + "value": 17, + "score": 3.49 + }, + { + "region_id": 18, + "type": "temperature_mean", + "value": 10.1, + "score": 0 + }, + { + "region_id": 24, + "type": "temperature_mean", + "value": 29, + "score": 2.19 + } + ], + "percipitation": [ + { + "region_id": 2, + "type": "percipitation", + "value": 51.4, + "score": 10 + }, + { + "region_id": 3, + "type": "percipitation", + "value": 154.7, + "score": 9.5 + }, + { + "region_id": 6, + "type": "percipitation", + "value": 48.8, + "score": 10 + }, + { + "region_id": 7, + "type": "percipitation", + "value": 192, + "score": 9.15 + }, + { + "region_id": 9, + "type": "percipitation", + "value": 85, + "score": 10 + }, + { + "region_id": 10, + "type": "percipitation", + "value": 199.4, + "score": 9.09 + }, + { + "region_id": 11, + "type": "percipitation", + "value": 57.8, + "score": 10 + }, + { + "region_id": 12, + "type": "percipitation", + "value": 0, + "score": 10 + }, + { + "region_id": 13, + "type": "percipitation", + "value": 45.7, + "score": 10 + }, + { + "region_id": 18, + "type": "percipitation", + "value": 49.3, + "score": 10 + }, + { + "region_id": 24, + "type": "percipitation", + "value": 137.9, + "score": 9.65 + } + ], + "raindays": [ + { + "region_id": 2, + "type": "raindays", + "value": 8, + "score": 5.56 + }, + { + "region_id": 3, + "type": "raindays", + "value": 9.4, + "score": 4 + }, + { + "region_id": 6, + "type": "raindays", + "value": 4.5, + "score": 9.44 + }, + { + "region_id": 7, + "type": "raindays", + "value": 14, + "score": 0 + }, + { + "region_id": 9, + "type": "raindays", + "value": 7.9, + "score": 5.67 + }, + { + "region_id": 10, + "type": "raindays", + "value": 10.6, + "score": 2.67 + }, + { + "region_id": 11, + "type": "raindays", + "value": 9.8, + "score": 3.56 + }, + { + "region_id": 12, + "type": "raindays", + "value": 0, + "score": 8.89 + }, + { + "region_id": 13, + "type": "raindays", + "value": 7.6, + "score": 6 + }, + { + "region_id": 18, + "type": "raindays", + "value": 9.8, + "score": 3.56 + }, + { + "region_id": 24, + "type": "raindays", + "value": 7.4, + "score": 6.22 + } + ], + "sunhours": [ + { + "region_id": 2, + "type": "sunhours", + "value": 122.9, + "score": 0.01 + }, + { + "region_id": 3, + "type": "sunhours", + "value": 160, + "score": 2.92 + }, + { + "region_id": 6, + "type": "sunhours", + "value": null, + "score": null + }, + { + "region_id": 7, + "type": "sunhours", + "value": 253, + "score": 10 + }, + { + "region_id": 9, + "type": "sunhours", + "value": 209.2, + "score": 6.79 + }, + { + "region_id": 10, + "type": "sunhours", + "value": 94.1, + "score": 0 + }, + { + "region_id": 11, + "type": "sunhours", + "value": 121.6, + "score": 0 + }, + { + "region_id": 12, + "type": "sunhours", + "value": null, + "score": null + }, + { + "region_id": 13, + "type": "sunhours", + "value": 180.8, + "score": 4.56 + }, + { + "region_id": 18, + "type": "sunhours", + "value": 171.3, + "score": 3.81 + }, + { + "region_id": 24, + "type": "sunhours", + "value": null, + "score": null + } + ] + } + }, + { + "month": 7, + "days": 14, + "climate": [ + { + "region_id": 2, + "country_id": 2, + "name": "Melbourne", + "temperature_mean": 9.8, + "temperature_mean_min": 5.9, + "temperature_mean_max": 13.7, + "percipitation": 33.4, + "raindays": 8.7, + "sunhours": 139.3 + }, + { + "region_id": 3, + "country_id": 2, + "name": "Sydney", + "temperature_mean": 13.6, + "temperature_mean_min": 8.8, + "temperature_mean_max": 18.4, + "percipitation": 68.2, + "raindays": 6.7, + "sunhours": 221 + }, + { + "region_id": 6, + "country_id": 4, + "name": "Sao Paolo", + "temperature_mean": 18.1, + "temperature_mean_min": 13.6, + "temperature_mean_max": 22.5, + "percipitation": 19.4, + "raindays": 2.8, + "sunhours": null + }, + { + "region_id": 7, + "country_id": 5, + "name": "Toronto", + "temperature_mean": 23.6, + "temperature_mean_min": 18.5, + "temperature_mean_max": 28.5, + "percipitation": 90, + "raindays": 7, + "sunhours": 308 + }, + { + "region_id": 9, + "country_id": 7, + "name": "Peking", + "temperature_mean": 27.7, + "temperature_mean_min": 23.6, + "temperature_mean_max": 32.2, + "percipitation": 199.9, + "raindays": 8.6, + "sunhours": 174.9 + }, + { + "region_id": 10, + "country_id": 7, + "name": "Shanghai", + "temperature_mean": 29.5, + "temperature_mean_min": 26.5, + "temperature_mean_max": 33.3, + "percipitation": 125.9, + "raindays": 8.2, + "sunhours": 183.6 + }, + { + "region_id": 11, + "country_id": 8, + "name": "Bogota", + "temperature_mean": 13.7, + "temperature_mean_min": 9.4, + "temperature_mean_max": 18.7, + "percipitation": 55.3, + "raindays": 9.7, + "sunhours": 129.2 + }, + { + "region_id": 12, + "country_id": 9, + "name": "Kairo", + "temperature_mean": 29.4, + "temperature_mean_min": 24.3, + "temperature_mean_max": 34.9, + "percipitation": 0, + "raindays": 0, + "sunhours": null + }, + { + "region_id": 13, + "country_id": 10, + "name": "London", + "temperature_mean": 19.5, + "temperature_mean_min": 14.5, + "temperature_mean_max": 24.4, + "percipitation": 45.3, + "raindays": 7.7, + "sunhours": 204 + }, + { + "region_id": 18, + "country_id": 15, + "name": "Reykjavik", + "temperature_mean": 11.8, + "temperature_mean_min": 9.3, + "temperature_mean_max": 15.3, + "percipitation": 51, + "raindays": 10.9, + "sunhours": 178.1 + }, + { + "region_id": 24, + "country_id": 20, + "name": "Kuala Lumpur", + "temperature_mean": 28.6, + "temperature_mean_min": 25, + "temperature_mean_max": 33.2, + "percipitation": 150.2, + "raindays": 10, + "sunhours": null + } + ], + "scores": { + "temperature_mean": [ + { + "region_id": 2, + "type": "temperature_mean", + "value": 9.8, + "score": 0 + }, + { + "region_id": 3, + "type": "temperature_mean", + "value": 13.6, + "score": 0 + }, + { + "region_id": 6, + "type": "temperature_mean", + "value": 18.1, + "score": 4.92 + }, + { + "region_id": 7, + "type": "temperature_mean", + "value": 23.6, + "score": 9.22 + }, + { + "region_id": 9, + "type": "temperature_mean", + "value": 27.7, + "score": 3.88 + }, + { + "region_id": 10, + "type": "temperature_mean", + "value": 29.5, + "score": 1.54 + }, + { + "region_id": 11, + "type": "temperature_mean", + "value": 13.7, + "score": 0 + }, + { + "region_id": 12, + "type": "temperature_mean", + "value": 29.4, + "score": 1.67 + }, + { + "region_id": 13, + "type": "temperature_mean", + "value": 19.5, + "score": 6.74 + }, + { + "region_id": 18, + "type": "temperature_mean", + "value": 11.8, + "score": 0 + }, + { + "region_id": 24, + "type": "temperature_mean", + "value": 28.6, + "score": 2.71 + } + ], + "percipitation": [ + { + "region_id": 2, + "type": "percipitation", + "value": 33.4, + "score": 10 + }, + { + "region_id": 3, + "type": "percipitation", + "value": 68.2, + "score": 10 + }, + { + "region_id": 6, + "type": "percipitation", + "value": 19.4, + "score": 10 + }, + { + "region_id": 7, + "type": "percipitation", + "value": 90, + "score": 10 + }, + { + "region_id": 9, + "type": "percipitation", + "value": 199.9, + "score": 9.08 + }, + { + "region_id": 10, + "type": "percipitation", + "value": 125.9, + "score": 9.76 + }, + { + "region_id": 11, + "type": "percipitation", + "value": 55.3, + "score": 10 + }, + { + "region_id": 12, + "type": "percipitation", + "value": 0, + "score": 10 + }, + { + "region_id": 13, + "type": "percipitation", + "value": 45.3, + "score": 10 + }, + { + "region_id": 18, + "type": "percipitation", + "value": 51, + "score": 10 + }, + { + "region_id": 24, + "type": "percipitation", + "value": 150.2, + "score": 9.54 + } + ], + "raindays": [ + { + "region_id": 2, + "type": "raindays", + "value": 8.7, + "score": 4.78 + }, + { + "region_id": 3, + "type": "raindays", + "value": 6.7, + "score": 7 + }, + { + "region_id": 6, + "type": "raindays", + "value": 2.8, + "score": 10 + }, + { + "region_id": 7, + "type": "raindays", + "value": 7, + "score": 6.67 + }, + { + "region_id": 9, + "type": "raindays", + "value": 8.6, + "score": 4.89 + }, + { + "region_id": 10, + "type": "raindays", + "value": 8.2, + "score": 5.33 + }, + { + "region_id": 11, + "type": "raindays", + "value": 9.7, + "score": 3.67 + }, + { + "region_id": 12, + "type": "raindays", + "value": 0, + "score": 8.89 + }, + { + "region_id": 13, + "type": "raindays", + "value": 7.7, + "score": 5.89 + }, + { + "region_id": 18, + "type": "raindays", + "value": 10.9, + "score": 2.33 + }, + { + "region_id": 24, + "type": "raindays", + "value": 10, + "score": 3.33 + } + ], + "sunhours": [ + { + "region_id": 2, + "type": "sunhours", + "value": 139.3, + "score": 1.3 + }, + { + "region_id": 3, + "type": "sunhours", + "value": 221, + "score": 7.72 + }, + { + "region_id": 6, + "type": "sunhours", + "value": null, + "score": null + }, + { + "region_id": 7, + "type": "sunhours", + "value": 308, + "score": 9.37 + }, + { + "region_id": 9, + "type": "sunhours", + "value": 174.9, + "score": 4.1 + }, + { + "region_id": 10, + "type": "sunhours", + "value": 183.6, + "score": 4.78 + }, + { + "region_id": 11, + "type": "sunhours", + "value": 129.2, + "score": 0.5 + }, + { + "region_id": 12, + "type": "sunhours", + "value": null, + "score": null + }, + { + "region_id": 13, + "type": "sunhours", + "value": 204, + "score": 6.38 + }, + { + "region_id": 18, + "type": "sunhours", + "value": 178.1, + "score": 4.35 + }, + { + "region_id": 24, + "type": "sunhours", + "value": null, + "score": null + } + ] + } + } +] \ No newline at end of file diff --git a/backend/transformer.js b/backend/transformer.js new file mode 100644 index 0000000..9ac37f2 --- /dev/null +++ b/backend/transformer.js @@ -0,0 +1,77 @@ +const _ = require('lodash') +const fs = require('fs') + + +exports.transform = (data) => { + // get data + // let data = JSON.parse(fs.readFileSync('transformer-test.json')); + const types = Object.keys(data[0].scores) + + // STEP 1 Create Response Array with region names from first climate object + let byRegion = data[0].climate.map(el => { + return { + region_id: el.region_id, + country_id: el.country_id, + name: el.name, + } + }) + + // STEP 2 Prepare flat scoreobject array and set days property + scoreObjs = _.flatten(_.map(data, (period) => { + return _.reduce(period.scores, (arr, el) => { + return arr.concat(el) + }).map(element => { + element.days = period.days + return element + }) + })) + + // STEP 3 Collect scoreobjects for each region + let results = byRegion.map(region => { + let scores = [] + types.forEach(typ => { + let tempScores = _.filter(scoreObjs, { 'region_id': region.region_id, 'type': typ }) + if (_.some(tempScores, { 'score': null })) { + console.log("found 'null' scores! skipping...") + //console.log(tempScores) + return + } + let averagedScore = { + region_id: region.region_id, + type: typ, + value: 0, + score: 0, + days: 0 + } + tempScores.forEach(el => { + averagedScore.value += (el.value * el.days) + averagedScore.score += (el.score * el.days) + averagedScore.days += (el.days) + }) + + averagedScore.value = _.round(averagedScore.value / averagedScore.days, 1) + averagedScore.score = _.round(averagedScore.score / averagedScore.days, 1) + delete averagedScore.region_id + delete averagedScore.days + scores.push(averagedScore) + + }) + region.scores = scores + + // STEP 4 Calculate Average Score + region.score = calculateAverage(region.scores) + //console.log(region) + return region + }) + + // console.log(results) + return _.orderBy(results, 'score', 'desc') + //end +} + +function calculateAverage(scores) { + let sum = 0 + scores.forEach(el => sum += el.score) + //console.log(sum) + return _.round(sum / scores.length, 2) +} \ No newline at end of file From 2e4ec7b24e6f59b53db52354d6c8b4d6bf4b6a48 Mon Sep 17 00:00:00 2001 From: Timo Volkmann Date: Sun, 14 Jun 2020 01:32:21 +0200 Subject: [PATCH 08/20] balancing --- backend/app.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/backend/app.js b/backend/app.js index 6b83f4e..1321d14 100644 --- a/backend/app.js +++ b/backend/app.js @@ -10,7 +10,7 @@ const port = 3000 //const multiplier_temp = 5 const multiplier = { temperature_mean: 5, - percipitation: 0.5, + percipitation: 3.5, raindays: 3, sunhours: 2.5, } @@ -135,7 +135,13 @@ async function search(from, to, queries) { // TODO: calculate ratio - return transformer.transform(detailScores) + return { + results: transformer.transform(detailScores), + debug: { + detailScores: detailScores, + minMax: minMax + } + } // return { TODO___scores: detailScores } // return { TODO___scores: calculateAverageScore(detailScores, Object.keys(queries)) } } From bbf483e445a42aae86d93cfdc245a63d34884977 Mon Sep 17 00:00:00 2001 From: Timo Volkmann Date: Sun, 14 Jun 2020 13:33:07 +0200 Subject: [PATCH 09/20] little cleanup --- README.md | 40 ++++++++++++++++- backend/app.js | 93 +++++++++++++++++---------------------- backend/climate.js | 79 +++++++++++++++++---------------- backend/package-lock.json | 63 +++----------------------- backend/package.json | 2 +- backend/sampledata.js | 16 ------- 6 files changed, 125 insertions(+), 168 deletions(-) delete mode 100644 backend/sampledata.js diff --git a/README.md b/README.md index a452ec5..d5aebb6 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,45 @@ Campus Cup AKMC Data Traveloptimizer -## Installation +## Backend + +### Requirements + - MariaDB or MySQL + - node `10.12` or higher + - Configure database in `.env`-file or environment variables. See `.env` for reference + - Set API-Key for meteostat.net in `.env`-file or environment variable + - import `setup.sql` for sample data + +### Start + - Run `$(cd backend && npm run start)` + - call http://localhost:3000/v1/update/climate to fetch climate data for sample entries. + +### Search + Customize your search with query parameters. For now, only climate parameters are supported. If you omit climate queries, all climate parameters will be randomized. + + Following queries are supperted by now: + - from=YYYY-MM-DD _(required)_ + - to=YYYY-MM-DD _(required)_ + - temperature=NUMBER,NUMBER + - raindays=NUMBER,NUMBER + - sunhours=NUMBER,NUMBER + - percipitation=NUMBER,NUMBER + +__Examples:__ +http://localhost:3000/v1/search?from=2020-06-14&to=2020-07-29&temperature=27,29&raindays=8,12&sunhours=250,300 +http://localhost:3000/v1/search?from=2020-06-14&to=2020-07-29 + + +### More +To get more search results, add more entries with meteostat station IDs to the `regions` table in the database + + + +## Frontend + +### Installation - Install node 10.15.3 - Run "(cd frontend && npm i)" -# Start dev server +### Start dev server - Run "(cd frontend && npm run start)" diff --git a/backend/app.js b/backend/app.js index 1321d14..c3b7ffa 100644 --- a/backend/app.js +++ b/backend/app.js @@ -4,6 +4,8 @@ var sampledata = require('./sampledata') var db = require('./mysql') var score = require('./score') var transformer = require('./transformer') +var climate = require('./climate') +var moment = require('moment') const app = express() const port = 3000 @@ -34,7 +36,29 @@ const samplePresets = [ app.get('/', (req, res) => res.send('Hello Timo!')) app.get('/v1/regions', (req, res) => res.json(sampleRegions)) app.get('/v1/presets', (req, res) => res.json(samplePresets)) -app.get('/v1/search', (req, res) => { +app.get('/v1/search', searchHandler) +app.get('/v1/update/climate', climateUpdateHandler) + +app.listen(port, () => console.log(`Travopti backend listening at http://localhost:${port}`)) + +function climateUpdateHandler(req, res) { + let parameter = [] + if (req.query.startDate) parameter.push(req.query.startDate) + if (req.query.endDate) parameter.push(req.query.endDate) + climate.update(...parameter).then(x => { + res.send(x) + }).catch(e => { + let result = { + message: 'error during update process. check backend logs.', + error: e + } + res.send(result) + } + + ) +} + +function searchHandler(req, res) { let response = {} response.meta = { @@ -42,7 +66,7 @@ app.get('/v1/search', (req, res) => { query: req.query, headers: req.headers } - + console.log('log params') console.log(req.query.from) console.log(req.query.to) @@ -57,15 +81,14 @@ app.get('/v1/search', (req, res) => { search(req.query.from, req.query.to, validQueries).then(searchResults => { response.data = searchResults res.json(response) + }).catch(e => { + console.log(e) + res.json(e.message) }) -}) - -app.listen(port, () => console.log(`Travopti backend listening at http://localhost:${port}`)) - - - +} async function search(from, to, queries) { + console.log('search') // get Min and Max values for each Parameter const minMax = await getClimateMinMax() // randomize if empty queries @@ -79,23 +102,28 @@ async function search(from, to, queries) { queries.raindays = `${r},${r + 5}` queries.sunhours = `${s},${s + 50}` } - console.log('search') // validate regex: YYYY-MM-DD let re = /([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/i; - if (!from.match(re) || !to.match(re)) throw new Error("invalid parameter: " + from + " " + to) + if (!from.match(re) || !to.match(re)) throw new Error("ERR: invalid parameter:",from,to) + // check for valid period + if (moment(from, 'YYYY-MM-DD').isAfter(moment(to, 'YYYY-MM-DD'))) { + console.log(moment(from, 'YYYY-MM-DD')) + console.log(moment(to, 'YYYY-MM-DD')) + console.log(moment(from, 'YYYY-MM-DD').isAfter(moment(to, 'YYYY-MM-DD'))) + throw new Error("ERR: from is before to date.") + } // -- Prepare search -- // calculate average if traveldates are in more than one month let monthFrom = Number(from.split("-")[1]) let monthTo = Number(to.split("-")[1]) + let travelPeriods = [] - let virtDays = 0 if (monthFrom === monthTo) { let element = { month: monthFrom, days: Number(to.split("-")[2]) - Number(from.split("-")[2]) } - virtDays = element.days travelPeriods.push(element) } else { for (let index = monthFrom; index <= monthTo; index++) { @@ -116,7 +144,6 @@ async function search(from, to, queries) { days: 30 } } - virtDays += element.days travelPeriods.push(element) } } @@ -142,51 +169,11 @@ async function search(from, to, queries) { minMax: minMax } } - // return { TODO___scores: detailScores } - // return { TODO___scores: calculateAverageScore(detailScores, Object.keys(queries)) } -} - -function calculateAverageScore(scorePeriods, types) { - const days = scorePeriods.reduce((total, period) => total += period.days) - let totalScores = {} - - let finalRegionObj = { - - } - - scorePeriods.forEach(element => { - - types.forEach(type => { - element.scores[type].forEach(regionScore => { - - }) - }) - }) -} - -function transformToRegionObjects(scorePeriods) { - let response = [] - // create region objects - scorePeriods[0].climate.forEach(element => { - let obj = { - region_id: element.region_id, - country_id: element.country_id, - name: element.name, - } - response.push(obj) - }) - // - } function calculateScores(type, regionDataRows, searchLowParam, searchMaxParam, minMax) { console.log('calculateScores for', type) - // console.log(searchLowParam) - // console.log(searchMaxParam) - // console.log(minMax) - //console.log(regionDataRows) let result = regionDataRows.map(x => { - // console.log(x.temperature_mean) const sc = Math.round(score.calculateScoreRange(minMax.min[type], minMax.max[type], multiplier[type], x[type], searchLowParam, searchMaxParam) * 100) / 100 return { diff --git a/backend/climate.js b/backend/climate.js index 96ff87c..f0ca4a2 100644 --- a/backend/climate.js +++ b/backend/climate.js @@ -5,7 +5,9 @@ const axios = require('axios') const rangeStartDate = '2010-01' const rangeEndDate = '2018-12' -async function main() { +exports.update = async function (startDate = rangeStartDate, endDate = rangeEndDate) { + console.log('update climate with:', startDate, endDate); + const connection = await mysql.createConnection({ host: process.env.DB_HOST, user: process.env.DB_USER, @@ -26,39 +28,42 @@ async function main() { await writeToDatabase(connection, final2) connection.end(); + let response = 'database update complete. see backend logs for info.' + console.log(response) + return response } -async function createClimateObject(src) { - let response - try { - response = await axios.get(`https://api.meteostat.net/v1/climate/normals?station=${src.meteostat_id}&key=${process.env.METEOSTAT_API_KEY}`) - } catch (error) { - console.log("skipping: couldn't find results for following region: ") - console.log(src.region + " with meteostat_id " + src.meteostat_id) - return [] - } - if (!response.data.data) { - console.log("skipping: no data for station meteostat_id " + src.meteostat_id + " (" + src.region + ")") - return [] - } - let results = [] - for (let index = 1; index <= 12; index++) { - let result = { - region: src.region, - region_id: src.id, - month: index, - temperature: Object.values(response.data.data.temperature)[index - 1] ? Object.values(response.data.data.temperature)[index - 1] : null, - temperature_min: Object.values(response.data.data.temperature_min)[index - 1] ? Object.values(response.data.data.temperature_min)[index - 1] : null, - temperature_max: Object.values(response.data.data.temperature_max)[index - 1] ? Object.values(response.data.data.temperature_max)[index - 1] : null, - precipitation: Object.values(response.data.data.precipitation)[index - 1] ? Object.values(response.data.data.precipitation)[index - 1] : null, - sunshine: Object.values(response.data.data.sunshine)[index - 1] ? Object.values(response.data.data.sunshine)[index - 1] : null, - } - results.push(result) - } - return results -} +// async function createClimateObject(src) { +// let response +// try { +// response = await axios.get(`https://api.meteostat.net/v1/climate/normals?station=${src.meteostat_id}&key=${process.env.METEOSTAT_API_KEY}`) +// } catch (error) { +// console.log("skipping: couldn't find results for following region: ") +// console.log(src.region + " with meteostat_id " + src.meteostat_id) +// return [] +// } +// if (!response.data.data) { +// console.log("skipping: no data for station meteostat_id " + src.meteostat_id + " (" + src.region + ")") +// return [] +// } +// let results = [] +// for (let index = 1; index <= 12; index++) { +// let result = { +// region: src.region, +// region_id: src.id, +// month: index, +// temperature: Object.values(response.data.data.temperature)[index - 1] ? Object.values(response.data.data.temperature)[index - 1] : null, +// temperature_min: Object.values(response.data.data.temperature_min)[index - 1] ? Object.values(response.data.data.temperature_min)[index - 1] : null, +// temperature_max: Object.values(response.data.data.temperature_max)[index - 1] ? Object.values(response.data.data.temperature_max)[index - 1] : null, +// precipitation: Object.values(response.data.data.precipitation)[index - 1] ? Object.values(response.data.data.precipitation)[index - 1] : null, +// sunshine: Object.values(response.data.data.sunshine)[index - 1] ? Object.values(response.data.data.sunshine)[index - 1] : null, +// } +// results.push(result) +// } +// return results +// } -async function createClimateObjectFrom(src, startDate = '2010-01', endDate = '2018-12') { +async function createClimateObjectFrom(src, startDate, endDate) { let response try { response = await axios.get(`https://api.meteostat.net/v1/history/monthly?station=${src.meteostat_id}&start=${startDate}&end=${endDate}&key=${process.env.METEOSTAT_API_KEY}`) @@ -92,17 +97,17 @@ async function createClimateObjectFrom(src, startDate = '2010-01', endDate = '20 return results } -async function writeToDatabase(dbConnection, src) { - src.forEach(async (element) => { +async function writeToDatabase(dbConnection, climateObjArr) { + climateObjArr.forEach(async (element) => { //console.log(element) try { if (!element.year) { await dbConnection.execute(` - INSERT INTO region_climate (region_id, year, month, temperature_mean, temperature_mean_min, temperature_mean_max, percipitation, sunshine) + REPLACE INTO region_climate (region_id, year, month, temperature_mean, temperature_mean_min, temperature_mean_max, percipitation, sunshine) VALUES (${element.region_id}, 0, ${element.month}, ${element.temperature}, ${element.temperature_min}, ${element.temperature_max}, ${element.precipitation}, ${element.sunshine});`) } else { await dbConnection.execute(` - INSERT INTO region_climate (region_id, year, month, temperature_mean, temperature_mean_min, temperature_mean_max, percipitation, sunshine, humidity, raindays) + REPLACE INTO region_climate (region_id, year, month, temperature_mean, temperature_mean_min, temperature_mean_max, percipitation, sunshine, humidity, raindays) VALUES (${element.region_id}, ${element.year}, ${element.month}, ${element.temperature}, ${element.temperature_min}, ${element.temperature_max}, ${element.precipitation}, ${element.sunshine}, ${element.humidity}, ${element.raindays});`) } } catch (error) { @@ -116,6 +121,4 @@ async function writeToDatabase(dbConnection, src) { } } }); -} - -main() \ No newline at end of file +} \ No newline at end of file diff --git a/backend/package-lock.json b/backend/package-lock.json index 249085f..7d000fc 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -112,11 +112,6 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, - "bignumber.js": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", - "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" - }, "binary-extensions": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", @@ -349,11 +344,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, "crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", @@ -787,11 +777,6 @@ "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", "dev": true }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, "json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", @@ -911,22 +896,16 @@ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, + "moment": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.26.0.tgz", + "integrity": "sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw==" + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "mysql": { - "version": "2.18.1", - "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", - "integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==", - "requires": { - "bignumber.js": "9.0.0", - "readable-stream": "2.3.7", - "safe-buffer": "5.1.2", - "sqlstring": "2.3.1" - } - }, "mysql2": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.1.0.tgz", @@ -1103,11 +1082,6 @@ "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", "dev": true }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -1180,20 +1154,6 @@ "strip-json-comments": "~2.0.1" } }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, "readdirp": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", @@ -1375,14 +1335,6 @@ } } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", @@ -1519,11 +1471,6 @@ "prepend-http": "^2.0.0" } }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", diff --git a/backend/package.json b/backend/package.json index c07ce33..c02c6df 100644 --- a/backend/package.json +++ b/backend/package.json @@ -13,7 +13,7 @@ "dotenv": "^8.2.0", "express": "^4.17.1", "lodash": "^4.17.15", - "mysql": "^2.18.1", + "moment": "^2.26.0", "mysql2": "^2.1.0" }, "devDependencies": { diff --git a/backend/sampledata.js b/backend/sampledata.js deleted file mode 100644 index 5c5e8a0..0000000 --- a/backend/sampledata.js +++ /dev/null @@ -1,16 +0,0 @@ -var sampledata = {} - -sampledata.search_response_model = [ - { - region_id: 1, - name: 'Chicago', - country: 'USA', - avg_score: 0.0, - scores: { - temperature: 0.0, - humidity: 0.0, - } - } -] - -module.exports = sampledata; \ No newline at end of file From cdae8c6e4578ac3e6bc621e5268c15921343f9bd Mon Sep 17 00:00:00 2001 From: Timo Volkmann Date: Sun, 14 Jun 2020 19:28:06 +0200 Subject: [PATCH 10/20] changed query syntax --- backend/app.js | 149 ++++++++++++++++++++++++++++------------------ backend/base64.js | 23 +++++++ 2 files changed, 113 insertions(+), 59 deletions(-) create mode 100644 backend/base64.js diff --git a/backend/app.js b/backend/app.js index c3b7ffa..f76b308 100644 --- a/backend/app.js +++ b/backend/app.js @@ -1,11 +1,11 @@ -var express = require('express') -var _ = require('lodash') -var sampledata = require('./sampledata') -var db = require('./mysql') -var score = require('./score') -var transformer = require('./transformer') -var climate = require('./climate') -var moment = require('moment') +const express = require('express') +const moment = require('moment') +const _ = require('lodash') +const db = require('./mysql') +const score = require('./score') +const transformer = require('./transformer') +const climate = require('./climate') +const base = require('./base64') const app = express() const port = 3000 @@ -17,13 +17,6 @@ const multiplier = { sunhours: 2.5, } -const sampleRegions = [ - { - id: 29837, - name: "Timbuktu", - country: "Mali" - } -] const samplePresets = [ { id: 29837, @@ -34,8 +27,8 @@ const samplePresets = [ ] app.get('/', (req, res) => res.send('Hello Timo!')) -app.get('/v1/regions', (req, res) => res.json(sampleRegions)) -app.get('/v1/presets', (req, res) => res.json(samplePresets)) +app.get('/v1/regions', (req, res) => getAllRegions().then(x => res.json({ data: x }))) +app.get('/v1/presets', (req, res) => res.json({ data: samplePresets})) app.get('/v1/search', searchHandler) app.get('/v1/update/climate', climateUpdateHandler) @@ -60,25 +53,23 @@ function climateUpdateHandler(req, res) { function searchHandler(req, res) { let response = {} - + response.meta = { params: req.params, query: req.query, headers: req.headers } - - console.log('log params') - console.log(req.query.from) - console.log(req.query.to) - console.log(req.query.temperature) - - let validQueries = {} - if (req.query.temperature) validQueries['temperature_mean'] = req.query.temperature - if (req.query.percipitation) validQueries['percipitation'] = req.query.percipitation - if (req.query.raindays) validQueries['raindays'] = req.query.raindays - if (req.query.sunhours) validQueries['sunhours'] = req.query.sunhours - - search(req.query.from, req.query.to, validQueries).then(searchResults => { + + let q = req.query.q ? base.base64ToObj(req.query.q) : req.query + console.log('Q:', q) + + let queryObj = {} + if (q.temperature) queryObj['temperature_mean'] = q.temperature + if (q.percipitation) queryObj['percipitation'] = q.percipitation + if (q.raindays) queryObj['raindays'] = q.raindays + if (q.sunhours) queryObj['sunhours'] = q.sunhours + + scoreAndSearch(q.from, q.to, queryObj).then(searchResults => { response.data = searchResults res.json(response) }).catch(e => { @@ -87,42 +78,60 @@ function searchHandler(req, res) { }) } -async function search(from, to, queries) { +async function scoreAndSearch(from, to, queries) { + // TODO break funtion into parts when implementing non-climate queries and modularize (new file) + console.log('search') + // get Min and Max values for each Parameter const minMax = await getClimateMinMax() + // randomize if empty queries if (_.isEmpty(queries)) { - let t = _.random(minMax.min.temperature_mean, minMax.max.temperature_mean-5) - let p = _.random(minMax.min.percipitation, minMax.max.percipitation-50) - let r = _.random(minMax.min.raindays, minMax.max.raindays-5) - let s = _.random(minMax.min.sunhours, minMax.max.sunhours-50) + let t = _.round(_.random(minMax.min.temperature_mean, minMax.max.temperature_mean-5),0) + let p = _.round(_.random(minMax.min.percipitation, minMax.max.percipitation - 50), 0) + let r = _.round(_.random(minMax.min.raindays, minMax.max.raindays - 5), 0) + let s = _.round(_.random(minMax.min.sunhours, minMax.max.sunhours - 50), 0) queries.temperature_mean = `${t},${t + 5}` queries.percipitation = `${p},${p + 50}` queries.raindays = `${r},${r + 5}` queries.sunhours = `${s},${s + 50}` } - // validate regex: YYYY-MM-DD - let re = /([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/i; - if (!from.match(re) || !to.match(re)) throw new Error("ERR: invalid parameter:",from,to) - // check for valid period - if (moment(from, 'YYYY-MM-DD').isAfter(moment(to, 'YYYY-MM-DD'))) { - console.log(moment(from, 'YYYY-MM-DD')) - console.log(moment(to, 'YYYY-MM-DD')) - console.log(moment(from, 'YYYY-MM-DD').isAfter(moment(to, 'YYYY-MM-DD'))) - throw new Error("ERR: from is before to date.") + queries = oldToNewQuerySyntax(queries) + console.log(queries) + + // TODO simplify and remove support for old query syntaax + let monthFrom = 0 + let monthTo = 0 + let dayFrom = 0 + let dayTo = 0 + + if (_.isNumber(from) && _.isNumber(to)) { + let dateFrom = moment(from).toDate() + let dateTo = moment(to).toDate() + monthFrom = dateFrom.getMonth() + monthTo = dateTo.getMonth() + dayFrom = dateFrom.getDay() + dayTo = dateTo.getDay() + if (moment(dateFrom).add(23, 'hours').isAfter(moment(dateTo))) throw new Error("ERR: 'to' must be at least one day after 'from'.") + } else { + // to still support old query syntax + let re = /([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/i; + monthFrom = Number(from.split("-")[1]) + monthTo = Number(to.split("-")[1]) + dayFrom = Number(from.split("-")[2]) + dayTo = Number(to.split("-")[2]) + if (!from.match(re) || !to.match(re)) throw new Error("ERR: invalid parameter:",from,to) + if (moment(from, 'YYYY-MM-DD').add(23, 'hours').isAfter(moment(to, 'YYYY-MM-DD'))) throw new Error("ERR: 'to' must be at least one day after 'from'.") } // -- Prepare search -- - // calculate average if traveldates are in more than one month - let monthFrom = Number(from.split("-")[1]) - let monthTo = Number(to.split("-")[1]) - + // to calculate average if traveldates are in more than one month let travelPeriods = [] if (monthFrom === monthTo) { let element = { month: monthFrom, - days: Number(to.split("-")[2]) - Number(from.split("-")[2]) + days: dayTo - dayFrom } travelPeriods.push(element) } else { @@ -131,12 +140,12 @@ async function search(from, to, queries) { if (index === monthFrom) { element = { month: index, - days: 32 - Number(from.split("-")[2]) + days: 32 - dayFrom } } else if (index === monthTo) { element = { month: index, - days: Number(to.split("-")[2]) + days: dayTo } } else { element = { @@ -148,20 +157,20 @@ async function search(from, to, queries) { } } - // calculate score + // calculate detail scores let detailScores = await Promise.all(travelPeriods.map(async period => { period.climate = await getAllRegionsWithClimatePerMonth(period.month) period.scores = {} Object.entries(queries).forEach(([key, value]) => { // console.log('key',key) // console.log('val', value) - period.scores[key] = calculateScores(key, period.climate, value.split(',')[0], value.split(',')[1], minMax) + period.scores[key] = calculateScores(key, period.climate, value[0], value[1], minMax) }); return period })); - // TODO: calculate ratio + // calculate ratio and transform into target object return { results: transformer.transform(detailScores), debug: { @@ -216,10 +225,17 @@ async function getQueryRows(sql) { return rows } -function getRegionIdsWithMeteostatData() { - console.log('getRegionIdsWithMeteostatData') - //return await getQueryRows(`SELECT * FROM regions WHERE meteostat_id IS NOT NULL`) - return getQueryRows(`SELECT region_id FROM region_climate GROUP BY region_id`) +function getAllRegions() { + const sql = `SELECT + regions.id AS region_id, + regions.region AS name, + regions.country_id AS country_id, + countries.country AS country, + regions.meteostat_id AS meteostat_id + FROM regions + JOIN countries + ON regions.country_id = countries.id` + return getQueryRows(sql) } function getClimatePerRegionAndMonth(regionId, month) { @@ -242,4 +258,19 @@ function getAllRegionsWithClimatePerMonth(month) { ROUND(AVG(region_climate.sunshine), 1) AS sunhours FROM region_climate JOIN regions ON region_climate.region_id = regions.id WHERE region_climate.month = ${month} GROUP BY region_id` return getQueryRows(sql) +} + +function oldToNewQuerySyntax(queries) { + let res = {} + try { + if (queries.temperature_mean) res.temperature_mean = [queries.temperature_mean.split(',')[0], queries.temperature_mean.split(',')[1]] + if (queries.percipitation) res.percipitation = [queries.percipitation.split(',')[0], queries.percipitation.split(',')[1]] + if (queries.raindays) res.raindays = [queries.raindays.split(',')[0], queries.raindays.split(',')[1]] + if (queries.sunhours) res.sunhours = [queries.sunhours.split(',')[0], queries.sunhours.split(',')[1]] + console.log('queries successfully transformed'); + } catch (error) { + console.log('queries are ok'); + return queries + } + return res } \ No newline at end of file diff --git a/backend/base64.js b/backend/base64.js new file mode 100644 index 0000000..d4d9583 --- /dev/null +++ b/backend/base64.js @@ -0,0 +1,23 @@ +/** + * Encodes an object as base64 string.. + * @param obj The object to encode + */ +exports.objToBase64 = function(obj) { + return btoa(JSON.stringify(obj)); +} + +/** + * Decodes a base64 encoded object. + * @param base64 Encoded object + */ +exports.base64ToObj = function(base64) { + return JSON.parse(atob(base64)); +} + +function atob(base64) { + return Buffer.from(base64, 'base64').toString('binary') +} + +function btoa(string) { + return Buffer.from(string).toString('base64') +} \ No newline at end of file From b23a7f6858a49744293bf471fd4f89ffec548f69 Mon Sep 17 00:00:00 2001 From: Timo Volkmann Date: Wed, 10 Jun 2020 08:07:15 +0200 Subject: [PATCH 11/20] initial backend, first score calc draft --- .gitignore | 2 + backend/.gitkeep | 0 backend/app.js | 41 +++++ backend/package-lock.json | 374 ++++++++++++++++++++++++++++++++++++++ backend/package.json | 14 ++ backend/sampledata.js | 16 ++ backend/score.js | 31 ++++ 7 files changed, 478 insertions(+) delete mode 100644 backend/.gitkeep create mode 100644 backend/app.js create mode 100644 backend/package-lock.json create mode 100644 backend/package.json create mode 100644 backend/sampledata.js create mode 100644 backend/score.js diff --git a/.gitignore b/.gitignore index b039da1..a6deceb 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ # System Files .DS_Store Thumbs.db + +node_modules \ No newline at end of file diff --git a/backend/.gitkeep b/backend/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/backend/app.js b/backend/app.js new file mode 100644 index 0000000..e9f4a02 --- /dev/null +++ b/backend/app.js @@ -0,0 +1,41 @@ +var express = require('express') +var sampledata = require('./sampledata') + +const app = express() +const port = 3000 + +const sampleRegions = [ + { + id: 29837, + name: "Timbuktu", + country: "Mali" + } +] +const samplePresets = [ + { + id: 29837, + parameter: "temperature", + label: "warm", + values: [22, 25] + } +] + +app.get('/', (req, res) => res.send('Hello Timo!')) +app.get('/v1/regions', (req, res) => res.json(sampleRegions)) +app.get('/v1/presets', (req, res) => res.json(samplePresets)) +app.get('/v1/search', (req, res) => { + // check query params + let response = {} + + response.meta = { + params: req.params, + query: req.query, + headers: req.headers + } + + response.data = sampledata.search_response_model + + res.json(response) +}) + +app.listen(port, () => console.log(`Travopti backend listening at http://localhost:${port}`)) \ No newline at end of file diff --git a/backend/package-lock.json b/backend/package-lock.json new file mode 100644 index 0000000..fd25ecb --- /dev/null +++ b/backend/package-lock.json @@ -0,0 +1,374 @@ +{ + "name": "cc-data-backend", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + } + } +} diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000..87a7cb8 --- /dev/null +++ b/backend/package.json @@ -0,0 +1,14 @@ +{ + "name": "cc-data-backend", + "version": "1.0.0", + "description": "", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "express": "^4.17.1" + } +} diff --git a/backend/sampledata.js b/backend/sampledata.js new file mode 100644 index 0000000..5c5e8a0 --- /dev/null +++ b/backend/sampledata.js @@ -0,0 +1,16 @@ +var sampledata = {} + +sampledata.search_response_model = [ + { + region_id: 1, + name: 'Chicago', + country: 'USA', + avg_score: 0.0, + scores: { + temperature: 0.0, + humidity: 0.0, + } + } +] + +module.exports = sampledata; \ No newline at end of file diff --git a/backend/score.js b/backend/score.js new file mode 100644 index 0000000..4bba82e --- /dev/null +++ b/backend/score.js @@ -0,0 +1,31 @@ +/** + * + */ +const multiplier_temperature = 5; + + +/** + * + * @param {...any} scores expects objects which contains score and their weight + */ +function calculateAvgScore(...scores) { + return avgScore = scores.reduce((total, score) => total += score) / scores.length; +} + +function calculateScoreRange(min, max, multiplier, targetVal, sLowVal, sHighVal) { + // return full score when in range + if (targetVal >= sLowVal && targetVal <= sHighVal) return 10; + // choose value with smallest distance + let sVal = Math.abs(targetVal - sLowVal) < Math.abs(targetVal - sHighVal) ? sLowVal : sHighVal; + + return calculateScore(min, max, multiplier, targetVal, sVal); +} + +function calculateScore(min, max, multiplier, targetVal, searchVal) { + + let score = 1 - (Math.abs(searchVal - targetVal) / (max - min) * multiplier); + return score <= 0 ? 0 : score * 10; +} + +console.log('Calc') +console.log(calculateScoreRange(-15, 45, 5, 24, 15, 22)) \ No newline at end of file From fccd897f79440e118e59f3c2a82bb61bb90bc84e Mon Sep 17 00:00:00 2001 From: Timo Volkmann Date: Wed, 10 Jun 2020 09:20:15 +0200 Subject: [PATCH 12/20] add sql connection, config through ENV --- Scripts/setup.sql | 30 ++++++++--------- backend/.env | 5 +++ backend/climate.js | 0 backend/dbsql.js | 13 ++++++++ backend/package-lock.json | 68 +++++++++++++++++++++++++++++++++++++++ backend/package.json | 4 ++- 6 files changed, 104 insertions(+), 16 deletions(-) create mode 100644 backend/.env create mode 100644 backend/climate.js create mode 100644 backend/dbsql.js diff --git a/Scripts/setup.sql b/Scripts/setup.sql index b4378f3..5984893 100644 --- a/Scripts/setup.sql +++ b/Scripts/setup.sql @@ -14,19 +14,19 @@ -- Exportiere Datenbank Struktur für travopti DROP DATABASE IF EXISTS `travopti`; -CREATE DATABASE IF NOT EXISTS `travopti` /*!40100 DEFAULT CHARACTER SET latin1 COLLATE latin1_german1_ci */; +CREATE DATABASE IF NOT EXISTS `travopti` /*!40100 DEFAULT CHARACTER SET latin1 */; USE `travopti`; -- Exportiere Struktur von Tabelle travopti.countries DROP TABLE IF EXISTS `countries`; CREATE TABLE IF NOT EXISTS `countries` ( `id` int(11) NOT NULL AUTO_INCREMENT, - `country` varchar(255) COLLATE latin1_german1_ci NOT NULL, + `country` varchar(255) NOT NULL, `created_at` timestamp NOT NULL DEFAULT current_timestamp(), `updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), PRIMARY KEY (`id`), UNIQUE KEY `country` (`country`) -) ENGINE=InnoDB AUTO_INCREMENT=64 DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci; +) ENGINE=InnoDB AUTO_INCREMENT=64 DEFAULT CHARSET=utf8mb4; -- Exportiere Daten aus Tabelle travopti.countries: ~37 rows (ungefähr) DELETE FROM `countries`; @@ -75,9 +75,9 @@ INSERT INTO `countries` (`id`, `country`, `created_at`, `updated_at`) VALUES DROP TABLE IF EXISTS `regions`; CREATE TABLE IF NOT EXISTS `regions` ( `id` int(11) NOT NULL AUTO_INCREMENT, - `region` varchar(255) COLLATE latin1_german1_ci NOT NULL, + `region` varchar(255) NOT NULL, `country_id` int(11) DEFAULT NULL, - `meteostat_id` int(11) DEFAULT NULL, + `meteostat_id` varchar(11) DEFAULT NULL, `lon` double(22,0) DEFAULT NULL, `lat` double(22,0) DEFAULT NULL, `created_at` timestamp NOT NULL DEFAULT current_timestamp(), @@ -85,7 +85,7 @@ CREATE TABLE IF NOT EXISTS `regions` ( PRIMARY KEY (`id`) USING BTREE, KEY `FK_regions_countries` (`country_id`) USING BTREE, CONSTRAINT `FK_regions_countries` FOREIGN KEY (`country_id`) REFERENCES `countries` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=127 DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci; +) ENGINE=InnoDB AUTO_INCREMENT=127 DEFAULT CHARSET=utf8mb4; -- Exportiere Daten aus Tabelle travopti.regions: ~47 rows (ungefähr) DELETE FROM `regions`; @@ -160,7 +160,7 @@ CREATE TABLE IF NOT EXISTS `regions_byt` ( PRIMARY KEY (`id`), UNIQUE KEY `region_id` (`region_id`,`travelstyle`) USING BTREE, CONSTRAINT `FK_regions_byt_regions` FOREIGN KEY (`region_id`) REFERENCES `regions` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=191 DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci; +) ENGINE=InnoDB AUTO_INCREMENT=191 DEFAULT CHARSET=utf8mb4; -- Exportiere Daten aus Tabelle travopti.regions_byt: ~141 rows (ungefähr) DELETE FROM `regions_byt`; @@ -323,7 +323,7 @@ CREATE TABLE IF NOT EXISTS `regions_trivago` ( UNIQUE KEY `region_id_year_month` (`region_id`,`year`,`month`), KEY `FK_regions_trivago_regions` (`region_id`), CONSTRAINT `FK_regions_trivago_regions` FOREIGN KEY (`region_id`) REFERENCES `regions` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=1278 DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci; +) ENGINE=InnoDB AUTO_INCREMENT=1278 DEFAULT CHARSET=utf8mb4; -- Exportiere Daten aus Tabelle travopti.regions_trivago: ~940 rows (ungefähr) DELETE FROM `regions_trivago`; @@ -1289,7 +1289,7 @@ CREATE TABLE IF NOT EXISTS `region_climate` ( UNIQUE KEY `region_id_year_month` (`region_id`,`year`,`month`), KEY `FK_region_climate_regions` (`region_id`), CONSTRAINT `FK_region_climate_regions` FOREIGN KEY (`region_id`) REFERENCES `regions` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- Exportiere Daten aus Tabelle travopti.region_climate: ~0 rows (ungefähr) DELETE FROM `region_climate`; @@ -1300,14 +1300,14 @@ DELETE FROM `region_climate`; DROP TABLE IF EXISTS `search_presets`; CREATE TABLE IF NOT EXISTS `search_presets` ( `id` int(11) NOT NULL AUTO_INCREMENT, - `parameter` varchar(255) COLLATE latin1_german1_ci NOT NULL, - `name` varchar(255) COLLATE latin1_german1_ci NOT NULL, + `parameter` varchar(255) NOT NULL, + `name` varchar(255) NOT NULL, `value_1` int(11) NOT NULL, `value_2` int(11) DEFAULT NULL, `created_at` timestamp NOT NULL DEFAULT current_timestamp(), `updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- Exportiere Daten aus Tabelle travopti.search_presets: ~0 rows (ungefähr) DELETE FROM `search_presets`; @@ -1318,11 +1318,11 @@ DELETE FROM `search_presets`; DROP TABLE IF EXISTS `search_tags`; CREATE TABLE IF NOT EXISTS `search_tags` ( `id` int(11) NOT NULL AUTO_INCREMENT, - `searchtag` varchar(255) COLLATE latin1_german1_ci NOT NULL, + `searchtag` varchar(255) NOT NULL, `created_at` timestamp NOT NULL DEFAULT current_timestamp(), `updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- Exportiere Daten aus Tabelle travopti.search_tags: ~0 rows (ungefähr) DELETE FROM `search_tags`; @@ -1343,7 +1343,7 @@ CREATE TABLE IF NOT EXISTS `user_feedback` ( KEY `FK_user_feedback_search_tags` (`tag_id`), CONSTRAINT `FK_user_feedback_regions` FOREIGN KEY (`region_id`) REFERENCES `regions` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `FK_user_feedback_search_tags` FOREIGN KEY (`tag_id`) REFERENCES `search_tags` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- Exportiere Daten aus Tabelle travopti.user_feedback: ~0 rows (ungefähr) DELETE FROM `user_feedback`; diff --git a/backend/.env b/backend/.env new file mode 100644 index 0000000..afb8d07 --- /dev/null +++ b/backend/.env @@ -0,0 +1,5 @@ +METEOSTAT_API_KEY=LMlDskju +DB_HOST=192.168.43.52 +DB_USER=root +DB_PASSWORD=devtest +DB_PORT=3307 \ No newline at end of file diff --git a/backend/climate.js b/backend/climate.js new file mode 100644 index 0000000..e69de29 diff --git a/backend/dbsql.js b/backend/dbsql.js new file mode 100644 index 0000000..d40689e --- /dev/null +++ b/backend/dbsql.js @@ -0,0 +1,13 @@ +var mysql = require('mysql'); +require('dotenv').config() +console.log(process.env); + +var connection = mysql.createConnection({ + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + port: process.env.DB_PORT +}); + +connection.connect(); + \ No newline at end of file diff --git a/backend/package-lock.json b/backend/package-lock.json index fd25ecb..a4a6d28 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -18,6 +18,11 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "bignumber.js": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", + "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" + }, "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -63,6 +68,11 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -81,6 +91,11 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -192,6 +207,11 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -230,6 +250,17 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "mysql": { + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", + "integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==", + "requires": { + "bignumber.js": "9.0.0", + "readable-stream": "2.3.7", + "safe-buffer": "5.1.2", + "sqlstring": "2.3.1" + } + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -253,6 +284,11 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -283,6 +319,20 @@ "unpipe": "1.0.0" } }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -336,11 +386,24 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, + "sqlstring": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", + "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=" + }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", @@ -360,6 +423,11 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", diff --git a/backend/package.json b/backend/package.json index 87a7cb8..4ff4bc6 100644 --- a/backend/package.json +++ b/backend/package.json @@ -9,6 +9,8 @@ "author": "", "license": "ISC", "dependencies": { - "express": "^4.17.1" + "dotenv": "^8.2.0", + "express": "^4.17.1", + "mysql": "^2.18.1" } } From 67ec5a132cd73c45fb20bd6ef3f09040436917c3 Mon Sep 17 00:00:00 2001 From: Timo Volkmann Date: Fri, 12 Jun 2020 08:32:01 +0200 Subject: [PATCH 13/20] climate api implementation 1 --- Scripts/setup.sql | 34 +- backend/.env | 4 +- backend/climate.js | 64 +++ backend/dbsql.js | 68 ++- backend/package-lock.json | 1129 +++++++++++++++++++++++++++++++++++++ backend/package.json | 7 +- 6 files changed, 1282 insertions(+), 24 deletions(-) diff --git a/Scripts/setup.sql b/Scripts/setup.sql index 5984893..45c5ffe 100644 --- a/Scripts/setup.sql +++ b/Scripts/setup.sql @@ -91,30 +91,30 @@ CREATE TABLE IF NOT EXISTS `regions` ( DELETE FROM `regions`; /*!40000 ALTER TABLE `regions` DISABLE KEYS */; INSERT INTO `regions` (`id`, `region`, `country_id`, `meteostat_id`, `lon`, `lat`, `created_at`, `updated_at`) VALUES - (1, 'Buenos Aires', 1, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (2, 'Melbourne', 2, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (3, 'Sydney', 2, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (4, 'Bruessel', 3, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (5, 'Rio de Janei..', 4, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (6, 'Sao Paolo', 4, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (7, 'Toronto', 5, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (8, 'Santiago de ..', 6, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (9, 'Peking', 7, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (10, 'Shanghai', 7, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (11, 'Bogota', 8, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (12, 'Kairo', 9, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (13, 'London', 10, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (1, 'Buenos Aires', 1, '87585', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (2, 'Melbourne', 2, '94866', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (3, 'Sydney', 2, '94767', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (4, 'Bruessel', 3, '06458', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (5, 'Rio de Janeiro', 4, '83755', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (6, 'Sao Paolo', 4, '83780', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (7, 'Toronto', 5, '71624', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (8, 'Santiago de Chile', 6, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (9, 'Peking', 7, '54511', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (10, 'Shanghai', 7, '58362', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (11, 'Bogota', 8, '80222', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (12, 'Kairo', 9, '62366', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (13, 'London', 10, '03772', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (14, 'Paris', 11, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (15, 'Berlin', 12, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (16, 'Athen', 13, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (17, 'Hongkong', 14, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (18, 'Reykjavik', 15, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (18, 'Reykjavik', 15, '04030', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (19, 'Delhi', 16, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (20, 'Mumbai', 16, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (20, 'Mumbai', 16, '43003', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (21, 'Dublin', 17, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (22, 'Rom', 18, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (23, 'Tokio', 19, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (24, 'Kuala Lumpur', 20, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (24, 'Kuala Lumpur', 20, '48647', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (25, 'Mexico City', 21, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (26, 'Marrakesch', 22, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (27, 'Amsterdam', 23, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), @@ -136,7 +136,7 @@ INSERT INTO `regions` (`id`, `region`, `country_id`, `meteostat_id`, `lon`, `lat (43, 'Las Vegas', 36, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (44, 'Miami', 36, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (45, 'New York', 36, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (46, 'San Francis ..', 36, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (46, 'San Francisco', 36, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (47, 'Dubai', 37, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'); /*!40000 ALTER TABLE `regions` ENABLE KEYS */; diff --git a/backend/.env b/backend/.env index afb8d07..b90e137 100644 --- a/backend/.env +++ b/backend/.env @@ -1,5 +1,5 @@ METEOSTAT_API_KEY=LMlDskju -DB_HOST=192.168.43.52 +DB_HOST=localhost DB_USER=root DB_PASSWORD=devtest -DB_PORT=3307 \ No newline at end of file +DB_PORT=3306 \ No newline at end of file diff --git a/backend/climate.js b/backend/climate.js index e69de29..ab6a290 100644 --- a/backend/climate.js +++ b/backend/climate.js @@ -0,0 +1,64 @@ +require('dotenv').config() +const mysql = require('mysql2/promise'); +const axios = require('axios') + +const startDate = '2018-01' +const endDate = '2018-12' + +async function main() { + const connection = await mysql.createConnection({ + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + port: process.env.DB_PORT, + database: 'travopti' + }); + const [result, fields] = await connection.execute(`SELECT * FROM regions WHERE meteostat_id IS NOT NULL`) + + Promise.all(result.map(res => { + console.log(res) + return axios.get(`https://api.meteostat.net/v1/climate/normals?station=${res.meteostat_id}&key=${process.env.METEOSTAT_API_KEY}`) + })).then(responses => { + responses.forEach(response => { + //console.log(response.data) + }) + //console.log(responses[0]) + }) + + connection.end(); +} + +async function createClimateObject(src) { + let response = await axios.get(`https://api.meteostat.net/v1/climate/normals?station=${res.meteostat_id}&key=${process.env.METEOSTAT_API_KEY}`) + let temperatures = response.data.data.temperature + let precipitations = response.data.data.precipitation + let sunshines = response.data.data.sunshine + + console.log(Object.entries(temperatures)[0]) + let results = [] + for (let index = 1; index <= 12; index++) { + //const element = array[index]; + let result = { + region: src.region, + region_id: src.id, + month: index, + temperature: Object.values(temperatures)[index], + precipitation: Object.values(precipitations)[index], + sunshine: Object.values(sunshines)[index], + } + results.push(result) + } + return results +} + +async function createClimateObjectFromLastYear(src) { + let response = await axios.get(`https://api.meteostat.net/v1/history/monthly?station=${res.meteostat_id}&start=${startDate}&end=${endDate}&key=${process.env.METEOSTAT_API_KEY}`) + + + let result = { + region_id: src.id, + + } +} + +main() \ No newline at end of file diff --git a/backend/dbsql.js b/backend/dbsql.js index d40689e..a5d7b72 100644 --- a/backend/dbsql.js +++ b/backend/dbsql.js @@ -1,13 +1,73 @@ var mysql = require('mysql'); require('dotenv').config() -console.log(process.env); var connection = mysql.createConnection({ host: process.env.DB_HOST, user: process.env.DB_USER, password: process.env.DB_PASSWORD, - port: process.env.DB_PORT + port: process.env.DB_PORT, + database: 'travopti' }); -connection.connect(); - \ No newline at end of file +// var pool = mysql.createPool({ +// connectionLimit: 10, +// host: process.env.DB_HOST, +// user: process.env.DB_USER, +// password: process.env.DB_PASSWORD, +// port: process.env.DB_PORT, +// database: 'travopti' +// }); + +// let travoptidb = {} +// travoptidb.all = () => { +// return new Promise((resolve, reject) => { +// pool.query(`SELECT * FROM regions`, (err, results) => { +// if (err) { +// return reject(err) +// } +// return resolve(results) +// }) +// }) +// } + +connection.connect((err) => { + if (err) throw err; + console.log('Database connected!') +}); + + +exports.getRegions = () => { + let sql = `SELECT * FROM regions`; + console.log(connection.state) + if (connection.state === 'disconnected') { + setTimeout(() => console.log('waiting...'), 1000); + } + console.log('executed') + let res = {} + connection.query(sql, (error, results, fields) => { + if (error) { + return console.error(error.message); + } + console.log('innercallback(1)') + res = results[0] + }); + console.log('outsidecallback(2)') + return res; +} + +exports.getBYTdata = () => { + connection.query(`SELECT * FROM regions_byt`, (error, results, fields) => { + if (error) { + return console.error(error.message); + } + console.log(results[0]) + nres = results.map((obj) => { + return obj.region + }) + //console.log(nres); + }); +} + +exports.end = () => connection.end(); + +// module.exports = connection; diff --git a/backend/package-lock.json b/backend/package-lock.json index a4a6d28..9a19ff0 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -4,6 +4,33 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -13,16 +40,89 @@ "negotiator": "0.6.2" } }, + "ansi-align": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", + "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "dev": true, + "requires": { + "string-width": "^3.0.0" + }, + "dependencies": { + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "ansicolors": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", + "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=" + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "axios": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "requires": { + "follow-redirects": "1.5.10" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, "bignumber.js": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" }, + "binary-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", + "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", + "dev": true + }, "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -40,11 +140,192 @@ "type-is": "~1.6.17" } }, + "boxen": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", + "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", + "dev": true, + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "cli-boxes": "^2.2.0", + "string-width": "^4.1.0", + "term-size": "^2.1.0", + "type-fest": "^0.8.1", + "widest-line": "^3.1.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + } + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "cardinal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", + "integrity": "sha1-fMEFXYItISlU0HsIXeolHMe8VQU=", + "requires": { + "ansicolors": "~0.3.2", + "redeyed": "~2.1.0" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "chokidar": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz", + "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.4.0" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "cli-boxes": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.0.tgz", + "integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==", + "dev": true + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + } + }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -73,6 +354,12 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -81,6 +368,32 @@ "ms": "2.0.0" } }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true + }, + "denque": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", + "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -91,26 +404,67 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "dot-prop": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", + "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, "dotenv": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "dev": true + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -153,6 +507,15 @@ "vary": "~1.1.2" } }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -167,6 +530,24 @@ "unpipe": "~1.0.0" } }, + "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" + } + } + } + }, "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", @@ -177,6 +558,91 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + }, + "generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "requires": { + "is-property": "^1.0.2" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "global-dirs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", + "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==", + "dev": true, + "requires": { + "ini": "^1.3.5" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "dev": true + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -197,21 +663,195 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", + "dev": true + }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-installed-globally": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", + "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", + "dev": true, + "requires": { + "global-dirs": "^2.0.1", + "is-path-inside": "^3.0.1" + } + }, + "is-npm": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", + "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", + "dev": true + }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", + "dev": true + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dev": true, + "requires": { + "json-buffer": "3.0.0" + } + }, + "latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "dev": true, + "requires": { + "package-json": "^6.3.0" + } + }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -245,6 +885,27 @@ "mime-db": "1.44.0" } }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -261,11 +922,117 @@ "sqlstring": "2.3.1" } }, + "mysql2": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.1.0.tgz", + "integrity": "sha512-9kGVyi930rG2KaHrz3sHwtc6K+GY9d8wWk1XRSYxQiunvGcn4DwuZxOwmK11ftuhhwrYDwGx9Ta4VBwznJn36A==", + "requires": { + "cardinal": "^2.1.1", + "denque": "^1.4.1", + "generate-function": "^2.3.1", + "iconv-lite": "^0.5.0", + "long": "^4.0.0", + "lru-cache": "^5.1.1", + "named-placeholders": "^1.1.2", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.1" + }, + "dependencies": { + "iconv-lite": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", + "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "named-placeholders": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.2.tgz", + "integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==", + "requires": { + "lru-cache": "^4.1.3" + }, + "dependencies": { + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + } + } + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, + "nodemon": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.4.tgz", + "integrity": "sha512-Ltced+hIfTmaS28Zjv1BM552oQ3dbwPqI4+zI0SLgq+wpJhSyqgYude/aZa/3i31VCQWMfXJVxvu86abcam3uQ==", + "dev": true, + "requires": { + "chokidar": "^3.2.2", + "debug": "^3.2.6", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.0.4", + "pstree.remy": "^1.1.7", + "semver": "^5.7.1", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.2", + "update-notifier": "^4.0.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "dev": true + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -274,6 +1041,41 @@ "ee-first": "1.1.1" } }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "dev": true + }, + "package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "dev": true, + "requires": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -284,6 +1086,18 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -298,6 +1112,36 @@ "ipaddr.js": "1.9.1" } }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pupa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.0.1.tgz", + "integrity": "sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==", + "dev": true, + "requires": { + "escape-goat": "^2.0.0" + } + }, "qs": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", @@ -319,6 +1163,18 @@ "unpipe": "1.0.0" } }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -333,6 +1189,50 @@ "util-deprecate": "~1.0.1" } }, + "readdirp": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", + "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "redeyed": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", + "integrity": "sha1-iYS1gV2ZyyIEacme7v/jiRPmzAs=", + "requires": { + "esprima": "~4.0.0" + } + }, + "registry-auth-token": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.1.1.tgz", + "integrity": "sha512-9bKS7nTl9+/A1s7tnPeGrUpRcVY+LUh7bfFgzpndALdPfXQBfQV77rQVtqgUV3ti4vc/Ik81Ex8UJDWDQ12zQA==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, + "registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -343,6 +1243,29 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dev": true, + "requires": { + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "send": { "version": "0.17.1", "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", @@ -370,6 +1293,11 @@ } } }, + "seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4=" + }, "serve-static": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", @@ -386,6 +1314,12 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, "sqlstring": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", @@ -396,6 +1330,46 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -404,11 +1378,71 @@ "safe-buffer": "~5.1.0" } }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "term-size": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz", + "integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==", + "dev": true + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, + "touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "requires": { + "nopt": "~1.0.10" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -418,11 +1452,68 @@ "mime-types": "~2.1.24" } }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "undefsafe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", + "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==", + "dev": true, + "requires": { + "debug": "^2.2.0" + } + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "requires": { + "crypto-random-string": "^2.0.0" + } + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "update-notifier": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.0.tgz", + "integrity": "sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew==", + "dev": true, + "requires": { + "boxen": "^4.2.0", + "chalk": "^3.0.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.3.1", + "is-npm": "^4.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.0.0", + "pupa": "^2.0.1", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + } + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dev": true, + "requires": { + "prepend-http": "^2.0.0" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -437,6 +1528,44 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "requires": { + "string-width": "^4.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" } } } diff --git a/backend/package.json b/backend/package.json index 4ff4bc6..9ffa857 100644 --- a/backend/package.json +++ b/backend/package.json @@ -9,8 +9,13 @@ "author": "", "license": "ISC", "dependencies": { + "axios": "^0.19.2", "dotenv": "^8.2.0", "express": "^4.17.1", - "mysql": "^2.18.1" + "mysql": "^2.18.1", + "mysql2": "^2.1.0" + }, + "devDependencies": { + "nodemon": "^2.0.4" } } From 92b9fe3e9dc9b43f86a37b033d39b76e93e6184c Mon Sep 17 00:00:00 2001 From: Timo Volkmann Date: Fri, 12 Jun 2020 10:48:59 +0200 Subject: [PATCH 14/20] first draft for database population (climate) --- Scripts/setup.sql | 6 +++-- backend/climate.js | 58 ++++++++++++++++++++++++++++++---------------- 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/Scripts/setup.sql b/Scripts/setup.sql index 45c5ffe..12b48c7 100644 --- a/Scripts/setup.sql +++ b/Scripts/setup.sql @@ -110,7 +110,7 @@ INSERT INTO `regions` (`id`, `region`, `country_id`, `meteostat_id`, `lon`, `lat (17, 'Hongkong', 14, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (18, 'Reykjavik', 15, '04030', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (19, 'Delhi', 16, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), - (20, 'Mumbai', 16, '43003', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), + (20, 'Mumbai', 16, '43002', NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (21, 'Dublin', 17, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (22, 'Rom', 18, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), (23, 'Tokio', 19, NULL, NULL, NULL, '2020-06-09 22:11:03', '2020-06-09 22:11:03'), @@ -1278,7 +1278,9 @@ CREATE TABLE IF NOT EXISTS `region_climate` ( `region_id` int(11) NOT NULL, `year` int(11) NOT NULL, `month` int(11) NOT NULL, - `temperature` float DEFAULT NULL, + `temperature_mean` float DEFAULT NULL, + `temperature_mean_min` float DEFAULT NULL, + `temperature_mean_max` float DEFAULT NULL, `percipitation` float DEFAULT NULL, `raindays` int(11) DEFAULT NULL, `sunshine` float DEFAULT NULL, diff --git a/backend/climate.js b/backend/climate.js index ab6a290..41ea1e9 100644 --- a/backend/climate.js +++ b/backend/climate.js @@ -15,38 +15,47 @@ async function main() { }); const [result, fields] = await connection.execute(`SELECT * FROM regions WHERE meteostat_id IS NOT NULL`) - Promise.all(result.map(res => { - console.log(res) - return axios.get(`https://api.meteostat.net/v1/climate/normals?station=${res.meteostat_id}&key=${process.env.METEOSTAT_API_KEY}`) - })).then(responses => { - responses.forEach(response => { - //console.log(response.data) - }) - //console.log(responses[0]) - }) + // Promise.all(result.map(res => { + // return createClimateObject(res) + // })).then(results => { + // let flat = results.reduce((total, element) => total.concat(element), []) + // console.log(flat) + // }) + let temp = await Promise.all(result.map(x => createClimateObject(x))) + let finalresults = temp.reduce((total, element) => total.concat(element), []) + + await writeToDatabase(connection, finalresults) + connection.end(); } async function createClimateObject(src) { - let response = await axios.get(`https://api.meteostat.net/v1/climate/normals?station=${res.meteostat_id}&key=${process.env.METEOSTAT_API_KEY}`) - let temperatures = response.data.data.temperature - let precipitations = response.data.data.precipitation - let sunshines = response.data.data.sunshine - - console.log(Object.entries(temperatures)[0]) + let response + try { + response = await axios.get(`https://api.meteostat.net/v1/climate/normals?station=${src.meteostat_id}&key=${process.env.METEOSTAT_API_KEY}`) + } catch (error) { + console.log("skipping: couldn't find results for following region: ") + console.log(src.region + " with meteostat_id " + src.meteostat_id) + return [] + } + if (!response.data.data) { + console.log("skipping: no data for station meteostat_id " + src.meteostat_id + " (" + src.region + ")") + return [] + } let results = [] for (let index = 1; index <= 12; index++) { - //const element = array[index]; let result = { region: src.region, region_id: src.id, month: index, - temperature: Object.values(temperatures)[index], - precipitation: Object.values(precipitations)[index], - sunshine: Object.values(sunshines)[index], + temperature: Object.values(response.data.data.temperature)[index - 1] ? Object.values(response.data.data.temperature)[index - 1] : null, + temperature_min: Object.values(response.data.data.temperature_min)[index - 1] ? Object.values(response.data.data.temperature_min)[index - 1] : null, + temperature_max: Object.values(response.data.data.temperature_max)[index - 1] ? Object.values(response.data.data.temperature_max)[index - 1] : null, + precipitation: Object.values(response.data.data.precipitation)[index - 1] ? Object.values(response.data.data.precipitation)[index - 1] : null, + sunshine: Object.values(response.data.data.sunshine)[index - 1] ? Object.values(response.data.data.sunshine)[index - 1] : null, } - results.push(result) + results.push(result) } return results } @@ -61,4 +70,13 @@ async function createClimateObjectFromLastYear(src) { } } +async function writeToDatabase(dbConnection, src) { + src.forEach(async (element) => { + //console.log(element) + await dbConnection.execute(` + INSERT INTO region_climate (region_id, year, month, temperature_mean, temperature_mean_min, temperature_mean_max, percipitation, sunshine) + VALUES (${element.region_id}, 0, ${element.month}, ${element.temperature}, ${element.temperature_min}, ${element.temperature_max}, ${element.precipitation}, ${element.sunshine});`) + }); +} + main() \ No newline at end of file From e1a788c56bc94a3a96dd4b603fe7153450a86869 Mon Sep 17 00:00:00 2001 From: Timo Volkmann Date: Fri, 12 Jun 2020 11:30:40 +0200 Subject: [PATCH 15/20] finished climate importer --- backend/climate.js | 74 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 14 deletions(-) diff --git a/backend/climate.js b/backend/climate.js index 41ea1e9..d45f167 100644 --- a/backend/climate.js +++ b/backend/climate.js @@ -2,8 +2,8 @@ require('dotenv').config() const mysql = require('mysql2/promise'); const axios = require('axios') -const startDate = '2018-01' -const endDate = '2018-12' +const rangeStartDate = '2018-01' +const rangeEndDate = '2018-12' async function main() { const connection = await mysql.createConnection({ @@ -23,10 +23,15 @@ async function main() { // }) let temp = await Promise.all(result.map(x => createClimateObject(x))) - let finalresults = temp.reduce((total, element) => total.concat(element), []) + let final = temp.reduce((total, element) => total.concat(element), []) - await writeToDatabase(connection, finalresults) + //await writeToDatabase(connection, final) + let temp2 = await Promise.all(result.map(x => createClimateObjectFrom(x))) + let final2 = temp2.reduce((total, element) => total.concat(element), []) + + await writeToDatabase(connection, final2) + connection.end(); } @@ -60,22 +65,63 @@ async function createClimateObject(src) { return results } -async function createClimateObjectFromLastYear(src) { - let response = await axios.get(`https://api.meteostat.net/v1/history/monthly?station=${res.meteostat_id}&start=${startDate}&end=${endDate}&key=${process.env.METEOSTAT_API_KEY}`) - - - let result = { - region_id: src.id, - +async function createClimateObjectFrom(src, startDate = '2010-01', endDate = '2018-12') { + let response + try { + response = await axios.get(`https://api.meteostat.net/v1/history/monthly?station=${src.meteostat_id}&start=${startDate}&end=${endDate}&key=${process.env.METEOSTAT_API_KEY}`) + } catch (error) { + console.log("skipping createClimateObjectFrom: couldn't find results for following region: ") + console.log(src.region + " with meteostat_id " + src.meteostat_id) + console.log(error) + return [] } + if (!response.data.data) { + console.log("skipping: no data for station meteostat_id " + src.meteostat_id + " (" + src.region + ")") + return [] + } + let results = response.data.data.map(element => { + let result = { + region: src.region, + region_id: src.id, + year: element.month.split("-")[0], + month: element.month.split("-")[1], + temperature: element.temperature_mean, + temperature_min: element.temperature_mean_min, + temperature_max: element.temperature_mean_max, + precipitation: element.precipitation, + raindays: element.raindays, + sunshine: element.sunshine, + humidity: element.humidity ? element.humidity : null + } + //console.log(result) + return result + }) + return results } async function writeToDatabase(dbConnection, src) { src.forEach(async (element) => { //console.log(element) - await dbConnection.execute(` - INSERT INTO region_climate (region_id, year, month, temperature_mean, temperature_mean_min, temperature_mean_max, percipitation, sunshine) - VALUES (${element.region_id}, 0, ${element.month}, ${element.temperature}, ${element.temperature_min}, ${element.temperature_max}, ${element.precipitation}, ${element.sunshine});`) + try { + if (!element.year) { + await dbConnection.execute(` + INSERT INTO region_climate (region_id, year, month, temperature_mean, temperature_mean_min, temperature_mean_max, percipitation, sunshine) + VALUES (${element.region_id}, 0, ${element.month}, ${element.temperature}, ${element.temperature_min}, ${element.temperature_max}, ${element.precipitation}, ${element.sunshine});`) + } else { + await dbConnection.execute(` + INSERT INTO region_climate (region_id, year, month, temperature_mean, temperature_mean_min, temperature_mean_max, percipitation, sunshine, humidity, raindays) + VALUES (${element.region_id}, ${element.year}, ${element.month}, ${element.temperature}, ${element.temperature_min}, ${element.temperature_max}, ${element.precipitation}, ${element.sunshine}, ${element.humidity}, ${element.raindays});`) + } + } catch (error) { + if (error.code !== 'ER_DUP_ENTRY') { + console.log("element which causes problems: ") + console.log(element) + console.log("query which causes problems: ") + console.log(error) + } else { + console.log(element.region + ": " + error.sqlMessage) + } + } }); } From 7bb07af76a7d19c399ac5af36b4e507b825357e9 Mon Sep 17 00:00:00 2001 From: Timo Volkmann Date: Fri, 12 Jun 2020 18:43:09 +0200 Subject: [PATCH 16/20] first score search draft working --- backend/app.js | 151 +++++++++++++++++++++++++++++++++++++++++-- backend/climate.js | 9 +-- backend/dbsql.js | 73 --------------------- backend/mysql.js | 84 ++++++++++++++++++++++++ backend/package.json | 6 +- backend/score.js | 17 +++-- 6 files changed, 243 insertions(+), 97 deletions(-) delete mode 100644 backend/dbsql.js create mode 100644 backend/mysql.js diff --git a/backend/app.js b/backend/app.js index e9f4a02..c15d23d 100644 --- a/backend/app.js +++ b/backend/app.js @@ -1,8 +1,11 @@ var express = require('express') var sampledata = require('./sampledata') +var db = require('./mysql') +var score = require('./score') const app = express() const port = 3000 +const multiplier_temp = 5 const sampleRegions = [ { @@ -20,6 +23,11 @@ const samplePresets = [ } ] +// db.connect((err) => { +// if (err) throw err; +// console.log('Database connected!') +// }); + app.get('/', (req, res) => res.send('Hello Timo!')) app.get('/v1/regions', (req, res) => res.json(sampleRegions)) app.get('/v1/presets', (req, res) => res.json(samplePresets)) @@ -32,10 +40,145 @@ app.get('/v1/search', (req, res) => { query: req.query, headers: req.headers } + + console.log('log params') + console.log(req.query.from) + console.log(req.query.to) + console.log(req.query.temperature) - response.data = sampledata.search_response_model - - res.json(response) + search(req.query.from, req.query.to, { temperature: req.query.temperature }).then(searchResults => { + response.data = searchResults + res.json(response) + }) }) -app.listen(port, () => console.log(`Travopti backend listening at http://localhost:${port}`)) \ No newline at end of file +app.listen(port, () => console.log(`Travopti backend listening at http://localhost:${port}`)) + +async function search(from, to, queries) { + console.log('search') + // validate regex: YYYY-MM-DD + let re = /([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/i; + if (!from.match(re) || !to.match(re)) throw new Error("invalid parameter: " + from + " " + to) + + // -- Prepare search -- + // calculate average if traveldates are in more than one month + let monthFrom = Number(from.split("-")[1]) + let monthTo = Number(to.split("-")[1]) + let values = [] + let virtDays = 0 + if (monthFrom === monthTo) { + let element = { + month: monthFrom, + days: Number(to.split("-")[2]) - Number(from.split("-")[2]) + } + virtDays = element.days + values.push(element) + } else { + for (let index = monthFrom; index <= monthTo; index++) { + let element = {} + if (index === monthFrom) { + element = { + month: index, + days: 31 - Number(from.split("-")[2]) + } + } else if (index === monthTo) { + element = { + month: index, + days: Number(to.split("-")[2]) + } + } else { + element = { + month: index, + days: 30 + } + } + virtDays += element.days + values.push(element) + } + } + // retrieve data from database + const minMax = await getClimateMinMax() + const data = await Promise.all(values.map(async (val) => { + return await getAllClimatePerMonth(val.month) + })) + console.log("data from getAllClimatePerMonth") + //console.log(data) + // calculate score + // TODO: calculate score for all months + let scores_temp + if (queries.temperature) { + scores_temp = calculateTempScores(data[0], queries.temperature.split(',')[0], queries.temperature.split(',')[1], minMax) + } + // TODO: calculate ratio + return { TODO___temperature_scores: scores_temp } +} + +function calculateTempScores(regionDataRows, searchLowParam, searchMaxParam, minMax) { + console.log('calculateTempScores') + console.log(searchLowParam) + console.log(searchMaxParam) + console.log(minMax) + //console.log(regionDataRows) + let result = regionDataRows.map(x => { + console.log(x.temperature_mean) + return { + region_id: x.region_id, + value: x.temperature_mean, + score: Math.round(score.calculateScoreRange(minMax.min.temperature_mean, minMax.max.temperature_mean, multiplier_temp, x.temperature_mean, searchLowParam, searchMaxParam)*100)/100 + } + + }) + return result +} + +async function getClimateMinMax() { + console.log('getClimateMinMax') + const sqlMin = `SELECT + MIN(temperature_mean) AS temperature_mean, + MIN(temperature_mean_min) AS temperature_mean_min, + MIN(temperature_mean_max) AS temperature_mean_max, + MIN(percipitation) AS percipitation, + MIN(sunshine) AS sunshine + FROM region_climate` + const sqlMax = `SELECT + MAX(temperature_mean) AS temperature_mean, + MAX(temperature_mean_min) AS temperature_mean_min, + MAX(temperature_mean_max) AS temperature_mean_max, + MAX(percipitation) AS percipitation, + MAX(sunshine) AS sunshine + FROM region_climate` + const [qResMin, qResMax] = await Promise.all([getQueryRows(sqlMin), getQueryRows(sqlMax)]) + console.log(qResMin) + return { min: qResMin[0], max: qResMax[0] } +} + +async function getQueryRows(sql) { + console.log('getQueryRows') + const [rows, fields] = await db.execute(sql) + return rows +} + +async function getRegionIdsWithMeteostatData() { + console.log('getRegionIdsWithMeteostatData') + //return await getQueryRows(`SELECT * FROM regions WHERE meteostat_id IS NOT NULL`) + return await getQueryRows(`SELECT region_id FROM region_climate GROUP BY region_id`) +} + +async function getClimatePerRegionAndMonth(regionId, month) { + console.log('getClimatePerRegionAndMonth') + const sql = `SELECT region_id, AVG(temperature_mean), AVG(temperature_mean_min), AVG(temperature_mean_max), AVG(percipitation), AVG(sunshine) FROM region_climate WHERE month = ${month} AND region_id = ${regionId}` + return await getQueryRows(sql) +} + +async function getAllClimatePerMonth(month) { + console.log('getAllClimatePerMonth') + const sql = `SELECT + region_id, + ROUND(AVG(temperature_mean), 1) AS temperature_mean, + ROUND(AVG(temperature_mean_min), 1) AS temperature_mean_min, + ROUND(AVG(temperature_mean_max), 1) AS temperature_mean_max, + ROUND(AVG(percipitation), 1) AS percipitation, + ROUND(AVG(sunshine), 1) AS sunshine + FROM region_climate WHERE month = ${month} GROUP BY region_id` + return await getQueryRows(sql) +} \ No newline at end of file diff --git a/backend/climate.js b/backend/climate.js index d45f167..2b2c5ea 100644 --- a/backend/climate.js +++ b/backend/climate.js @@ -15,17 +15,10 @@ async function main() { }); const [result, fields] = await connection.execute(`SELECT * FROM regions WHERE meteostat_id IS NOT NULL`) - // Promise.all(result.map(res => { - // return createClimateObject(res) - // })).then(results => { - // let flat = results.reduce((total, element) => total.concat(element), []) - // console.log(flat) - // }) - let temp = await Promise.all(result.map(x => createClimateObject(x))) let final = temp.reduce((total, element) => total.concat(element), []) - //await writeToDatabase(connection, final) + await writeToDatabase(connection, final) let temp2 = await Promise.all(result.map(x => createClimateObjectFrom(x))) let final2 = temp2.reduce((total, element) => total.concat(element), []) diff --git a/backend/dbsql.js b/backend/dbsql.js deleted file mode 100644 index a5d7b72..0000000 --- a/backend/dbsql.js +++ /dev/null @@ -1,73 +0,0 @@ -var mysql = require('mysql'); -require('dotenv').config() - -var connection = mysql.createConnection({ - host: process.env.DB_HOST, - user: process.env.DB_USER, - password: process.env.DB_PASSWORD, - port: process.env.DB_PORT, - database: 'travopti' -}); - -// var pool = mysql.createPool({ -// connectionLimit: 10, -// host: process.env.DB_HOST, -// user: process.env.DB_USER, -// password: process.env.DB_PASSWORD, -// port: process.env.DB_PORT, -// database: 'travopti' -// }); - -// let travoptidb = {} -// travoptidb.all = () => { -// return new Promise((resolve, reject) => { -// pool.query(`SELECT * FROM regions`, (err, results) => { -// if (err) { -// return reject(err) -// } -// return resolve(results) -// }) -// }) -// } - -connection.connect((err) => { - if (err) throw err; - console.log('Database connected!') -}); - - -exports.getRegions = () => { - let sql = `SELECT * FROM regions`; - console.log(connection.state) - if (connection.state === 'disconnected') { - setTimeout(() => console.log('waiting...'), 1000); - } - console.log('executed') - let res = {} - connection.query(sql, (error, results, fields) => { - if (error) { - return console.error(error.message); - } - console.log('innercallback(1)') - res = results[0] - }); - console.log('outsidecallback(2)') - return res; -} - -exports.getBYTdata = () => { - connection.query(`SELECT * FROM regions_byt`, (error, results, fields) => { - if (error) { - return console.error(error.message); - } - console.log(results[0]) - nres = results.map((obj) => { - return obj.region - }) - //console.log(nres); - }); -} - -exports.end = () => connection.end(); - -// module.exports = connection; diff --git a/backend/mysql.js b/backend/mysql.js new file mode 100644 index 0000000..c7114d5 --- /dev/null +++ b/backend/mysql.js @@ -0,0 +1,84 @@ +var mysql = require('mysql2/promise'); +require('dotenv').config() + +// var connection = mysql.createConnection({ +// host: process.env.DB_HOST, +// user: process.env.DB_USER, +// password: process.env.DB_PASSWORD, +// port: process.env.DB_PORT, +// database: 'travopti' +// }); + +const pool = mysql.createPool({ + connectionLimit: 10, + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + port: process.env.DB_PORT, + database: 'travopti' +}); + +pool.getConnection() + .then(function (connection) { + console.log(`Connected to database: ${process.env.DB_HOST}`); + //pool.releaseConnection(connection) + }) + .catch(function (error) { + console.error(error.message); + }); + +module.exports = pool; + +// let travoptidb = {} +// travoptidb.all = () => { +// return new Promise((resolve, reject) => { +// pool.query(`SELECT * FROM regions`, (err, results) => { +// if (err) { +// return reject(err) +// } +// return resolve(results) +// }) +// }) +// } + +// connection.connect((err) => { +// if (err) throw err; +// console.log('Database connected!') +// }); + + +// exports.getRegions = () => { +// let sql = `SELECT * FROM regions`; +// console.log(connection.state) +// if (connection.state === 'disconnected') { +// setTimeout(() => console.log('waiting...'), 1000); +// } +// console.log('executed') +// let res = {} +// connection.query(sql, (error, results, fields) => { +// if (error) { +// return console.error(error.message); +// } +// console.log('innercallback(1)') +// res = results[0] +// }); +// console.log('outsidecallback(2)') +// return res; +// } + +// exports.getBYTdata = () => { +// connection.query(`SELECT * FROM regions_byt`, (error, results, fields) => { +// if (error) { +// return console.error(error.message); +// } +// console.log(results[0]) +// nres = results.map((obj) => { +// return obj.region +// }) +// //console.log(nres); +// }); +// } + +// exports.end = () => connection.end(); + +// module.exports = connection; diff --git a/backend/package.json b/backend/package.json index 9ffa857..30dcacc 100644 --- a/backend/package.json +++ b/backend/package.json @@ -3,9 +3,9 @@ "version": "1.0.0", "description": "", "main": "app.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, +"scripts": { + "start": "nodemon ./app.js" +}, "author": "", "license": "ISC", "dependencies": { diff --git a/backend/score.js b/backend/score.js index 4bba82e..feec62a 100644 --- a/backend/score.js +++ b/backend/score.js @@ -8,24 +8,23 @@ const multiplier_temperature = 5; * * @param {...any} scores expects objects which contains score and their weight */ -function calculateAvgScore(...scores) { +exports.calculateAvgScore = (...scores) => { return avgScore = scores.reduce((total, score) => total += score) / scores.length; } -function calculateScoreRange(min, max, multiplier, targetVal, sLowVal, sHighVal) { +exports.calculateScoreRange = (min, max, multiplier, regionVal, sLowVal, sHighVal) => { // return full score when in range - if (targetVal >= sLowVal && targetVal <= sHighVal) return 10; + if (regionVal >= sLowVal && regionVal <= sHighVal) return 10; // choose value with smallest distance - let sVal = Math.abs(targetVal - sLowVal) < Math.abs(targetVal - sHighVal) ? sLowVal : sHighVal; + let sVal = Math.abs(regionVal - sLowVal) < Math.abs(regionVal - sHighVal) ? sLowVal : sHighVal; - return calculateScore(min, max, multiplier, targetVal, sVal); + return this.calculateScore(min, max, multiplier, regionVal, sVal); } -function calculateScore(min, max, multiplier, targetVal, searchVal) { +exports.calculateScore = (min, max, multiplier, regionVal, searchVal) => { - let score = 1 - (Math.abs(searchVal - targetVal) / (max - min) * multiplier); + let score = 1 - (Math.abs(searchVal - regionVal) / (max - min) * multiplier); return score <= 0 ? 0 : score * 10; } -console.log('Calc') -console.log(calculateScoreRange(-15, 45, 5, 24, 15, 22)) \ No newline at end of file +console.log('test score calculation. result: ' + this.calculateScoreRange(-15, 45, 5, 24, 15, 22)) \ No newline at end of file From 06236e881ae3a0a0186f1438eeb524937f2823c6 Mon Sep 17 00:00:00 2001 From: Timo Volkmann Date: Sun, 14 Jun 2020 01:14:04 +0200 Subject: [PATCH 17/20] First working search (climate params, randomizing) --- backend/app.js | 170 +++-- backend/climate.js | 10 +- backend/mysql.js | 10 +- backend/package-lock.json | 5 + backend/package.json | 7 +- backend/score.js | 3 +- backend/transformer-test.json | 1205 +++++++++++++++++++++++++++++++++ backend/transformer.js | 77 +++ 8 files changed, 1426 insertions(+), 61 deletions(-) create mode 100644 backend/transformer-test.json create mode 100644 backend/transformer.js diff --git a/backend/app.js b/backend/app.js index c15d23d..6b83f4e 100644 --- a/backend/app.js +++ b/backend/app.js @@ -1,11 +1,19 @@ var express = require('express') +var _ = require('lodash') var sampledata = require('./sampledata') var db = require('./mysql') var score = require('./score') +var transformer = require('./transformer') const app = express() const port = 3000 -const multiplier_temp = 5 +//const multiplier_temp = 5 +const multiplier = { + temperature_mean: 5, + percipitation: 0.5, + raindays: 3, + sunhours: 2.5, +} const sampleRegions = [ { @@ -23,16 +31,10 @@ const samplePresets = [ } ] -// db.connect((err) => { -// if (err) throw err; -// console.log('Database connected!') -// }); - app.get('/', (req, res) => res.send('Hello Timo!')) app.get('/v1/regions', (req, res) => res.json(sampleRegions)) app.get('/v1/presets', (req, res) => res.json(samplePresets)) app.get('/v1/search', (req, res) => { - // check query params let response = {} response.meta = { @@ -46,7 +48,13 @@ app.get('/v1/search', (req, res) => { console.log(req.query.to) console.log(req.query.temperature) - search(req.query.from, req.query.to, { temperature: req.query.temperature }).then(searchResults => { + let validQueries = {} + if (req.query.temperature) validQueries['temperature_mean'] = req.query.temperature + if (req.query.percipitation) validQueries['percipitation'] = req.query.percipitation + if (req.query.raindays) validQueries['raindays'] = req.query.raindays + if (req.query.sunhours) validQueries['sunhours'] = req.query.sunhours + + search(req.query.from, req.query.to, validQueries).then(searchResults => { response.data = searchResults res.json(response) }) @@ -54,7 +62,23 @@ app.get('/v1/search', (req, res) => { app.listen(port, () => console.log(`Travopti backend listening at http://localhost:${port}`)) + + + async function search(from, to, queries) { + // get Min and Max values for each Parameter + const minMax = await getClimateMinMax() + // randomize if empty queries + if (_.isEmpty(queries)) { + let t = _.random(minMax.min.temperature_mean, minMax.max.temperature_mean-5) + let p = _.random(minMax.min.percipitation, minMax.max.percipitation-50) + let r = _.random(minMax.min.raindays, minMax.max.raindays-5) + let s = _.random(minMax.min.sunhours, minMax.max.sunhours-50) + queries.temperature_mean = `${t},${t + 5}` + queries.percipitation = `${p},${p + 50}` + queries.raindays = `${r},${r + 5}` + queries.sunhours = `${s},${s + 50}` + } console.log('search') // validate regex: YYYY-MM-DD let re = /([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/i; @@ -64,7 +88,7 @@ async function search(from, to, queries) { // calculate average if traveldates are in more than one month let monthFrom = Number(from.split("-")[1]) let monthTo = Number(to.split("-")[1]) - let values = [] + let travelPeriods = [] let virtDays = 0 if (monthFrom === monthTo) { let element = { @@ -72,14 +96,14 @@ async function search(from, to, queries) { days: Number(to.split("-")[2]) - Number(from.split("-")[2]) } virtDays = element.days - values.push(element) + travelPeriods.push(element) } else { for (let index = monthFrom; index <= monthTo; index++) { let element = {} if (index === monthFrom) { element = { month: index, - days: 31 - Number(from.split("-")[2]) + days: 32 - Number(from.split("-")[2]) } } else if (index === monthTo) { element = { @@ -93,38 +117,77 @@ async function search(from, to, queries) { } } virtDays += element.days - values.push(element) + travelPeriods.push(element) } } - // retrieve data from database - const minMax = await getClimateMinMax() - const data = await Promise.all(values.map(async (val) => { - return await getAllClimatePerMonth(val.month) - })) - console.log("data from getAllClimatePerMonth") - //console.log(data) + // calculate score - // TODO: calculate score for all months - let scores_temp - if (queries.temperature) { - scores_temp = calculateTempScores(data[0], queries.temperature.split(',')[0], queries.temperature.split(',')[1], minMax) - } + let detailScores = await Promise.all(travelPeriods.map(async period => { + period.climate = await getAllRegionsWithClimatePerMonth(period.month) + period.scores = {} + Object.entries(queries).forEach(([key, value]) => { + // console.log('key',key) + // console.log('val', value) + period.scores[key] = calculateScores(key, period.climate, value.split(',')[0], value.split(',')[1], minMax) + }); + return period + })); + + // TODO: calculate ratio - return { TODO___temperature_scores: scores_temp } + return transformer.transform(detailScores) + // return { TODO___scores: detailScores } + // return { TODO___scores: calculateAverageScore(detailScores, Object.keys(queries)) } } -function calculateTempScores(regionDataRows, searchLowParam, searchMaxParam, minMax) { - console.log('calculateTempScores') - console.log(searchLowParam) - console.log(searchMaxParam) - console.log(minMax) +function calculateAverageScore(scorePeriods, types) { + const days = scorePeriods.reduce((total, period) => total += period.days) + let totalScores = {} + + let finalRegionObj = { + + } + + scorePeriods.forEach(element => { + + types.forEach(type => { + element.scores[type].forEach(regionScore => { + + }) + }) + }) +} + +function transformToRegionObjects(scorePeriods) { + let response = [] + // create region objects + scorePeriods[0].climate.forEach(element => { + let obj = { + region_id: element.region_id, + country_id: element.country_id, + name: element.name, + } + response.push(obj) + }) + // + +} + +function calculateScores(type, regionDataRows, searchLowParam, searchMaxParam, minMax) { + console.log('calculateScores for', type) + // console.log(searchLowParam) + // console.log(searchMaxParam) + // console.log(minMax) //console.log(regionDataRows) let result = regionDataRows.map(x => { - console.log(x.temperature_mean) + // console.log(x.temperature_mean) + const sc = Math.round(score.calculateScoreRange(minMax.min[type], minMax.max[type], multiplier[type], x[type], searchLowParam, searchMaxParam) * 100) / 100 + return { region_id: x.region_id, - value: x.temperature_mean, - score: Math.round(score.calculateScoreRange(minMax.min.temperature_mean, minMax.max.temperature_mean, multiplier_temp, x.temperature_mean, searchLowParam, searchMaxParam)*100)/100 + type: type, + value: x[type], + score: x[type] === null ? null : sc } }) @@ -138,47 +201,52 @@ async function getClimateMinMax() { MIN(temperature_mean_min) AS temperature_mean_min, MIN(temperature_mean_max) AS temperature_mean_max, MIN(percipitation) AS percipitation, - MIN(sunshine) AS sunshine + MIN(raindays) AS raindays, + MIN(sunshine) AS sunhours FROM region_climate` const sqlMax = `SELECT MAX(temperature_mean) AS temperature_mean, MAX(temperature_mean_min) AS temperature_mean_min, MAX(temperature_mean_max) AS temperature_mean_max, MAX(percipitation) AS percipitation, - MAX(sunshine) AS sunshine + MAX(raindays) AS raindays, + MAX(sunshine) AS sunhours FROM region_climate` const [qResMin, qResMax] = await Promise.all([getQueryRows(sqlMin), getQueryRows(sqlMax)]) - console.log(qResMin) + //console.log(qResMin) return { min: qResMin[0], max: qResMax[0] } } async function getQueryRows(sql) { - console.log('getQueryRows') + //console.log('getQueryRows') const [rows, fields] = await db.execute(sql) return rows } -async function getRegionIdsWithMeteostatData() { +function getRegionIdsWithMeteostatData() { console.log('getRegionIdsWithMeteostatData') //return await getQueryRows(`SELECT * FROM regions WHERE meteostat_id IS NOT NULL`) - return await getQueryRows(`SELECT region_id FROM region_climate GROUP BY region_id`) + return getQueryRows(`SELECT region_id FROM region_climate GROUP BY region_id`) } -async function getClimatePerRegionAndMonth(regionId, month) { +function getClimatePerRegionAndMonth(regionId, month) { console.log('getClimatePerRegionAndMonth') const sql = `SELECT region_id, AVG(temperature_mean), AVG(temperature_mean_min), AVG(temperature_mean_max), AVG(percipitation), AVG(sunshine) FROM region_climate WHERE month = ${month} AND region_id = ${regionId}` - return await getQueryRows(sql) + return getQueryRows(sql) } -async function getAllClimatePerMonth(month) { - console.log('getAllClimatePerMonth') +function getAllRegionsWithClimatePerMonth(month) { + console.log('getAllRegionsWithClimatePerMonth') const sql = `SELECT - region_id, - ROUND(AVG(temperature_mean), 1) AS temperature_mean, - ROUND(AVG(temperature_mean_min), 1) AS temperature_mean_min, - ROUND(AVG(temperature_mean_max), 1) AS temperature_mean_max, - ROUND(AVG(percipitation), 1) AS percipitation, - ROUND(AVG(sunshine), 1) AS sunshine - FROM region_climate WHERE month = ${month} GROUP BY region_id` - return await getQueryRows(sql) + region_climate.region_id AS region_id, + regions.country_id AS country_id, + regions.region AS name, + ROUND(AVG(region_climate.temperature_mean), 1) AS temperature_mean, + ROUND(AVG(region_climate.temperature_mean_min), 1) AS temperature_mean_min, + ROUND(AVG(region_climate.temperature_mean_max), 1) AS temperature_mean_max, + ROUND(AVG(region_climate.percipitation), 1) AS percipitation, + ROUND(AVG(region_climate.raindays), 1) AS raindays, + ROUND(AVG(region_climate.sunshine), 1) AS sunhours + FROM region_climate JOIN regions ON region_climate.region_id = regions.id WHERE region_climate.month = ${month} GROUP BY region_id` + return getQueryRows(sql) } \ No newline at end of file diff --git a/backend/climate.js b/backend/climate.js index 2b2c5ea..96ff87c 100644 --- a/backend/climate.js +++ b/backend/climate.js @@ -2,7 +2,7 @@ require('dotenv').config() const mysql = require('mysql2/promise'); const axios = require('axios') -const rangeStartDate = '2018-01' +const rangeStartDate = '2010-01' const rangeEndDate = '2018-12' async function main() { @@ -15,12 +15,12 @@ async function main() { }); const [result, fields] = await connection.execute(`SELECT * FROM regions WHERE meteostat_id IS NOT NULL`) - let temp = await Promise.all(result.map(x => createClimateObject(x))) - let final = temp.reduce((total, element) => total.concat(element), []) + // let temp = await Promise.all(result.map(x => createClimateObject(x))) + // let final = temp.reduce((total, element) => total.concat(element), []) - await writeToDatabase(connection, final) + // await writeToDatabase(connection, final) - let temp2 = await Promise.all(result.map(x => createClimateObjectFrom(x))) + let temp2 = await Promise.all(result.map(x => createClimateObjectFrom(x, startDate, endDate))) let final2 = temp2.reduce((total, element) => total.concat(element), []) await writeToDatabase(connection, final2) diff --git a/backend/mysql.js b/backend/mysql.js index c7114d5..7d954a0 100644 --- a/backend/mysql.js +++ b/backend/mysql.js @@ -15,7 +15,15 @@ const pool = mysql.createPool({ user: process.env.DB_USER, password: process.env.DB_PASSWORD, port: process.env.DB_PORT, - database: 'travopti' + database: 'travopti', + // typeCast: function (field, next) { + // if (field.type == "INT") { + // var value = field.string(); + // return (value === null) ? null : Number(value); + // } + // return next(); + // } + decimalNumbers: true }); pool.getConnection() diff --git a/backend/package-lock.json b/backend/package-lock.json index 9a19ff0..249085f 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -816,6 +816,11 @@ "package-json": "^6.3.0" } }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, "long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", diff --git a/backend/package.json b/backend/package.json index 30dcacc..c07ce33 100644 --- a/backend/package.json +++ b/backend/package.json @@ -3,15 +3,16 @@ "version": "1.0.0", "description": "", "main": "app.js", -"scripts": { - "start": "nodemon ./app.js" -}, + "scripts": { + "start": "nodemon ./app.js" + }, "author": "", "license": "ISC", "dependencies": { "axios": "^0.19.2", "dotenv": "^8.2.0", "express": "^4.17.1", + "lodash": "^4.17.15", "mysql": "^2.18.1", "mysql2": "^2.1.0" }, diff --git a/backend/score.js b/backend/score.js index feec62a..4096aed 100644 --- a/backend/score.js +++ b/backend/score.js @@ -13,11 +13,12 @@ exports.calculateAvgScore = (...scores) => { } exports.calculateScoreRange = (min, max, multiplier, regionVal, sLowVal, sHighVal) => { + //console.log('scores.calculateScoreRange:', min, max, multiplier, regionVal, sLowVal, sHighVal) // return full score when in range if (regionVal >= sLowVal && regionVal <= sHighVal) return 10; // choose value with smallest distance let sVal = Math.abs(regionVal - sLowVal) < Math.abs(regionVal - sHighVal) ? sLowVal : sHighVal; - + //console.log('nearest value',sVal, regionVal) return this.calculateScore(min, max, multiplier, regionVal, sVal); } diff --git a/backend/transformer-test.json b/backend/transformer-test.json new file mode 100644 index 0000000..cb444bd --- /dev/null +++ b/backend/transformer-test.json @@ -0,0 +1,1205 @@ +[ + { + "month": 5, + "days": 31, + "climate": [ + { + "region_id": 2, + "country_id": 2, + "name": "Melbourne", + "temperature_mean": 12.6, + "temperature_mean_min": 8.3, + "temperature_mean_max": 17, + "percipitation": 35.4, + "raindays": 7.8, + "sunhours": 139 + }, + { + "region_id": 3, + "country_id": 2, + "name": "Sydney", + "temperature_mean": 16.6, + "temperature_mean_min": 12, + "temperature_mean_max": 21.2, + "percipitation": 59.2, + "raindays": 5.6, + "sunhours": 218.4 + }, + { + "region_id": 6, + "country_id": 4, + "name": "Sao Paolo", + "temperature_mean": 19, + "temperature_mean_min": 16.1, + "temperature_mean_max": 22.8, + "percipitation": 42.3, + "raindays": 5, + "sunhours": null + }, + { + "region_id": 7, + "country_id": 5, + "name": "Toronto", + "temperature_mean": 16.1, + "temperature_mean_min": 10.8, + "temperature_mean_max": 21.5, + "percipitation": 52, + "raindays": 11, + "sunhours": 268 + }, + { + "region_id": 9, + "country_id": 7, + "name": "Peking", + "temperature_mean": 22.1, + "temperature_mean_min": 15.8, + "temperature_mean_max": 28, + "percipitation": 26.7, + "raindays": 3.3, + "sunhours": 261.5 + }, + { + "region_id": 10, + "country_id": 7, + "name": "Shanghai", + "temperature_mean": 21.3, + "temperature_mean_min": 17.7, + "temperature_mean_max": 25.5, + "percipitation": 105.9, + "raindays": 8.6, + "sunhours": 165.9 + }, + { + "region_id": 11, + "country_id": 8, + "name": "Bogota", + "temperature_mean": 14.3, + "temperature_mean_min": 10.5, + "temperature_mean_max": 19.5, + "percipitation": 108, + "raindays": 13.3, + "sunhours": 103.6 + }, + { + "region_id": 12, + "country_id": 9, + "name": "Kairo", + "temperature_mean": 26.4, + "temperature_mean_min": 20.7, + "temperature_mean_max": 32.5, + "percipitation": 0.1, + "raindays": 0, + "sunhours": null + }, + { + "region_id": 13, + "country_id": 10, + "name": "London", + "temperature_mean": 13.9, + "temperature_mean_min": 9.2, + "temperature_mean_max": 18.4, + "percipitation": 47, + "raindays": 8, + "sunhours": 188 + }, + { + "region_id": 18, + "country_id": 15, + "name": "Reykjavik", + "temperature_mean": 6.8, + "temperature_mean_min": 4.1, + "temperature_mean_max": 10.3, + "percipitation": 53.4, + "raindays": 10.4, + "sunhours": 208.9 + }, + { + "region_id": 24, + "country_id": 20, + "name": "Kuala Lumpur", + "temperature_mean": 28.7, + "temperature_mean_min": 25.3, + "temperature_mean_max": 33.8, + "percipitation": 301.9, + "raindays": 15.4, + "sunhours": null + } + ], + "scores": { + "temperature_mean": [ + { + "region_id": 2, + "type": "temperature_mean", + "value": 12.6, + "score": 0 + }, + { + "region_id": 3, + "type": "temperature_mean", + "value": 16.6, + "score": 2.97 + }, + { + "region_id": 6, + "type": "temperature_mean", + "value": 19, + "score": 6.09 + }, + { + "region_id": 7, + "type": "temperature_mean", + "value": 16.1, + "score": 2.32 + }, + { + "region_id": 9, + "type": "temperature_mean", + "value": 22.1, + "score": 10 + }, + { + "region_id": 10, + "type": "temperature_mean", + "value": 21.3, + "score": 9.09 + }, + { + "region_id": 11, + "type": "temperature_mean", + "value": 14.3, + "score": 0 + }, + { + "region_id": 12, + "type": "temperature_mean", + "value": 26.4, + "score": 5.57 + }, + { + "region_id": 13, + "type": "temperature_mean", + "value": 13.9, + "score": 0 + }, + { + "region_id": 18, + "type": "temperature_mean", + "value": 6.8, + "score": 0 + }, + { + "region_id": 24, + "type": "temperature_mean", + "value": 28.7, + "score": 2.58 + } + ], + "percipitation": [ + { + "region_id": 2, + "type": "percipitation", + "value": 35.4, + "score": 10 + }, + { + "region_id": 3, + "type": "percipitation", + "value": 59.2, + "score": 10 + }, + { + "region_id": 6, + "type": "percipitation", + "value": 42.3, + "score": 10 + }, + { + "region_id": 7, + "type": "percipitation", + "value": 52, + "score": 10 + }, + { + "region_id": 9, + "type": "percipitation", + "value": 26.7, + "score": 10 + }, + { + "region_id": 10, + "type": "percipitation", + "value": 105.9, + "score": 9.95 + }, + { + "region_id": 11, + "type": "percipitation", + "value": 108, + "score": 9.93 + }, + { + "region_id": 12, + "type": "percipitation", + "value": 0.1, + "score": 10 + }, + { + "region_id": 13, + "type": "percipitation", + "value": 47, + "score": 10 + }, + { + "region_id": 18, + "type": "percipitation", + "value": 53.4, + "score": 10 + }, + { + "region_id": 24, + "type": "percipitation", + "value": 301.9, + "score": 8.14 + } + ], + "raindays": [ + { + "region_id": 2, + "type": "raindays", + "value": 7.8, + "score": 5.78 + }, + { + "region_id": 3, + "type": "raindays", + "value": 5.6, + "score": 8.22 + }, + { + "region_id": 6, + "type": "raindays", + "value": 5, + "score": 8.89 + }, + { + "region_id": 7, + "type": "raindays", + "value": 11, + "score": 2.22 + }, + { + "region_id": 9, + "type": "raindays", + "value": 3.3, + "score": 10 + }, + { + "region_id": 10, + "type": "raindays", + "value": 8.6, + "score": 4.89 + }, + { + "region_id": 11, + "type": "raindays", + "value": 13.3, + "score": 0 + }, + { + "region_id": 12, + "type": "raindays", + "value": 0, + "score": 8.89 + }, + { + "region_id": 13, + "type": "raindays", + "value": 8, + "score": 5.56 + }, + { + "region_id": 18, + "type": "raindays", + "value": 10.4, + "score": 2.89 + }, + { + "region_id": 24, + "type": "raindays", + "value": 15.4, + "score": 0 + } + ], + "sunhours": [ + { + "region_id": 2, + "type": "sunhours", + "value": 139, + "score": 1.27 + }, + { + "region_id": 3, + "type": "sunhours", + "value": 218.4, + "score": 7.52 + }, + { + "region_id": 6, + "type": "sunhours", + "value": null, + "score": null + }, + { + "region_id": 7, + "type": "sunhours", + "value": 268, + "score": 10 + }, + { + "region_id": 9, + "type": "sunhours", + "value": 261.5, + "score": 10 + }, + { + "region_id": 10, + "type": "sunhours", + "value": 165.9, + "score": 3.39 + }, + { + "region_id": 11, + "type": "sunhours", + "value": 103.6, + "score": 0 + }, + { + "region_id": 12, + "type": "sunhours", + "value": null, + "score": null + }, + { + "region_id": 13, + "type": "sunhours", + "value": 188, + "score": 5.13 + }, + { + "region_id": 18, + "type": "sunhours", + "value": 208.9, + "score": 6.77 + }, + { + "region_id": 24, + "type": "sunhours", + "value": null, + "score": null + } + ] + } + }, + { + "month": 6, + "days": 30, + "climate": [ + { + "region_id": 2, + "country_id": 2, + "name": "Melbourne", + "temperature_mean": 10, + "temperature_mean_min": 6.1, + "temperature_mean_max": 14, + "percipitation": 51.4, + "raindays": 8, + "sunhours": 122.9 + }, + { + "region_id": 3, + "country_id": 2, + "name": "Sydney", + "temperature_mean": 14.2, + "temperature_mean_min": 10.2, + "temperature_mean_max": 18.3, + "percipitation": 154.7, + "raindays": 9.4, + "sunhours": 160 + }, + { + "region_id": 6, + "country_id": 4, + "name": "Sao Paolo", + "temperature_mean": 17.5, + "temperature_mean_min": 14.6, + "temperature_mean_max": 21.8, + "percipitation": 48.8, + "raindays": 4.5, + "sunhours": null + }, + { + "region_id": 7, + "country_id": 5, + "name": "Toronto", + "temperature_mean": 19.3, + "temperature_mean_min": 15.1, + "temperature_mean_max": 23.8, + "percipitation": 192, + "raindays": 14, + "sunhours": 253 + }, + { + "region_id": 9, + "country_id": 7, + "name": "Peking", + "temperature_mean": 25.4, + "temperature_mean_min": 20.1, + "temperature_mean_max": 30.9, + "percipitation": 85, + "raindays": 7.9, + "sunhours": 209.2 + }, + { + "region_id": 10, + "country_id": 7, + "name": "Shanghai", + "temperature_mean": 24.2, + "temperature_mean_min": 21.6, + "temperature_mean_max": 27.5, + "percipitation": 199.4, + "raindays": 10.6, + "sunhours": 94.1 + }, + { + "region_id": 11, + "country_id": 8, + "name": "Bogota", + "temperature_mean": 14, + "temperature_mean_min": 9.4, + "temperature_mean_max": 18.6, + "percipitation": 57.8, + "raindays": 9.8, + "sunhours": 121.6 + }, + { + "region_id": 12, + "country_id": 9, + "name": "Kairo", + "temperature_mean": 28.6, + "temperature_mean_min": 23.1, + "temperature_mean_max": 34.6, + "percipitation": 0, + "raindays": 0, + "sunhours": null + }, + { + "region_id": 13, + "country_id": 10, + "name": "London", + "temperature_mean": 17, + "temperature_mean_min": 12.2, + "temperature_mean_max": 21.8, + "percipitation": 45.7, + "raindays": 7.6, + "sunhours": 180.8 + }, + { + "region_id": 18, + "country_id": 15, + "name": "Reykjavik", + "temperature_mean": 10.1, + "temperature_mean_min": 7.6, + "temperature_mean_max": 13.5, + "percipitation": 49.3, + "raindays": 9.8, + "sunhours": 171.3 + }, + { + "region_id": 24, + "country_id": 20, + "name": "Kuala Lumpur", + "temperature_mean": 29, + "temperature_mean_min": 25.5, + "temperature_mean_max": 33.8, + "percipitation": 137.9, + "raindays": 7.4, + "sunhours": null + } + ], + "scores": { + "temperature_mean": [ + { + "region_id": 2, + "type": "temperature_mean", + "value": 10, + "score": 0 + }, + { + "region_id": 3, + "type": "temperature_mean", + "value": 14.2, + "score": 0 + }, + { + "region_id": 6, + "type": "temperature_mean", + "value": 17.5, + "score": 4.14 + }, + { + "region_id": 7, + "type": "temperature_mean", + "value": 19.3, + "score": 6.48 + }, + { + "region_id": 9, + "type": "temperature_mean", + "value": 25.4, + "score": 6.88 + }, + { + "region_id": 10, + "type": "temperature_mean", + "value": 24.2, + "score": 8.44 + }, + { + "region_id": 11, + "type": "temperature_mean", + "value": 14, + "score": 0 + }, + { + "region_id": 12, + "type": "temperature_mean", + "value": 28.6, + "score": 2.71 + }, + { + "region_id": 13, + "type": "temperature_mean", + "value": 17, + "score": 3.49 + }, + { + "region_id": 18, + "type": "temperature_mean", + "value": 10.1, + "score": 0 + }, + { + "region_id": 24, + "type": "temperature_mean", + "value": 29, + "score": 2.19 + } + ], + "percipitation": [ + { + "region_id": 2, + "type": "percipitation", + "value": 51.4, + "score": 10 + }, + { + "region_id": 3, + "type": "percipitation", + "value": 154.7, + "score": 9.5 + }, + { + "region_id": 6, + "type": "percipitation", + "value": 48.8, + "score": 10 + }, + { + "region_id": 7, + "type": "percipitation", + "value": 192, + "score": 9.15 + }, + { + "region_id": 9, + "type": "percipitation", + "value": 85, + "score": 10 + }, + { + "region_id": 10, + "type": "percipitation", + "value": 199.4, + "score": 9.09 + }, + { + "region_id": 11, + "type": "percipitation", + "value": 57.8, + "score": 10 + }, + { + "region_id": 12, + "type": "percipitation", + "value": 0, + "score": 10 + }, + { + "region_id": 13, + "type": "percipitation", + "value": 45.7, + "score": 10 + }, + { + "region_id": 18, + "type": "percipitation", + "value": 49.3, + "score": 10 + }, + { + "region_id": 24, + "type": "percipitation", + "value": 137.9, + "score": 9.65 + } + ], + "raindays": [ + { + "region_id": 2, + "type": "raindays", + "value": 8, + "score": 5.56 + }, + { + "region_id": 3, + "type": "raindays", + "value": 9.4, + "score": 4 + }, + { + "region_id": 6, + "type": "raindays", + "value": 4.5, + "score": 9.44 + }, + { + "region_id": 7, + "type": "raindays", + "value": 14, + "score": 0 + }, + { + "region_id": 9, + "type": "raindays", + "value": 7.9, + "score": 5.67 + }, + { + "region_id": 10, + "type": "raindays", + "value": 10.6, + "score": 2.67 + }, + { + "region_id": 11, + "type": "raindays", + "value": 9.8, + "score": 3.56 + }, + { + "region_id": 12, + "type": "raindays", + "value": 0, + "score": 8.89 + }, + { + "region_id": 13, + "type": "raindays", + "value": 7.6, + "score": 6 + }, + { + "region_id": 18, + "type": "raindays", + "value": 9.8, + "score": 3.56 + }, + { + "region_id": 24, + "type": "raindays", + "value": 7.4, + "score": 6.22 + } + ], + "sunhours": [ + { + "region_id": 2, + "type": "sunhours", + "value": 122.9, + "score": 0.01 + }, + { + "region_id": 3, + "type": "sunhours", + "value": 160, + "score": 2.92 + }, + { + "region_id": 6, + "type": "sunhours", + "value": null, + "score": null + }, + { + "region_id": 7, + "type": "sunhours", + "value": 253, + "score": 10 + }, + { + "region_id": 9, + "type": "sunhours", + "value": 209.2, + "score": 6.79 + }, + { + "region_id": 10, + "type": "sunhours", + "value": 94.1, + "score": 0 + }, + { + "region_id": 11, + "type": "sunhours", + "value": 121.6, + "score": 0 + }, + { + "region_id": 12, + "type": "sunhours", + "value": null, + "score": null + }, + { + "region_id": 13, + "type": "sunhours", + "value": 180.8, + "score": 4.56 + }, + { + "region_id": 18, + "type": "sunhours", + "value": 171.3, + "score": 3.81 + }, + { + "region_id": 24, + "type": "sunhours", + "value": null, + "score": null + } + ] + } + }, + { + "month": 7, + "days": 14, + "climate": [ + { + "region_id": 2, + "country_id": 2, + "name": "Melbourne", + "temperature_mean": 9.8, + "temperature_mean_min": 5.9, + "temperature_mean_max": 13.7, + "percipitation": 33.4, + "raindays": 8.7, + "sunhours": 139.3 + }, + { + "region_id": 3, + "country_id": 2, + "name": "Sydney", + "temperature_mean": 13.6, + "temperature_mean_min": 8.8, + "temperature_mean_max": 18.4, + "percipitation": 68.2, + "raindays": 6.7, + "sunhours": 221 + }, + { + "region_id": 6, + "country_id": 4, + "name": "Sao Paolo", + "temperature_mean": 18.1, + "temperature_mean_min": 13.6, + "temperature_mean_max": 22.5, + "percipitation": 19.4, + "raindays": 2.8, + "sunhours": null + }, + { + "region_id": 7, + "country_id": 5, + "name": "Toronto", + "temperature_mean": 23.6, + "temperature_mean_min": 18.5, + "temperature_mean_max": 28.5, + "percipitation": 90, + "raindays": 7, + "sunhours": 308 + }, + { + "region_id": 9, + "country_id": 7, + "name": "Peking", + "temperature_mean": 27.7, + "temperature_mean_min": 23.6, + "temperature_mean_max": 32.2, + "percipitation": 199.9, + "raindays": 8.6, + "sunhours": 174.9 + }, + { + "region_id": 10, + "country_id": 7, + "name": "Shanghai", + "temperature_mean": 29.5, + "temperature_mean_min": 26.5, + "temperature_mean_max": 33.3, + "percipitation": 125.9, + "raindays": 8.2, + "sunhours": 183.6 + }, + { + "region_id": 11, + "country_id": 8, + "name": "Bogota", + "temperature_mean": 13.7, + "temperature_mean_min": 9.4, + "temperature_mean_max": 18.7, + "percipitation": 55.3, + "raindays": 9.7, + "sunhours": 129.2 + }, + { + "region_id": 12, + "country_id": 9, + "name": "Kairo", + "temperature_mean": 29.4, + "temperature_mean_min": 24.3, + "temperature_mean_max": 34.9, + "percipitation": 0, + "raindays": 0, + "sunhours": null + }, + { + "region_id": 13, + "country_id": 10, + "name": "London", + "temperature_mean": 19.5, + "temperature_mean_min": 14.5, + "temperature_mean_max": 24.4, + "percipitation": 45.3, + "raindays": 7.7, + "sunhours": 204 + }, + { + "region_id": 18, + "country_id": 15, + "name": "Reykjavik", + "temperature_mean": 11.8, + "temperature_mean_min": 9.3, + "temperature_mean_max": 15.3, + "percipitation": 51, + "raindays": 10.9, + "sunhours": 178.1 + }, + { + "region_id": 24, + "country_id": 20, + "name": "Kuala Lumpur", + "temperature_mean": 28.6, + "temperature_mean_min": 25, + "temperature_mean_max": 33.2, + "percipitation": 150.2, + "raindays": 10, + "sunhours": null + } + ], + "scores": { + "temperature_mean": [ + { + "region_id": 2, + "type": "temperature_mean", + "value": 9.8, + "score": 0 + }, + { + "region_id": 3, + "type": "temperature_mean", + "value": 13.6, + "score": 0 + }, + { + "region_id": 6, + "type": "temperature_mean", + "value": 18.1, + "score": 4.92 + }, + { + "region_id": 7, + "type": "temperature_mean", + "value": 23.6, + "score": 9.22 + }, + { + "region_id": 9, + "type": "temperature_mean", + "value": 27.7, + "score": 3.88 + }, + { + "region_id": 10, + "type": "temperature_mean", + "value": 29.5, + "score": 1.54 + }, + { + "region_id": 11, + "type": "temperature_mean", + "value": 13.7, + "score": 0 + }, + { + "region_id": 12, + "type": "temperature_mean", + "value": 29.4, + "score": 1.67 + }, + { + "region_id": 13, + "type": "temperature_mean", + "value": 19.5, + "score": 6.74 + }, + { + "region_id": 18, + "type": "temperature_mean", + "value": 11.8, + "score": 0 + }, + { + "region_id": 24, + "type": "temperature_mean", + "value": 28.6, + "score": 2.71 + } + ], + "percipitation": [ + { + "region_id": 2, + "type": "percipitation", + "value": 33.4, + "score": 10 + }, + { + "region_id": 3, + "type": "percipitation", + "value": 68.2, + "score": 10 + }, + { + "region_id": 6, + "type": "percipitation", + "value": 19.4, + "score": 10 + }, + { + "region_id": 7, + "type": "percipitation", + "value": 90, + "score": 10 + }, + { + "region_id": 9, + "type": "percipitation", + "value": 199.9, + "score": 9.08 + }, + { + "region_id": 10, + "type": "percipitation", + "value": 125.9, + "score": 9.76 + }, + { + "region_id": 11, + "type": "percipitation", + "value": 55.3, + "score": 10 + }, + { + "region_id": 12, + "type": "percipitation", + "value": 0, + "score": 10 + }, + { + "region_id": 13, + "type": "percipitation", + "value": 45.3, + "score": 10 + }, + { + "region_id": 18, + "type": "percipitation", + "value": 51, + "score": 10 + }, + { + "region_id": 24, + "type": "percipitation", + "value": 150.2, + "score": 9.54 + } + ], + "raindays": [ + { + "region_id": 2, + "type": "raindays", + "value": 8.7, + "score": 4.78 + }, + { + "region_id": 3, + "type": "raindays", + "value": 6.7, + "score": 7 + }, + { + "region_id": 6, + "type": "raindays", + "value": 2.8, + "score": 10 + }, + { + "region_id": 7, + "type": "raindays", + "value": 7, + "score": 6.67 + }, + { + "region_id": 9, + "type": "raindays", + "value": 8.6, + "score": 4.89 + }, + { + "region_id": 10, + "type": "raindays", + "value": 8.2, + "score": 5.33 + }, + { + "region_id": 11, + "type": "raindays", + "value": 9.7, + "score": 3.67 + }, + { + "region_id": 12, + "type": "raindays", + "value": 0, + "score": 8.89 + }, + { + "region_id": 13, + "type": "raindays", + "value": 7.7, + "score": 5.89 + }, + { + "region_id": 18, + "type": "raindays", + "value": 10.9, + "score": 2.33 + }, + { + "region_id": 24, + "type": "raindays", + "value": 10, + "score": 3.33 + } + ], + "sunhours": [ + { + "region_id": 2, + "type": "sunhours", + "value": 139.3, + "score": 1.3 + }, + { + "region_id": 3, + "type": "sunhours", + "value": 221, + "score": 7.72 + }, + { + "region_id": 6, + "type": "sunhours", + "value": null, + "score": null + }, + { + "region_id": 7, + "type": "sunhours", + "value": 308, + "score": 9.37 + }, + { + "region_id": 9, + "type": "sunhours", + "value": 174.9, + "score": 4.1 + }, + { + "region_id": 10, + "type": "sunhours", + "value": 183.6, + "score": 4.78 + }, + { + "region_id": 11, + "type": "sunhours", + "value": 129.2, + "score": 0.5 + }, + { + "region_id": 12, + "type": "sunhours", + "value": null, + "score": null + }, + { + "region_id": 13, + "type": "sunhours", + "value": 204, + "score": 6.38 + }, + { + "region_id": 18, + "type": "sunhours", + "value": 178.1, + "score": 4.35 + }, + { + "region_id": 24, + "type": "sunhours", + "value": null, + "score": null + } + ] + } + } +] \ No newline at end of file diff --git a/backend/transformer.js b/backend/transformer.js new file mode 100644 index 0000000..9ac37f2 --- /dev/null +++ b/backend/transformer.js @@ -0,0 +1,77 @@ +const _ = require('lodash') +const fs = require('fs') + + +exports.transform = (data) => { + // get data + // let data = JSON.parse(fs.readFileSync('transformer-test.json')); + const types = Object.keys(data[0].scores) + + // STEP 1 Create Response Array with region names from first climate object + let byRegion = data[0].climate.map(el => { + return { + region_id: el.region_id, + country_id: el.country_id, + name: el.name, + } + }) + + // STEP 2 Prepare flat scoreobject array and set days property + scoreObjs = _.flatten(_.map(data, (period) => { + return _.reduce(period.scores, (arr, el) => { + return arr.concat(el) + }).map(element => { + element.days = period.days + return element + }) + })) + + // STEP 3 Collect scoreobjects for each region + let results = byRegion.map(region => { + let scores = [] + types.forEach(typ => { + let tempScores = _.filter(scoreObjs, { 'region_id': region.region_id, 'type': typ }) + if (_.some(tempScores, { 'score': null })) { + console.log("found 'null' scores! skipping...") + //console.log(tempScores) + return + } + let averagedScore = { + region_id: region.region_id, + type: typ, + value: 0, + score: 0, + days: 0 + } + tempScores.forEach(el => { + averagedScore.value += (el.value * el.days) + averagedScore.score += (el.score * el.days) + averagedScore.days += (el.days) + }) + + averagedScore.value = _.round(averagedScore.value / averagedScore.days, 1) + averagedScore.score = _.round(averagedScore.score / averagedScore.days, 1) + delete averagedScore.region_id + delete averagedScore.days + scores.push(averagedScore) + + }) + region.scores = scores + + // STEP 4 Calculate Average Score + region.score = calculateAverage(region.scores) + //console.log(region) + return region + }) + + // console.log(results) + return _.orderBy(results, 'score', 'desc') + //end +} + +function calculateAverage(scores) { + let sum = 0 + scores.forEach(el => sum += el.score) + //console.log(sum) + return _.round(sum / scores.length, 2) +} \ No newline at end of file From b249665a9ed435453de489e6f5b113ea2e8f190c Mon Sep 17 00:00:00 2001 From: Timo Volkmann Date: Sun, 14 Jun 2020 01:32:21 +0200 Subject: [PATCH 18/20] balancing --- backend/app.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/backend/app.js b/backend/app.js index 6b83f4e..1321d14 100644 --- a/backend/app.js +++ b/backend/app.js @@ -10,7 +10,7 @@ const port = 3000 //const multiplier_temp = 5 const multiplier = { temperature_mean: 5, - percipitation: 0.5, + percipitation: 3.5, raindays: 3, sunhours: 2.5, } @@ -135,7 +135,13 @@ async function search(from, to, queries) { // TODO: calculate ratio - return transformer.transform(detailScores) + return { + results: transformer.transform(detailScores), + debug: { + detailScores: detailScores, + minMax: minMax + } + } // return { TODO___scores: detailScores } // return { TODO___scores: calculateAverageScore(detailScores, Object.keys(queries)) } } From 4f4d51b5142e2f43b02bf45a7c01c56d3897b70a Mon Sep 17 00:00:00 2001 From: Timo Volkmann Date: Sun, 14 Jun 2020 13:33:07 +0200 Subject: [PATCH 19/20] little cleanup --- README.md | 40 ++++++++++++++++- backend/app.js | 93 +++++++++++++++++---------------------- backend/climate.js | 79 +++++++++++++++++---------------- backend/package-lock.json | 63 +++----------------------- backend/package.json | 2 +- backend/sampledata.js | 16 ------- 6 files changed, 125 insertions(+), 168 deletions(-) delete mode 100644 backend/sampledata.js diff --git a/README.md b/README.md index a452ec5..d5aebb6 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,45 @@ Campus Cup AKMC Data Traveloptimizer -## Installation +## Backend + +### Requirements + - MariaDB or MySQL + - node `10.12` or higher + - Configure database in `.env`-file or environment variables. See `.env` for reference + - Set API-Key for meteostat.net in `.env`-file or environment variable + - import `setup.sql` for sample data + +### Start + - Run `$(cd backend && npm run start)` + - call http://localhost:3000/v1/update/climate to fetch climate data for sample entries. + +### Search + Customize your search with query parameters. For now, only climate parameters are supported. If you omit climate queries, all climate parameters will be randomized. + + Following queries are supperted by now: + - from=YYYY-MM-DD _(required)_ + - to=YYYY-MM-DD _(required)_ + - temperature=NUMBER,NUMBER + - raindays=NUMBER,NUMBER + - sunhours=NUMBER,NUMBER + - percipitation=NUMBER,NUMBER + +__Examples:__ +http://localhost:3000/v1/search?from=2020-06-14&to=2020-07-29&temperature=27,29&raindays=8,12&sunhours=250,300 +http://localhost:3000/v1/search?from=2020-06-14&to=2020-07-29 + + +### More +To get more search results, add more entries with meteostat station IDs to the `regions` table in the database + + + +## Frontend + +### Installation - Install node 10.15.3 - Run "(cd frontend && npm i)" -# Start dev server +### Start dev server - Run "(cd frontend && npm run start)" diff --git a/backend/app.js b/backend/app.js index 1321d14..c3b7ffa 100644 --- a/backend/app.js +++ b/backend/app.js @@ -4,6 +4,8 @@ var sampledata = require('./sampledata') var db = require('./mysql') var score = require('./score') var transformer = require('./transformer') +var climate = require('./climate') +var moment = require('moment') const app = express() const port = 3000 @@ -34,7 +36,29 @@ const samplePresets = [ app.get('/', (req, res) => res.send('Hello Timo!')) app.get('/v1/regions', (req, res) => res.json(sampleRegions)) app.get('/v1/presets', (req, res) => res.json(samplePresets)) -app.get('/v1/search', (req, res) => { +app.get('/v1/search', searchHandler) +app.get('/v1/update/climate', climateUpdateHandler) + +app.listen(port, () => console.log(`Travopti backend listening at http://localhost:${port}`)) + +function climateUpdateHandler(req, res) { + let parameter = [] + if (req.query.startDate) parameter.push(req.query.startDate) + if (req.query.endDate) parameter.push(req.query.endDate) + climate.update(...parameter).then(x => { + res.send(x) + }).catch(e => { + let result = { + message: 'error during update process. check backend logs.', + error: e + } + res.send(result) + } + + ) +} + +function searchHandler(req, res) { let response = {} response.meta = { @@ -42,7 +66,7 @@ app.get('/v1/search', (req, res) => { query: req.query, headers: req.headers } - + console.log('log params') console.log(req.query.from) console.log(req.query.to) @@ -57,15 +81,14 @@ app.get('/v1/search', (req, res) => { search(req.query.from, req.query.to, validQueries).then(searchResults => { response.data = searchResults res.json(response) + }).catch(e => { + console.log(e) + res.json(e.message) }) -}) - -app.listen(port, () => console.log(`Travopti backend listening at http://localhost:${port}`)) - - - +} async function search(from, to, queries) { + console.log('search') // get Min and Max values for each Parameter const minMax = await getClimateMinMax() // randomize if empty queries @@ -79,23 +102,28 @@ async function search(from, to, queries) { queries.raindays = `${r},${r + 5}` queries.sunhours = `${s},${s + 50}` } - console.log('search') // validate regex: YYYY-MM-DD let re = /([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/i; - if (!from.match(re) || !to.match(re)) throw new Error("invalid parameter: " + from + " " + to) + if (!from.match(re) || !to.match(re)) throw new Error("ERR: invalid parameter:",from,to) + // check for valid period + if (moment(from, 'YYYY-MM-DD').isAfter(moment(to, 'YYYY-MM-DD'))) { + console.log(moment(from, 'YYYY-MM-DD')) + console.log(moment(to, 'YYYY-MM-DD')) + console.log(moment(from, 'YYYY-MM-DD').isAfter(moment(to, 'YYYY-MM-DD'))) + throw new Error("ERR: from is before to date.") + } // -- Prepare search -- // calculate average if traveldates are in more than one month let monthFrom = Number(from.split("-")[1]) let monthTo = Number(to.split("-")[1]) + let travelPeriods = [] - let virtDays = 0 if (monthFrom === monthTo) { let element = { month: monthFrom, days: Number(to.split("-")[2]) - Number(from.split("-")[2]) } - virtDays = element.days travelPeriods.push(element) } else { for (let index = monthFrom; index <= monthTo; index++) { @@ -116,7 +144,6 @@ async function search(from, to, queries) { days: 30 } } - virtDays += element.days travelPeriods.push(element) } } @@ -142,51 +169,11 @@ async function search(from, to, queries) { minMax: minMax } } - // return { TODO___scores: detailScores } - // return { TODO___scores: calculateAverageScore(detailScores, Object.keys(queries)) } -} - -function calculateAverageScore(scorePeriods, types) { - const days = scorePeriods.reduce((total, period) => total += period.days) - let totalScores = {} - - let finalRegionObj = { - - } - - scorePeriods.forEach(element => { - - types.forEach(type => { - element.scores[type].forEach(regionScore => { - - }) - }) - }) -} - -function transformToRegionObjects(scorePeriods) { - let response = [] - // create region objects - scorePeriods[0].climate.forEach(element => { - let obj = { - region_id: element.region_id, - country_id: element.country_id, - name: element.name, - } - response.push(obj) - }) - // - } function calculateScores(type, regionDataRows, searchLowParam, searchMaxParam, minMax) { console.log('calculateScores for', type) - // console.log(searchLowParam) - // console.log(searchMaxParam) - // console.log(minMax) - //console.log(regionDataRows) let result = regionDataRows.map(x => { - // console.log(x.temperature_mean) const sc = Math.round(score.calculateScoreRange(minMax.min[type], minMax.max[type], multiplier[type], x[type], searchLowParam, searchMaxParam) * 100) / 100 return { diff --git a/backend/climate.js b/backend/climate.js index 96ff87c..f0ca4a2 100644 --- a/backend/climate.js +++ b/backend/climate.js @@ -5,7 +5,9 @@ const axios = require('axios') const rangeStartDate = '2010-01' const rangeEndDate = '2018-12' -async function main() { +exports.update = async function (startDate = rangeStartDate, endDate = rangeEndDate) { + console.log('update climate with:', startDate, endDate); + const connection = await mysql.createConnection({ host: process.env.DB_HOST, user: process.env.DB_USER, @@ -26,39 +28,42 @@ async function main() { await writeToDatabase(connection, final2) connection.end(); + let response = 'database update complete. see backend logs for info.' + console.log(response) + return response } -async function createClimateObject(src) { - let response - try { - response = await axios.get(`https://api.meteostat.net/v1/climate/normals?station=${src.meteostat_id}&key=${process.env.METEOSTAT_API_KEY}`) - } catch (error) { - console.log("skipping: couldn't find results for following region: ") - console.log(src.region + " with meteostat_id " + src.meteostat_id) - return [] - } - if (!response.data.data) { - console.log("skipping: no data for station meteostat_id " + src.meteostat_id + " (" + src.region + ")") - return [] - } - let results = [] - for (let index = 1; index <= 12; index++) { - let result = { - region: src.region, - region_id: src.id, - month: index, - temperature: Object.values(response.data.data.temperature)[index - 1] ? Object.values(response.data.data.temperature)[index - 1] : null, - temperature_min: Object.values(response.data.data.temperature_min)[index - 1] ? Object.values(response.data.data.temperature_min)[index - 1] : null, - temperature_max: Object.values(response.data.data.temperature_max)[index - 1] ? Object.values(response.data.data.temperature_max)[index - 1] : null, - precipitation: Object.values(response.data.data.precipitation)[index - 1] ? Object.values(response.data.data.precipitation)[index - 1] : null, - sunshine: Object.values(response.data.data.sunshine)[index - 1] ? Object.values(response.data.data.sunshine)[index - 1] : null, - } - results.push(result) - } - return results -} +// async function createClimateObject(src) { +// let response +// try { +// response = await axios.get(`https://api.meteostat.net/v1/climate/normals?station=${src.meteostat_id}&key=${process.env.METEOSTAT_API_KEY}`) +// } catch (error) { +// console.log("skipping: couldn't find results for following region: ") +// console.log(src.region + " with meteostat_id " + src.meteostat_id) +// return [] +// } +// if (!response.data.data) { +// console.log("skipping: no data for station meteostat_id " + src.meteostat_id + " (" + src.region + ")") +// return [] +// } +// let results = [] +// for (let index = 1; index <= 12; index++) { +// let result = { +// region: src.region, +// region_id: src.id, +// month: index, +// temperature: Object.values(response.data.data.temperature)[index - 1] ? Object.values(response.data.data.temperature)[index - 1] : null, +// temperature_min: Object.values(response.data.data.temperature_min)[index - 1] ? Object.values(response.data.data.temperature_min)[index - 1] : null, +// temperature_max: Object.values(response.data.data.temperature_max)[index - 1] ? Object.values(response.data.data.temperature_max)[index - 1] : null, +// precipitation: Object.values(response.data.data.precipitation)[index - 1] ? Object.values(response.data.data.precipitation)[index - 1] : null, +// sunshine: Object.values(response.data.data.sunshine)[index - 1] ? Object.values(response.data.data.sunshine)[index - 1] : null, +// } +// results.push(result) +// } +// return results +// } -async function createClimateObjectFrom(src, startDate = '2010-01', endDate = '2018-12') { +async function createClimateObjectFrom(src, startDate, endDate) { let response try { response = await axios.get(`https://api.meteostat.net/v1/history/monthly?station=${src.meteostat_id}&start=${startDate}&end=${endDate}&key=${process.env.METEOSTAT_API_KEY}`) @@ -92,17 +97,17 @@ async function createClimateObjectFrom(src, startDate = '2010-01', endDate = '20 return results } -async function writeToDatabase(dbConnection, src) { - src.forEach(async (element) => { +async function writeToDatabase(dbConnection, climateObjArr) { + climateObjArr.forEach(async (element) => { //console.log(element) try { if (!element.year) { await dbConnection.execute(` - INSERT INTO region_climate (region_id, year, month, temperature_mean, temperature_mean_min, temperature_mean_max, percipitation, sunshine) + REPLACE INTO region_climate (region_id, year, month, temperature_mean, temperature_mean_min, temperature_mean_max, percipitation, sunshine) VALUES (${element.region_id}, 0, ${element.month}, ${element.temperature}, ${element.temperature_min}, ${element.temperature_max}, ${element.precipitation}, ${element.sunshine});`) } else { await dbConnection.execute(` - INSERT INTO region_climate (region_id, year, month, temperature_mean, temperature_mean_min, temperature_mean_max, percipitation, sunshine, humidity, raindays) + REPLACE INTO region_climate (region_id, year, month, temperature_mean, temperature_mean_min, temperature_mean_max, percipitation, sunshine, humidity, raindays) VALUES (${element.region_id}, ${element.year}, ${element.month}, ${element.temperature}, ${element.temperature_min}, ${element.temperature_max}, ${element.precipitation}, ${element.sunshine}, ${element.humidity}, ${element.raindays});`) } } catch (error) { @@ -116,6 +121,4 @@ async function writeToDatabase(dbConnection, src) { } } }); -} - -main() \ No newline at end of file +} \ No newline at end of file diff --git a/backend/package-lock.json b/backend/package-lock.json index 249085f..7d000fc 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -112,11 +112,6 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, - "bignumber.js": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", - "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" - }, "binary-extensions": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", @@ -349,11 +344,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, "crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", @@ -787,11 +777,6 @@ "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", "dev": true }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, "json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", @@ -911,22 +896,16 @@ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, + "moment": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.26.0.tgz", + "integrity": "sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw==" + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "mysql": { - "version": "2.18.1", - "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", - "integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==", - "requires": { - "bignumber.js": "9.0.0", - "readable-stream": "2.3.7", - "safe-buffer": "5.1.2", - "sqlstring": "2.3.1" - } - }, "mysql2": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.1.0.tgz", @@ -1103,11 +1082,6 @@ "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", "dev": true }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -1180,20 +1154,6 @@ "strip-json-comments": "~2.0.1" } }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, "readdirp": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", @@ -1375,14 +1335,6 @@ } } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", @@ -1519,11 +1471,6 @@ "prepend-http": "^2.0.0" } }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", diff --git a/backend/package.json b/backend/package.json index c07ce33..c02c6df 100644 --- a/backend/package.json +++ b/backend/package.json @@ -13,7 +13,7 @@ "dotenv": "^8.2.0", "express": "^4.17.1", "lodash": "^4.17.15", - "mysql": "^2.18.1", + "moment": "^2.26.0", "mysql2": "^2.1.0" }, "devDependencies": { diff --git a/backend/sampledata.js b/backend/sampledata.js deleted file mode 100644 index 5c5e8a0..0000000 --- a/backend/sampledata.js +++ /dev/null @@ -1,16 +0,0 @@ -var sampledata = {} - -sampledata.search_response_model = [ - { - region_id: 1, - name: 'Chicago', - country: 'USA', - avg_score: 0.0, - scores: { - temperature: 0.0, - humidity: 0.0, - } - } -] - -module.exports = sampledata; \ No newline at end of file From eb5491b64b79d75a2a59f67eb889ccf4befa8c21 Mon Sep 17 00:00:00 2001 From: Timo Volkmann Date: Sun, 14 Jun 2020 19:28:06 +0200 Subject: [PATCH 20/20] changed query syntax --- backend/app.js | 149 ++++++++++++++++++++++++++++------------------ backend/base64.js | 23 +++++++ 2 files changed, 113 insertions(+), 59 deletions(-) create mode 100644 backend/base64.js diff --git a/backend/app.js b/backend/app.js index c3b7ffa..f76b308 100644 --- a/backend/app.js +++ b/backend/app.js @@ -1,11 +1,11 @@ -var express = require('express') -var _ = require('lodash') -var sampledata = require('./sampledata') -var db = require('./mysql') -var score = require('./score') -var transformer = require('./transformer') -var climate = require('./climate') -var moment = require('moment') +const express = require('express') +const moment = require('moment') +const _ = require('lodash') +const db = require('./mysql') +const score = require('./score') +const transformer = require('./transformer') +const climate = require('./climate') +const base = require('./base64') const app = express() const port = 3000 @@ -17,13 +17,6 @@ const multiplier = { sunhours: 2.5, } -const sampleRegions = [ - { - id: 29837, - name: "Timbuktu", - country: "Mali" - } -] const samplePresets = [ { id: 29837, @@ -34,8 +27,8 @@ const samplePresets = [ ] app.get('/', (req, res) => res.send('Hello Timo!')) -app.get('/v1/regions', (req, res) => res.json(sampleRegions)) -app.get('/v1/presets', (req, res) => res.json(samplePresets)) +app.get('/v1/regions', (req, res) => getAllRegions().then(x => res.json({ data: x }))) +app.get('/v1/presets', (req, res) => res.json({ data: samplePresets})) app.get('/v1/search', searchHandler) app.get('/v1/update/climate', climateUpdateHandler) @@ -60,25 +53,23 @@ function climateUpdateHandler(req, res) { function searchHandler(req, res) { let response = {} - + response.meta = { params: req.params, query: req.query, headers: req.headers } - - console.log('log params') - console.log(req.query.from) - console.log(req.query.to) - console.log(req.query.temperature) - - let validQueries = {} - if (req.query.temperature) validQueries['temperature_mean'] = req.query.temperature - if (req.query.percipitation) validQueries['percipitation'] = req.query.percipitation - if (req.query.raindays) validQueries['raindays'] = req.query.raindays - if (req.query.sunhours) validQueries['sunhours'] = req.query.sunhours - - search(req.query.from, req.query.to, validQueries).then(searchResults => { + + let q = req.query.q ? base.base64ToObj(req.query.q) : req.query + console.log('Q:', q) + + let queryObj = {} + if (q.temperature) queryObj['temperature_mean'] = q.temperature + if (q.percipitation) queryObj['percipitation'] = q.percipitation + if (q.raindays) queryObj['raindays'] = q.raindays + if (q.sunhours) queryObj['sunhours'] = q.sunhours + + scoreAndSearch(q.from, q.to, queryObj).then(searchResults => { response.data = searchResults res.json(response) }).catch(e => { @@ -87,42 +78,60 @@ function searchHandler(req, res) { }) } -async function search(from, to, queries) { +async function scoreAndSearch(from, to, queries) { + // TODO break funtion into parts when implementing non-climate queries and modularize (new file) + console.log('search') + // get Min and Max values for each Parameter const minMax = await getClimateMinMax() + // randomize if empty queries if (_.isEmpty(queries)) { - let t = _.random(minMax.min.temperature_mean, minMax.max.temperature_mean-5) - let p = _.random(minMax.min.percipitation, minMax.max.percipitation-50) - let r = _.random(minMax.min.raindays, minMax.max.raindays-5) - let s = _.random(minMax.min.sunhours, minMax.max.sunhours-50) + let t = _.round(_.random(minMax.min.temperature_mean, minMax.max.temperature_mean-5),0) + let p = _.round(_.random(minMax.min.percipitation, minMax.max.percipitation - 50), 0) + let r = _.round(_.random(minMax.min.raindays, minMax.max.raindays - 5), 0) + let s = _.round(_.random(minMax.min.sunhours, minMax.max.sunhours - 50), 0) queries.temperature_mean = `${t},${t + 5}` queries.percipitation = `${p},${p + 50}` queries.raindays = `${r},${r + 5}` queries.sunhours = `${s},${s + 50}` } - // validate regex: YYYY-MM-DD - let re = /([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/i; - if (!from.match(re) || !to.match(re)) throw new Error("ERR: invalid parameter:",from,to) - // check for valid period - if (moment(from, 'YYYY-MM-DD').isAfter(moment(to, 'YYYY-MM-DD'))) { - console.log(moment(from, 'YYYY-MM-DD')) - console.log(moment(to, 'YYYY-MM-DD')) - console.log(moment(from, 'YYYY-MM-DD').isAfter(moment(to, 'YYYY-MM-DD'))) - throw new Error("ERR: from is before to date.") + queries = oldToNewQuerySyntax(queries) + console.log(queries) + + // TODO simplify and remove support for old query syntaax + let monthFrom = 0 + let monthTo = 0 + let dayFrom = 0 + let dayTo = 0 + + if (_.isNumber(from) && _.isNumber(to)) { + let dateFrom = moment(from).toDate() + let dateTo = moment(to).toDate() + monthFrom = dateFrom.getMonth() + monthTo = dateTo.getMonth() + dayFrom = dateFrom.getDay() + dayTo = dateTo.getDay() + if (moment(dateFrom).add(23, 'hours').isAfter(moment(dateTo))) throw new Error("ERR: 'to' must be at least one day after 'from'.") + } else { + // to still support old query syntax + let re = /([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/i; + monthFrom = Number(from.split("-")[1]) + monthTo = Number(to.split("-")[1]) + dayFrom = Number(from.split("-")[2]) + dayTo = Number(to.split("-")[2]) + if (!from.match(re) || !to.match(re)) throw new Error("ERR: invalid parameter:",from,to) + if (moment(from, 'YYYY-MM-DD').add(23, 'hours').isAfter(moment(to, 'YYYY-MM-DD'))) throw new Error("ERR: 'to' must be at least one day after 'from'.") } // -- Prepare search -- - // calculate average if traveldates are in more than one month - let monthFrom = Number(from.split("-")[1]) - let monthTo = Number(to.split("-")[1]) - + // to calculate average if traveldates are in more than one month let travelPeriods = [] if (monthFrom === monthTo) { let element = { month: monthFrom, - days: Number(to.split("-")[2]) - Number(from.split("-")[2]) + days: dayTo - dayFrom } travelPeriods.push(element) } else { @@ -131,12 +140,12 @@ async function search(from, to, queries) { if (index === monthFrom) { element = { month: index, - days: 32 - Number(from.split("-")[2]) + days: 32 - dayFrom } } else if (index === monthTo) { element = { month: index, - days: Number(to.split("-")[2]) + days: dayTo } } else { element = { @@ -148,20 +157,20 @@ async function search(from, to, queries) { } } - // calculate score + // calculate detail scores let detailScores = await Promise.all(travelPeriods.map(async period => { period.climate = await getAllRegionsWithClimatePerMonth(period.month) period.scores = {} Object.entries(queries).forEach(([key, value]) => { // console.log('key',key) // console.log('val', value) - period.scores[key] = calculateScores(key, period.climate, value.split(',')[0], value.split(',')[1], minMax) + period.scores[key] = calculateScores(key, period.climate, value[0], value[1], minMax) }); return period })); - // TODO: calculate ratio + // calculate ratio and transform into target object return { results: transformer.transform(detailScores), debug: { @@ -216,10 +225,17 @@ async function getQueryRows(sql) { return rows } -function getRegionIdsWithMeteostatData() { - console.log('getRegionIdsWithMeteostatData') - //return await getQueryRows(`SELECT * FROM regions WHERE meteostat_id IS NOT NULL`) - return getQueryRows(`SELECT region_id FROM region_climate GROUP BY region_id`) +function getAllRegions() { + const sql = `SELECT + regions.id AS region_id, + regions.region AS name, + regions.country_id AS country_id, + countries.country AS country, + regions.meteostat_id AS meteostat_id + FROM regions + JOIN countries + ON regions.country_id = countries.id` + return getQueryRows(sql) } function getClimatePerRegionAndMonth(regionId, month) { @@ -242,4 +258,19 @@ function getAllRegionsWithClimatePerMonth(month) { ROUND(AVG(region_climate.sunshine), 1) AS sunhours FROM region_climate JOIN regions ON region_climate.region_id = regions.id WHERE region_climate.month = ${month} GROUP BY region_id` return getQueryRows(sql) +} + +function oldToNewQuerySyntax(queries) { + let res = {} + try { + if (queries.temperature_mean) res.temperature_mean = [queries.temperature_mean.split(',')[0], queries.temperature_mean.split(',')[1]] + if (queries.percipitation) res.percipitation = [queries.percipitation.split(',')[0], queries.percipitation.split(',')[1]] + if (queries.raindays) res.raindays = [queries.raindays.split(',')[0], queries.raindays.split(',')[1]] + if (queries.sunhours) res.sunhours = [queries.sunhours.split(',')[0], queries.sunhours.split(',')[1]] + console.log('queries successfully transformed'); + } catch (error) { + console.log('queries are ok'); + return queries + } + return res } \ No newline at end of file diff --git a/backend/base64.js b/backend/base64.js new file mode 100644 index 0000000..d4d9583 --- /dev/null +++ b/backend/base64.js @@ -0,0 +1,23 @@ +/** + * Encodes an object as base64 string.. + * @param obj The object to encode + */ +exports.objToBase64 = function(obj) { + return btoa(JSON.stringify(obj)); +} + +/** + * Decodes a base64 encoded object. + * @param base64 Encoded object + */ +exports.base64ToObj = function(base64) { + return JSON.parse(atob(base64)); +} + +function atob(base64) { + return Buffer.from(base64, 'base64').toString('binary') +} + +function btoa(string) { + return Buffer.from(string).toString('base64') +} \ No newline at end of file