From 06236e881ae3a0a0186f1438eeb524937f2823c6 Mon Sep 17 00:00:00 2001 From: Timo Volkmann Date: Sun, 14 Jun 2020 01:14:04 +0200 Subject: [PATCH] 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