From 380c7792f73c9258b7dd501591d52136c3a39721 Mon Sep 17 00:00:00 2001 From: Yoan VALLET <ext.sopra.yvallet@grandlyon.com> Date: Mon, 25 May 2020 18:02:41 +0200 Subject: [PATCH] change time format and add data aggregation --- manifest.konnector | 10 ++- src/index.js | 168 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 158 insertions(+), 20 deletions(-) diff --git a/manifest.konnector b/manifest.konnector index ef0316e..4c16279 100644 --- a/manifest.konnector +++ b/manifest.konnector @@ -27,8 +27,14 @@ "data_types": [], "screenshots": [], "permissions": { - "load profile": { - "type": "egl.loadprofile" + "daily eau du grand lyon data": { + "type": "io.egl.day" + }, + "monthly eau du grand lyon data": { + "type": "io.egl.month" + }, + "yearly eau du grand lyon data": { + "type": "io.egl.year" }, "files": { "type": "io.cozy.files" diff --git a/src/index.js b/src/index.js index 3a4cfff..1bad305 100644 --- a/src/index.js +++ b/src/index.js @@ -14,9 +14,26 @@ moment.locale('fr') // set the language moment.tz.setDefault('Europe/Paris') // set the timezone const startDate = moment() - .subtract(10, 'day') + .startOf('year') + .subtract(3, 'year') + .subtract(1, 'day') .format('MM/DD/YYYY') const endDate = moment().format('MM/DD/YYYY') +const timeRange = ['day', 'month', 'year'] +const rangeDate = { + day: { + doctype: 'io.egl.day', + keys: ['year', 'month', 'day'] + }, + month: { + doctype: 'io.egl.month', + keys: ['year', 'month'] + }, + year: { + doctype: 'io.egl.year', + keys: ['year'] + } +} module.exports = new BaseKonnector(start) @@ -29,6 +46,7 @@ async function start(fields, cozyParameters) { // await resetData() const baseUrl = cozyParameters.secret.eglBaseURL const apiAuthKey = cozyParameters.secret.eglAPIAuthKey + log('debug', fields, 'Fields') log('info', 'Authenticating ...') const response = await authenticate( @@ -38,14 +56,40 @@ async function start(fields, cozyParameters) { apiAuthKey ) log('info', 'Successfully logged in') - log('info', 'Getting data') - const loadProfile = await getData(response, baseUrl, apiAuthKey) - log('info', 'Saving data to Cozy') - storeLoadProfile(loadProfile) + Promise.all( + timeRange.map(timeStep => + processData(timeStep, response, baseUrl, apiAuthKey) + ) + ) } catch (error) { throw new Error(error.message) } } +async function processData(timeStep, response, baseUrl, apiAuthKey) { + const doctype = rangeDate[timeStep].doctype + log('info', 'Getting data') + const loadProfile = await getData(response, baseUrl, apiAuthKey) + log('info', 'Saving data to Cozy') + if (doctype === rangeDate.day.doctype) { + log('info', 'Saving daily data') + await storeData(loadProfile, rangeDate.day.doctype, rangeDate.day.keys) + } else if (doctype === rangeDate.month.doctype) { + log('info', 'Saving monthly data') + await resetInProgressAggregatedData(rangeDate.month.doctype) + const monthlyData = processMonthlyAggregation(loadProfile, rangeDate.month) + await storeData(monthlyData, rangeDate.month.doctype, rangeDate.month.keys) + } else if (doctype === rangeDate.year.doctype) { + log('info', 'Saving yearly data') + await resetInProgressAggregatedData(rangeDate.year.doctype) + const yearlyData = processYearAggregation( + loadProfile, + rangeDate.year.doctype + ) + await storeData(yearlyData, rangeDate.year.doctype, rangeDate.year.keys) + } else { + throw new Error('Unkonw range type: ' + doctype) + } +} async function authenticate(login, password, baseUrl, apiAuthKey) { const authRequest = { @@ -74,6 +118,8 @@ async function authenticate(login, password, baseUrl, apiAuthKey) { } async function getData(response, baseUrl, apiAuthKey) { + log('debug', startDate, 'Start date') + log('debug', endDate, 'End date') const dataRequest = { method: 'POST', uri: baseUrl + '/getAllAgregatsByAbonnement.aspx', @@ -108,31 +154,117 @@ async function getData(response, baseUrl, apiAuthKey) { function format(response) { const data = response.resultatRetour.slice(1).map((value, index) => { + const time = moment(value.DateReleve, moment.ISO_8601) return { - time: moment(value.DateReleve, moment.ISO_8601).format('YYYY-MM-DD'), load: value.ValeurIndex - response.resultatRetour[index].ValeurIndex, + year: parseInt(time.format('YYYY')), + month: parseInt(time.format('M')), + day: parseInt(time.format('D')), + hour: 0, + minute: 0, type: value.TypeAgregat } }) return data } -function storeLoadProfile(loadProfile) { - return hydrateAndFilter(loadProfile, 'egl.loadprofile', { - keys: ['time'] +function processYearAggregation(data, doctype) { + log('info', 'Start aggregation for : ' + doctype) + const grouped = data.reduce(reduceYearFunction, {}) + return Object.values(grouped) +} + +function processMonthlyAggregation(data, range) { + log('info', 'Start aggregation for : ' + range.doctype) + // Filter by year + const tmpData = groupBy(data, 'year') + const keys = Object.keys(tmpData) + var dataToStore = [] + // Monthly aggregation + for (const index in keys) { + // Get daily data of a year + var monthlyData = tmpData[keys[index]] + // Monthly aggregation + var aggregatedData = monthlyData.reduce(reduceMonthFunction, {}) + // Store it + dataToStore = dataToStore.concat(Object.values(aggregatedData)) + } + return dataToStore +} + +function groupBy(xs, key) { + return xs.reduce(function(rv, x) { + ;(rv[x[key]] = rv[x[key]] || []).push(x) + return rv + }, {}) +} + +function reduceYearFunction(acc, x) { + var id = acc[x.year] + if (id) { + id.load += x.load + } else { + acc[x.year] = x + } + return acc +} + +function reduceMonthFunction(acc, x) { + var id = acc[x.month] + if (id) { + id.load += x.load + } else { + acc[x.month] = x + } + return acc +} + +async function storeData(data, doctype, keys) { + log('debug', 'Store into ' + doctype) + log('debug', keys, 'Store into keys ') + return hydrateAndFilter(data, doctype, { + keys: keys }).then(filteredDocuments => { - addData(filteredDocuments, 'egl.loadprofile') + addData(filteredDocuments, doctype) }) } -// eslint-disable-next-line no-unused-vars -async function resetData() { - const result = await cozyClient.data.findAll('egl.loadprofile') - if (result.error) { +/** + * Function handling special case. + * The temporary aggregated data need to be remove in order for the most recent one te be saved. + * ex for io.egl.month : + * { load: 76.712, month: 2020, ... } need to be replace by + * { load: 82.212, month: 2020, ... } after grdf data reprocess + */ +async function resetInProgressAggregatedData(doctype) { + // /!\ Warning: cannot use mongo queries because not supported for dev by cozy-konnectors-libs + log('debug', doctype, 'Remove aggregated data for') + const result = await cozyClient.data.findAll(doctype) + if (result.error || result.length <= 0) { // eslint-disable-next-line no-console - console.error('Error while fetching loads') - } - for (const load of result) { - await cozyClient.data.delete('egl.loadprofile', load) + console.warn('Error while fetching loads, doctype not found ') + } else { + const currentDate = moment() + // Filter data to remove + var filtered = [] + if (doctype === rangeDate.year.doctype) { + // Yearly case + filtered = result.filter(function(el) { + return el.year == currentDate.year() + }) + } else { + // Monthly case + filtered = result.filter(function(el) { + return ( + el.year == currentDate.year() && + el.month == parseInt(moment().format('M')) + ) + }) + } + // Remove data + for (const doc of filtered) { + log('debug', doc, 'Removing this entry for ' + doctype) + await cozyClient.data.delete(doctype, doc) + } } } -- GitLab