travopti/backend/util/scoreAndSearch.js

133 lines
4.4 KiB
JavaScript

const _ = require('lodash')
const moment = require("moment")
const getClimateMinMax = require("./getClimateMinMax.js")
const oldToNewQuerySyntax = require("./oldToNewQuerySyntax.js")
const getAllRegionsWithClimatePerMonth = require('./getAllRegionsWithClimatePerMonth')
const score = require('./score')
const transformer = require('./transformer')
const MULTIPLIER = {
temperature_mean_max: 5,
percipitation: 3.5,
raindays: 3,
sunhours: 2.5,
}
module.exports = function (dbConn) {
return async function (from, to, queries) {
console.log('search')
// get Min and Max values for each Parameter
const minMax = await getClimateMinMax.getClimateMinMax(dbConn)
// randomize if query contains randomize=true
if (queries.randomize) {
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}`
delete queries.randomize
}
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(dbConn)(period.month)
period.scores = {}
Object.entries(queries).forEach(([key, value]) => {
// console.log('key',key)
// console.log('val', value)
period.scores[key] = calculateScoresWrapper(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
}
}
}
function calculateScoresWrapper(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
}
}