Compare commits
7 Commits
develop
...
refactor/v
| Author | SHA1 | Date | |
|---|---|---|---|
| 498f2daaf5 | |||
|
|
9c22609ae1 | ||
|
|
32e8e6cf59 | ||
|
|
0f0f8eb590 | ||
|
|
2dfec236b2 | ||
|
|
8074de0640 | ||
|
|
24971248c9 |
4
.gitignore
vendored
@ -1,3 +1,7 @@
|
|||||||
|
# credentials
|
||||||
|
.env
|
||||||
|
config.json
|
||||||
|
|
||||||
# compiled output
|
# compiled output
|
||||||
/dist
|
/dist
|
||||||
|
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
-- Exportiere Datenbank Struktur für travopti
|
-- Exportiere Datenbank Struktur für travopti
|
||||||
DROP DATABASE IF EXISTS `travopti`;
|
DROP DATABASE IF EXISTS `travopti`;
|
||||||
CREATE DATABASE IF NOT EXISTS `travopti` /*!40100 DEFAULT CHARACTER SET latin1 */;
|
CREATE DATABASE IF NOT EXISTS `travopti` /*!40100 DEFAULT CHARACTER SET utf8 */;
|
||||||
USE `travopti`;
|
USE `travopti`;
|
||||||
|
|
||||||
-- Exportiere Struktur von Tabelle travopti.countries
|
-- Exportiere Struktur von Tabelle travopti.countries
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
METEOSTAT_API_KEY=LMlDskju
|
|
||||||
DB_HOST=localhost
|
|
||||||
DB_USER=root
|
|
||||||
DB_PASSWORD=devtest
|
|
||||||
DB_PORT=3306
|
|
||||||
6
backend/.env.sample
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
PORT=
|
||||||
|
METEOSTAT_API_KEY=
|
||||||
|
DB_HOST=
|
||||||
|
DB_USER=
|
||||||
|
DB_PASSWORD=
|
||||||
|
DB_PORT=
|
||||||
@ -1,17 +1,16 @@
|
|||||||
const express = require('express')
|
const express = require('express')
|
||||||
const moment = require('moment')
|
const moment = require('moment')
|
||||||
const _ = require('lodash')
|
const _ = require('lodash')
|
||||||
const db = require('./mysql')
|
const score = require('./util/score')
|
||||||
const score = require('./score')
|
const transformer = require('./util/transformer')
|
||||||
const transformer = require('./transformer')
|
const base = require('./util/base64')
|
||||||
const climate = require('./climate')
|
|
||||||
const base = require('./base64')
|
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
|
|
||||||
const port = 3000
|
const port = 3000
|
||||||
//const multiplier_temp = 5
|
//const multiplier_temp = 5
|
||||||
const multiplier = {
|
const multiplier = {
|
||||||
temperature_mean: 5,
|
temperature_mean_max: 5,
|
||||||
percipitation: 3.5,
|
percipitation: 3.5,
|
||||||
raindays: 3,
|
raindays: 3,
|
||||||
sunhours: 2.5,
|
sunhours: 2.5,
|
||||||
@ -30,7 +29,7 @@ app.get('/', (req, res) => res.send('Hello Timo!'))
|
|||||||
app.get('/v1/regions', (req, res) => getAllRegions().then(x => res.json({ data: x })))
|
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/presets', (req, res) => res.json({ data: samplePresets}))
|
||||||
app.get('/v1/search', searchHandler)
|
app.get('/v1/search', searchHandler)
|
||||||
app.get('/v1/update/climate', climateUpdateHandler)
|
app.get('/v1/climate/update', climateUpdateHandler)
|
||||||
|
|
||||||
app.listen(port, () => console.log(`Travopti backend listening at http://localhost:${port}`))
|
app.listen(port, () => console.log(`Travopti backend listening at http://localhost:${port}`))
|
||||||
|
|
||||||
@ -64,7 +63,7 @@ function searchHandler(req, res) {
|
|||||||
console.log('Q:', q)
|
console.log('Q:', q)
|
||||||
|
|
||||||
let queryObj = {}
|
let queryObj = {}
|
||||||
if (q.temperature) queryObj['temperature_mean'] = q.temperature
|
if (q.temperature) queryObj['temperature_mean_max'] = q.temperature
|
||||||
if (q.percipitation) queryObj['percipitation'] = q.percipitation
|
if (q.percipitation) queryObj['percipitation'] = q.percipitation
|
||||||
if (q.raindays) queryObj['raindays'] = q.raindays
|
if (q.raindays) queryObj['raindays'] = q.raindays
|
||||||
if (q.sunhours) queryObj['sunhours'] = q.sunhours
|
if (q.sunhours) queryObj['sunhours'] = q.sunhours
|
||||||
@ -88,11 +87,11 @@ async function scoreAndSearch(from, to, queries) {
|
|||||||
|
|
||||||
// randomize if empty queries
|
// randomize if empty queries
|
||||||
if (_.isEmpty(queries)) {
|
if (_.isEmpty(queries)) {
|
||||||
let t = _.round(_.random(minMax.min.temperature_mean, minMax.max.temperature_mean-5),0)
|
let t = _.round(_.random(minMax.min.temperature_mean_max, minMax.max.temperature_mean_max-5),0)
|
||||||
let p = _.round(_.random(minMax.min.percipitation, minMax.max.percipitation - 50), 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 r = _.round(_.random(minMax.min.raindays, minMax.max.raindays - 5), 0)
|
||||||
let s = _.round(_.random(minMax.min.sunhours, minMax.max.sunhours - 50), 0)
|
let s = _.round(_.random(minMax.min.sunhours, minMax.max.sunhours - 50), 0)
|
||||||
queries.temperature_mean = `${t},${t + 5}`
|
queries.temperature_mean_max = `${t},${t + 5}`
|
||||||
queries.percipitation = `${p},${p + 50}`
|
queries.percipitation = `${p},${p + 50}`
|
||||||
queries.raindays = `${r},${r + 5}`
|
queries.raindays = `${r},${r + 5}`
|
||||||
queries.sunhours = `${s},${s + 50}`
|
queries.sunhours = `${s},${s + 50}`
|
||||||
@ -263,7 +262,7 @@ function getAllRegionsWithClimatePerMonth(month) {
|
|||||||
function oldToNewQuerySyntax(queries) {
|
function oldToNewQuerySyntax(queries) {
|
||||||
let res = {}
|
let res = {}
|
||||||
try {
|
try {
|
||||||
if (queries.temperature_mean) res.temperature_mean = [queries.temperature_mean.split(',')[0], queries.temperature_mean.split(',')[1]]
|
if (queries.temperature_mean_max) res.temperature_mean_max = [queries.temperature_mean_max.split(',')[0], queries.temperature_mean_max.split(',')[1]]
|
||||||
if (queries.percipitation) res.percipitation = [queries.percipitation.split(',')[0], queries.percipitation.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.raindays) res.raindays = [queries.raindays.split(',')[0], queries.raindays.split(',')[1]]
|
||||||
if (queries.sunhours) res.sunhours = [queries.sunhours.split(',')[0], queries.sunhours.split(',')[1]]
|
if (queries.sunhours) res.sunhours = [queries.sunhours.split(',')[0], queries.sunhours.split(',')[1]]
|
||||||
|
|||||||
@ -1,124 +0,0 @@
|
|||||||
require('dotenv').config()
|
|
||||||
const mysql = require('mysql2/promise');
|
|
||||||
const axios = require('axios')
|
|
||||||
|
|
||||||
const rangeStartDate = '2010-01'
|
|
||||||
const rangeEndDate = '2018-12'
|
|
||||||
|
|
||||||
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,
|
|
||||||
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`)
|
|
||||||
|
|
||||||
// let temp = await Promise.all(result.map(x => createClimateObject(x)))
|
|
||||||
// let final = temp.reduce((total, element) => total.concat(element), [])
|
|
||||||
|
|
||||||
// await writeToDatabase(connection, final)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
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 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}`)
|
|
||||||
} 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, climateObjArr) {
|
|
||||||
climateObjArr.forEach(async (element) => {
|
|
||||||
//console.log(element)
|
|
||||||
try {
|
|
||||||
if (!element.year) {
|
|
||||||
await dbConnection.execute(`
|
|
||||||
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(`
|
|
||||||
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) {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
BIN
backend/data/regions/images/10.jpg
Normal file
|
After Width: | Height: | Size: 238 KiB |
BIN
backend/data/regions/images/11.jpg
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
backend/data/regions/images/12.jpg
Normal file
|
After Width: | Height: | Size: 253 KiB |
BIN
backend/data/regions/images/13.jpg
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
backend/data/regions/images/18.jpg
Normal file
|
After Width: | Height: | Size: 555 KiB |
BIN
backend/data/regions/images/2.jpg
Normal file
|
After Width: | Height: | Size: 323 KiB |
BIN
backend/data/regions/images/24.jpg
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
backend/data/regions/images/3.jpg
Normal file
|
After Width: | Height: | Size: 995 KiB |
BIN
backend/data/regions/images/6.jpg
Normal file
|
After Width: | Height: | Size: 287 KiB |
BIN
backend/data/regions/images/7.jpg
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
backend/data/regions/images/9.jpg
Normal file
|
After Width: | Height: | Size: 504 KiB |
52
backend/index.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
const express = require("express");
|
||||||
|
const bodyParser = require("body-parser");
|
||||||
|
const path = require("path");
|
||||||
|
const morgan = require("morgan");
|
||||||
|
const dbConnection = require("./util/dbConnection");
|
||||||
|
const fs = require("fs");
|
||||||
|
const httpolyglot = require("httpolyglot");
|
||||||
|
require('dotenv').config()
|
||||||
|
|
||||||
|
// credentials
|
||||||
|
const port = process.env.PORT
|
||||||
|
|
||||||
|
// Router
|
||||||
|
const search = require("./routes/search");
|
||||||
|
const regions = require("./routes/regions");
|
||||||
|
const climate = require("./routes/climate");
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const dbConn = await dbConnection();
|
||||||
|
|
||||||
|
// Express middleware
|
||||||
|
app.use(morgan("dev"));
|
||||||
|
app.use(express.static(path.join(__dirname, "../../dist")));
|
||||||
|
app.use(bodyParser.json());
|
||||||
|
|
||||||
|
// Express routes
|
||||||
|
app.use(search(dbConn));
|
||||||
|
app.use(regions(dbConn));
|
||||||
|
app.use(climate(dbConn));
|
||||||
|
|
||||||
|
app.use((err, req, res, next) => {
|
||||||
|
// 500
|
||||||
|
if (true) {
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
res.status(500).send();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start webserver
|
||||||
|
app.listen(port, () => {
|
||||||
|
console.log(`Travopti backend listening at http://localhost:${port}`)
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// TODO: logging
|
||||||
|
console.error("Failed to start the webserver");
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
})();
|
||||||
6
backend/mockdata/multiplier.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"temperature_mean_max": 5,
|
||||||
|
"percipitation": 3.5,
|
||||||
|
"raindays": 3,
|
||||||
|
"sunhours": 2.5
|
||||||
|
}
|
||||||
6
backend/mockdata/sample-presets.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"id": 29837,
|
||||||
|
"parameter": "temperature",
|
||||||
|
"label": "warm",
|
||||||
|
"values": [22, 25]
|
||||||
|
}
|
||||||
14
backend/models/getRegions.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
module.exports = async (dbConn, lat, long, radius) => {
|
||||||
|
const regions = await dbConn.query(
|
||||||
|
`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 regions;
|
||||||
|
};
|
||||||
|
|
||||||
6
backend/models/getSearchPresets.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
module.exports = async (dbConn) => {
|
||||||
|
// TODO: Implement pulling data from database
|
||||||
|
const presets = require ("../mockdata/sample-presets.json")
|
||||||
|
const res = presets
|
||||||
|
return res;
|
||||||
|
};
|
||||||
29
backend/models/getSearchResults.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
const base64 = require ("../util/base64.js")
|
||||||
|
const ss = require ("../util/scoreAndSearch.js")
|
||||||
|
|
||||||
|
module.exports = async (dbConn, req, res) => {
|
||||||
|
let response = {}
|
||||||
|
|
||||||
|
response.meta = {
|
||||||
|
params: req.params,
|
||||||
|
query: req.query,
|
||||||
|
headers: req.headers
|
||||||
|
}
|
||||||
|
|
||||||
|
let q = req.query.q ? base64.base64ToObj(req.query.q) : req.query
|
||||||
|
console.log('Q:', q)
|
||||||
|
|
||||||
|
let queryObj = {}
|
||||||
|
if (q.temperature) queryObj['temperature_mean_max'] = q.temperature
|
||||||
|
if (q.percipitation) queryObj['percipitation'] = q.percipitation
|
||||||
|
if (q.raindays) queryObj['raindays'] = q.raindays
|
||||||
|
if (q.sunhours) queryObj['sunhours'] = q.sunhours
|
||||||
|
|
||||||
|
ss.scoreAndSearch(q.from, q.to, queryObj, dbConn).then(searchResults => {
|
||||||
|
response.data = searchResults
|
||||||
|
res.json(response)
|
||||||
|
}).catch(e => {
|
||||||
|
console.log(e)
|
||||||
|
res.json(e.message)
|
||||||
|
})
|
||||||
|
};
|
||||||
84
backend/models/handleClimateUpdate.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
const axios = require('axios')
|
||||||
|
|
||||||
|
// TODO: Automatically retrieve dates via aviable Data and get rid of random dates
|
||||||
|
const rangeStartDate = '2010-01' // If no date is given, this date will be used as startDate
|
||||||
|
const rangeEndDate = '2018-12'// If no date is given, this date will be used as endDate
|
||||||
|
|
||||||
|
// TODO: call method periodically, not over API
|
||||||
|
module.exports = async (dbConn, startDate = rangeStartDate, endDate = rangeEndDate) => {
|
||||||
|
console.log('update climate with:', startDate, endDate);
|
||||||
|
|
||||||
|
const result = await dbConn.query(`SELECT * FROM regions WHERE meteostat_id IS NOT NULL`)
|
||||||
|
|
||||||
|
const climateObject = await Promise.all(result.map(src => createClimateObjectFrom(src, startDate, endDate)))
|
||||||
|
const climateObjectArr = climateObject.reduce((total, element) => total.concat(element), [])
|
||||||
|
|
||||||
|
await writeToDatabase(dbConn, climateObjectArr)
|
||||||
|
|
||||||
|
const res = 'region_climate update complete. see backend logs for info.'
|
||||||
|
console.log(res)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createClimateObjectFrom(src, startDate, endDate) {
|
||||||
|
let res
|
||||||
|
try {
|
||||||
|
res = 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 (!res.data.data) {
|
||||||
|
console.log("skipping: no data for station meteostat_id " + src.meteostat_id + " (" + src.region + ")")
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
const retVal = res.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 retVal
|
||||||
|
}
|
||||||
|
|
||||||
|
async function writeToDatabase(dbConn, climateObjArr) {
|
||||||
|
for (const element of climateObjArr) {
|
||||||
|
//console.log(element)
|
||||||
|
try {
|
||||||
|
await dbConn.query(`
|
||||||
|
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})
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
temperature_mean = ${element.temperature},
|
||||||
|
temperature_mean_min = ${element.temperature_min},
|
||||||
|
temperature_mean_max = ${element.temperature_max},
|
||||||
|
percipitation = ${element.precipitation},
|
||||||
|
sunshine = ${element.sunshine},
|
||||||
|
humidity = ${element.humidity},
|
||||||
|
raindays = ${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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -1,92 +0,0 @@
|
|||||||
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',
|
|
||||||
// typeCast: function (field, next) {
|
|
||||||
// if (field.type == "INT") {
|
|
||||||
// var value = field.string();
|
|
||||||
// return (value === null) ? null : Number(value);
|
|
||||||
// }
|
|
||||||
// return next();
|
|
||||||
// }
|
|
||||||
decimalNumbers: true
|
|
||||||
});
|
|
||||||
|
|
||||||
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;
|
|
||||||
114
backend/package-lock.json
generated
@ -25,6 +25,16 @@
|
|||||||
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
|
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/geojson": {
|
||||||
|
"version": "7946.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.7.tgz",
|
||||||
|
"integrity": "sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ=="
|
||||||
|
},
|
||||||
|
"@types/node": {
|
||||||
|
"version": "13.13.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.12.tgz",
|
||||||
|
"integrity": "sha512-zWz/8NEPxoXNT9YyF2osqyA9WjssZukYpgI4UYZpOjcyqwIUqWGkcCionaEb9Ki+FULyPyvNFpg/329Kd2/pbw=="
|
||||||
|
},
|
||||||
"abbrev": {
|
"abbrev": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||||
@ -112,6 +122,14 @@
|
|||||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"basic-auth": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "5.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"binary-extensions": {
|
"binary-extensions": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz",
|
||||||
@ -645,6 +663,11 @@
|
|||||||
"toidentifier": "1.0.0"
|
"toidentifier": "1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"httpolyglot": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/httpolyglot/-/httpolyglot-0.1.2.tgz",
|
||||||
|
"integrity": "sha1-5NNH/omEpi9GfUBg31J/GFH2mXs="
|
||||||
|
},
|
||||||
"iconv-lite": {
|
"iconv-lite": {
|
||||||
"version": "0.4.24",
|
"version": "0.4.24",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
@ -842,6 +865,30 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mariadb": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mariadb/-/mariadb-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-78zrj9SpF6I3eVWMMkdm+SEfcsMb/uWVKPo7pKhhCfuGywEf3I1dK0ewSTjD0SyTEgSEuWn/H/I4TIErGgYTCQ==",
|
||||||
|
"requires": {
|
||||||
|
"@types/geojson": "^7946.0.7",
|
||||||
|
"@types/node": "^13.9.8",
|
||||||
|
"denque": "^1.4.1",
|
||||||
|
"iconv-lite": "^0.5.1",
|
||||||
|
"long": "^4.0.0",
|
||||||
|
"moment-timezone": "^0.5.31",
|
||||||
|
"please-upgrade-node": "^3.2.0"
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"media-typer": {
|
"media-typer": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||||
@ -901,6 +948,33 @@
|
|||||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.26.0.tgz",
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.26.0.tgz",
|
||||||
"integrity": "sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw=="
|
"integrity": "sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw=="
|
||||||
},
|
},
|
||||||
|
"moment-timezone": {
|
||||||
|
"version": "0.5.31",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.31.tgz",
|
||||||
|
"integrity": "sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA==",
|
||||||
|
"requires": {
|
||||||
|
"moment": ">= 2.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"morgan": {
|
||||||
|
"version": "1.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",
|
||||||
|
"integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==",
|
||||||
|
"requires": {
|
||||||
|
"basic-auth": "~2.0.1",
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "~2.0.0",
|
||||||
|
"on-finished": "~2.3.0",
|
||||||
|
"on-headers": "~1.0.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"depd": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
@ -1025,6 +1099,11 @@
|
|||||||
"ee-first": "1.1.1"
|
"ee-first": "1.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"on-headers": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
|
||||||
|
},
|
||||||
"once": {
|
"once": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
@ -1065,6 +1144,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||||
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
|
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
|
||||||
},
|
},
|
||||||
|
"path": {
|
||||||
|
"version": "0.12.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz",
|
||||||
|
"integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=",
|
||||||
|
"requires": {
|
||||||
|
"process": "^0.11.1",
|
||||||
|
"util": "^0.10.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"path-to-regexp": {
|
"path-to-regexp": {
|
||||||
"version": "0.1.7",
|
"version": "0.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||||
@ -1076,12 +1164,25 @@
|
|||||||
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
|
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"please-upgrade-node": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==",
|
||||||
|
"requires": {
|
||||||
|
"semver-compare": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"prepend-http": {
|
"prepend-http": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
|
||||||
"integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=",
|
"integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"process": {
|
||||||
|
"version": "0.11.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||||
|
"integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
|
||||||
|
},
|
||||||
"proxy-addr": {
|
"proxy-addr": {
|
||||||
"version": "2.0.6",
|
"version": "2.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
|
||||||
@ -1214,6 +1315,11 @@
|
|||||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"semver-compare": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w="
|
||||||
|
},
|
||||||
"semver-diff": {
|
"semver-diff": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz",
|
||||||
@ -1471,6 +1577,14 @@
|
|||||||
"prepend-http": "^2.0.0"
|
"prepend-http": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"util": {
|
||||||
|
"version": "0.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
|
||||||
|
"integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
|
||||||
|
"requires": {
|
||||||
|
"inherits": "2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"utils-merge": {
|
"utils-merge": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||||
|
|||||||
@ -4,17 +4,22 @@
|
|||||||
"description": "",
|
"description": "",
|
||||||
"main": "app.js",
|
"main": "app.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "nodemon ./app.js"
|
"start": "nodemon ./index.js"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.19.2",
|
"axios": "^0.19.2",
|
||||||
|
"body-parser": "^1.19.0",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
|
"httpolyglot": "^0.1.2",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
|
"mariadb": "^2.4.0",
|
||||||
"moment": "^2.26.0",
|
"moment": "^2.26.0",
|
||||||
"mysql2": "^2.1.0"
|
"morgan": "^1.10.0",
|
||||||
|
"mysql2": "^2.1.0",
|
||||||
|
"path": "^0.12.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"nodemon": "^2.0.4"
|
"nodemon": "^2.0.4"
|
||||||
|
|||||||
11
backend/routes/climate.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
const router = require("express").Router()
|
||||||
|
const handleClimateUpdate = require("../models/handleClimateUpdate.js")
|
||||||
|
|
||||||
|
module.exports = dbConn => {
|
||||||
|
router.put("/api/v1/climate/update", async (req, res) => {
|
||||||
|
const update = await handleClimateUpdate(dbConn)
|
||||||
|
res.json(update)
|
||||||
|
});
|
||||||
|
|
||||||
|
return router;
|
||||||
|
};
|
||||||
10
backend/routes/regions.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
const router = require("express").Router();
|
||||||
|
const getRegions = require("../models/getRegions.js");
|
||||||
|
|
||||||
|
module.exports = dbConn => {
|
||||||
|
router.get("/api/v1/regions", async (req, res) => {
|
||||||
|
res.json(await getRegions(dbConn));
|
||||||
|
});
|
||||||
|
|
||||||
|
return router;
|
||||||
|
};
|
||||||
20
backend/routes/search.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
const router = require("express").Router();
|
||||||
|
const getSearchResults = require("../models/getSearchResults.js");
|
||||||
|
const getSearchPresets = require("../models/getSearchPresets.js");
|
||||||
|
|
||||||
|
module.exports = dbConn => {
|
||||||
|
router.get("/api/v1/search", async (req, res) => {
|
||||||
|
const query = req.query.q;
|
||||||
|
if (query != undefined) {
|
||||||
|
res.json(await getSearchResults(dbConn, req));
|
||||||
|
} else {
|
||||||
|
res.status(400).send();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/api/v1/search/presets", async (req, res) => {
|
||||||
|
res.json(await getSearchPresets(dbConn));
|
||||||
|
});
|
||||||
|
|
||||||
|
return router;
|
||||||
|
};
|
||||||
15
backend/util/calculateScores.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
function calculateScores(type, regionDataRows, searchLowParam, searchMaxParam, minMax) {
|
||||||
|
console.log('calculateScores for', type)
|
||||||
|
let result = regionDataRows.map(x => {
|
||||||
|
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,
|
||||||
|
type: type,
|
||||||
|
value: x[type],
|
||||||
|
score: x[type] === null ? null : sc
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
||||||
47
backend/util/dbConnection.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
const mariadb = require("mariadb");
|
||||||
|
let dbConn;
|
||||||
|
let conPool;
|
||||||
|
// mariadb doc: https://github.com/MariaDB/mariadb-connector-nodejs/blob/master/documentation/promise-api.md
|
||||||
|
|
||||||
|
async function reconnect() {
|
||||||
|
try {
|
||||||
|
dbConn = await conPool.getConnection();
|
||||||
|
} catch (e) {
|
||||||
|
if (e.code === "ECONNREFUSED") {
|
||||||
|
let err = new Error("Lost connection to the database");
|
||||||
|
err.code = "ERR_DB_CONN_LOST";
|
||||||
|
throw err;
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = async config => {
|
||||||
|
conPool = mariadb.createPool({
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
user: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
port: process.env.DB_PORT,
|
||||||
|
database: 'travopti',
|
||||||
|
connectionLimit: 10
|
||||||
|
});
|
||||||
|
|
||||||
|
dbConn = await conPool.getConnection();
|
||||||
|
return {
|
||||||
|
async query(q, p) {
|
||||||
|
let res;
|
||||||
|
try {
|
||||||
|
res = await dbConn.query(q, p);
|
||||||
|
} catch (e) {
|
||||||
|
if (e.code === "ER_CMD_CONNECTION_CLOSED") {
|
||||||
|
await reconnect();
|
||||||
|
await this.query(q, p);
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
15
backend/util/getAllRegionsWithClimatePerMonth.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
function getAllRegionsWithClimatePerMonth(month) {
|
||||||
|
console.log('getAllRegionsWithClimatePerMonth')
|
||||||
|
const sql = `SELECT
|
||||||
|
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)
|
||||||
|
}
|
||||||
22
backend/util/getClimateMinMax.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
exports.getClimateMinMax = async function (dbConn) {
|
||||||
|
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(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(raindays) AS raindays,
|
||||||
|
MAX(sunshine) AS sunhours
|
||||||
|
FROM region_climate`
|
||||||
|
const [qResMin, qResMax] = await Promise.all([await dbConn.query(sqlMin), await dbConn.query(sqlMax)])
|
||||||
|
//console.log(qResMin)
|
||||||
|
return { min: qResMin[0], max: qResMax[0] }
|
||||||
|
}
|
||||||
5
backend/util/getClimatePerRegionAndMonth.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
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 getQueryRows(sql)
|
||||||
|
}
|
||||||
14
backend/util/oldToNewQuerySyntax.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
exports.oldToNewQuerySyntax = function (queries) {
|
||||||
|
let res = {}
|
||||||
|
try {
|
||||||
|
if (queries.temperature_mean_max) res.temperature_mean_max = [queries.temperature_mean_max.split(',')[0], queries.temperature_mean_max.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
|
||||||
|
}
|
||||||
107
backend/util/scoreAndSearch.js
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
const _ = require('lodash')
|
||||||
|
const getClimateMinMax = require("./getClimateMinMax.js")
|
||||||
|
const oldToNewQuerySyntax = require("./oldToNewQuerySyntax.js")
|
||||||
|
const moment = require("moment")
|
||||||
|
|
||||||
|
exports.scoreAndSearch = async function (from, to, queries, dbConn) {
|
||||||
|
// 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.getClimateMinMax(dbConn)
|
||||||
|
|
||||||
|
// randomize if empty queries
|
||||||
|
// TODO: WHY
|
||||||
|
if (_.isEmpty(queries)) {
|
||||||
|
let t = _.round(_.random(minMax.min.temperature_mean_max, minMax.max.temperature_mean_max-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_max = `${t},${t + 5}`
|
||||||
|
queries.percipitation = `${p},${p + 50}`
|
||||||
|
queries.raindays = `${r},${r + 5}`
|
||||||
|
queries.sunhours = `${s},${s + 50}`
|
||||||
|
}
|
||||||
|
queries = oldToNewQuerySyntax.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 --
|
||||||
|
// to calculate average if traveldates are in more than one month
|
||||||
|
let travelPeriods = []
|
||||||
|
if (monthFrom === monthTo) {
|
||||||
|
let element = {
|
||||||
|
month: monthFrom,
|
||||||
|
days: dayTo - dayFrom
|
||||||
|
}
|
||||||
|
travelPeriods.push(element)
|
||||||
|
} else {
|
||||||
|
for (let index = monthFrom; index <= monthTo; index++) {
|
||||||
|
let element = {}
|
||||||
|
if (index === monthFrom) {
|
||||||
|
element = {
|
||||||
|
month: index,
|
||||||
|
days: 32 - dayFrom
|
||||||
|
}
|
||||||
|
} else if (index === monthTo) {
|
||||||
|
element = {
|
||||||
|
month: index,
|
||||||
|
days: dayTo
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
element = {
|
||||||
|
month: index,
|
||||||
|
days: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
travelPeriods.push(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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[0], value[1], minMax)
|
||||||
|
});
|
||||||
|
return period
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
// calculate ratio and transform into target object
|
||||||
|
return {
|
||||||
|
results: transformer.transform(detailScores),
|
||||||
|
debug: {
|
||||||
|
detailScores: detailScores,
|
||||||
|
minMax: minMax
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
package-lock.json
generated
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"requires": true,
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"dependencies": {
|
||||||
|
"httpolyglot": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/httpolyglot/-/httpolyglot-0.1.2.tgz",
|
||||||
|
"integrity": "sha1-5NNH/omEpi9GfUBg31J/GFH2mXs="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||