From e56634409805ca896d9bfb3f6b5bf096ee516ec7 Mon Sep 17 00:00:00 2001 From: Timo Volkmann Date: Fri, 12 Jun 2020 18:43:09 +0200 Subject: [PATCH] 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