diff --git a/src/index.js b/src/index.js index bbfefc10f5aad8d68f3ff08bb7f26e2b748dc7ca..4bb82b0d60b18fdb96746c2a024e6247fb70d237 100644 --- a/src/index.js +++ b/src/index.js @@ -5,27 +5,25 @@ const { hydrateAndFilter, errors, cozyClient -} = require('cozy-konnector-libs') +} = require("cozy-konnector-libs"); -const getAccountId = require('./helpers/getAccountId') +const getAccountId = require("./helpers/getAccountId"); -const moment = require('moment') -const rp = require('request-promise') -require('moment-timezone') +const moment = require("moment"); +const rp = require("request-promise"); +require("moment-timezone"); -moment.locale('fr') // set the language -moment.tz.setDefault('Europe/Paris') // set the timezone +moment.locale("fr"); // set the language +moment.tz.setDefault("Europe/Paris"); // set the timezone /*** Connector Constants ***/ -const startDailyDate = moment().subtract(32, 'month') -const startDailyDateString = startDailyDate.format('YYYY-MM-DD') -const startLoadDate = moment() - .subtract(7, 'day') - .format('YYYY-MM-DD') -const startLoadDateString = startLoadDate.format('YYYY-MM-DD') -const endDate = moment() -const endDateString = endDate.format('YYYY-MM-DD') -const baseUrl = 'https://gw.prd.api.enedis.fr' +const startDailyDate = moment().subtract(32, "month"); +const startDailyDateString = startDailyDate.format("YYYY-MM-DD"); +const startLoadDate = moment().subtract(7, "day"); +const startLoadDateString = startLoadDate.format("YYYY-MM-DD"); +const endDate = moment(); +const endDateString = endDate.format("YYYY-MM-DD"); +const baseUrl = "https://gw.prd.api.enedis.fr"; /** * The start function is run by the BaseKonnector instance only when it got all the account @@ -40,67 +38,67 @@ const baseUrl = 'https://gw.prd.api.enedis.fr' * @param {boolean} doRetry - whether we should use the refresh token or not */ async function start(fields, cozyParameters, doRetry = true) { - log('info', 'Starting the enedis konnector') - const accountId = getAccountId() + log("info", "Starting the enedis konnector"); + const accountId = getAccountId(); try { - const { access_token } = fields - let usage_point_id = '' + const { access_token } = fields; + let usage_point_id = ""; if ( this._account && this._account.oauth_callback_results && this._account.oauth_callback_results.usage_points_id ) { const usage_points_id = this._account.oauth_callback_results.usage_points_id.split( - ',' - ) - usage_point_id = usage_points_id[0] + "," + ); + usage_point_id = usage_points_id[0]; } else { - log('error', 'no usage_point_id found') - throw errors.USER_ACTION_NEEDED_OAUTH_OUTDATED + log("error", "no usage_point_id found"); + throw errors.USER_ACTION_NEEDED_OAUTH_OUTDATED; } - log('info', 'Fetching enedis daily data') - const fetchedDailyData = await getDailyData(access_token, usage_point_id) - log('info', 'Process enedis daily data') + log("info", "Fetching enedis daily data"); + const fetchedDailyData = await getDailyData(access_token, usage_point_id); + log("info", "Process enedis daily data"); const processedDailyData = await processData( fetchedDailyData, - 'com.grandlyon.enedis.day', - ['year', 'month', 'day'] - ) - log('info', 'Agregate enedis daily data for month and year') - await agregateMonthAndYearData(processedDailyData) + "com.grandlyon.enedis.day", + ["year", "month", "day"] + ); + log("info", "Agregate enedis daily data for month and year"); + await agregateMonthAndYearData(processedDailyData); - log('info', 'Process enedis load data') - await startLoadDataProcess(access_token, usage_point_id) + log("info", "Process enedis load data"); + await startLoadDataProcess(access_token, usage_point_id); } catch (err) { if (err.statusCode === 403 || err.code === 403) { if (!fields.refresh_token) { - log('info', 'no refresh token found') - throw errors.USER_ACTION_NEEDED_OAUTH_OUTDATED + log("info", "no refresh token found"); + throw errors.USER_ACTION_NEEDED_OAUTH_OUTDATED; } else if (doRetry) { - log('info', 'asking refresh from the stack') - let body + log("info", "asking refresh from the stack"); + let body; try { body = await cozyClient.fetchJSON( - 'POST', + "POST", `/accounts/enedisgrandlyon/${accountId}/refresh` - ) + ); } catch (err) { - log('info', `Error during refresh ${err.message}`) - throw errors.USER_ACTION_NEEDED_OAUTH_OUTDATED + log("info", `Error during refresh ${err.message}`); + throw errors.USER_ACTION_NEEDED_OAUTH_OUTDATED; } - log('info', 'refresh response') - log('info', JSON.stringify(body)) - fields.access_token = body.attributes.oauth.access_token - return start(fields, cozyParameters, false) + log("info", "refresh response"); + log("info", JSON.stringify(body)); + fields.access_token = body.attributes.oauth.access_token; + return start(fields, cozyParameters, false); } - log('error', `Error during authentication: ${err.message}`) - throw errors.VENDOR_DOWN + log("error", `Error during authentication: ${err.message}`); + throw errors.VENDOR_DOWN; } else { - log('error', 'caught an unexpected error') - log('error', err.message) - throw errors.VENDOR_DOWN + log("error", "caught an unexpected error"); + log("error", err.message); + throw errors.VENDOR_DOWN; } } } @@ -111,22 +109,22 @@ async function start(fields, cozyParameters, doRetry = true) { */ async function getDailyData(token, usagePointID) { const dataRequest = { - method: 'GET', + method: "GET", uri: baseUrl + - '/v4/metering_data/daily_consumption?start=' + + "/v4/metering_data/daily_consumption?start=" + startDailyDateString + - '&end=' + + "&end=" + endDateString + - '&usage_point_id=' + + "&usage_point_id=" + usagePointID, headers: { - Accept: 'application/json', - Authorization: 'Bearer ' + token + Accept: "application/json", + Authorization: "Bearer " + token } - } - const response = await rp(dataRequest) - return response + }; + const response = await rp(dataRequest); + return response; } /** @@ -135,27 +133,27 @@ async function getDailyData(token, usagePointID) { * If yes only call once the api */ async function startLoadDataProcess(token, usagePointID) { - log('info', 'Check history') - const isHistory = await isHistoryLoaded('com.grandlyon.enedis.minute') + log("info", "Check history"); + const isHistory = await isHistoryLoaded("com.grandlyon.enedis.minute"); if (isHistory) { - log('info', 'launch process without history') + log("info", "launch process without history"); await launchLoadDataProcess( token, usagePointID, startLoadDateString, endDateString - ) + ); } else { - log('info', 'launch process with history') - const increamentedStartDate = startLoadDate - const incrementedEndDate = endDate + log("info", "launch process with history"); + const increamentedStartDate = startLoadDate; + const incrementedEndDate = endDate; for (var i = 0; i < 4; i++) { await launchLoadDataProcess( token, usagePointID, - increamentedStartDate.subtract(7 * i, 'day').format('YYYY-MM-DD'), - incrementedEndDate.subtract(7 * i, 'day').format('YYYY-MM-DD') - ) + increamentedStartDate.subtract(7 * i, "day").format("YYYY-MM-DD"), + incrementedEndDate.subtract(7 * i, "day").format("YYYY-MM-DD") + ); } } } @@ -169,24 +167,24 @@ async function launchLoadDataProcess( _startLoadDate, _endDate ) { - log('info', 'Fetching enedis load data') + log("info", "Fetching enedis load data"); const fetchedLoadData = await getLoadData( token, usagePointID, _startLoadDate, _endDate - ) + ); if (fetchedLoadData && fetchedLoadData.length > 0) { - log('info', 'Process enedis load data') + log("info", "Process enedis load data"); const processedLoadData = await processData( fetchedLoadData, - 'com.grandlyon.enedis.minute', - ['year', 'month', 'day', 'hour', 'minute'] - ) - log('info', 'Agregate enedis load data for hour') - await agregateHourlyData(processedLoadData) + "com.grandlyon.enedis.minute", + ["year", "month", "day", "hour", "minute"] + ); + log("info", "Agregate enedis load data for hour"); + await agregateHourlyData(processedLoadData); } else { - log('info', 'No consent or data for load curve') + log("info", "No consent or data for load curve"); } } @@ -196,31 +194,31 @@ async function launchLoadDataProcess( */ async function getLoadData(token, usagePointID, _startDate, _endDate) { const dataRequest = { - method: 'GET', + method: "GET", uri: baseUrl + - '/v4/metering_data/consumption_load_curve?start=' + + "/v4/metering_data/consumption_load_curve?start=" + _startDate + - '&end=' + + "&end=" + _endDate + - '&usage_point_id=' + + "&usage_point_id=" + usagePointID, headers: { - Accept: 'application/json', - Authorization: 'Bearer ' + token + Accept: "application/json", + Authorization: "Bearer " + token } - } + }; try { - const response = await rp(dataRequest) - return response + const response = await rp(dataRequest); + return response; } catch (err) { if (err.statusCode === 404 || err.code === 404) { - log('warning', 'caught an 404 error') - log('warning', err.message) - log('warning', err) - return null + log("warning", "caught an 404 error"); + log("warning", err.message); + log("warning", err); + return null; } else { - throw err + throw err; } } } @@ -232,16 +230,16 @@ async function getLoadData(token, usagePointID, _startDate, _endDate) { * Return the list of filtered data */ async function processData(data, doctype, filterKeys) { - const parsedData = JSON.parse(data) - const intervalData = parsedData.meter_reading.interval_reading - const formatedData = await formateData(intervalData, doctype) + const parsedData = JSON.parse(data); + const intervalData = parsedData.meter_reading.interval_reading; + const formatedData = await formateData(intervalData, doctype); // Remove data for existing days into the DB const filteredData = await hydrateAndFilter(formatedData, doctype, { keys: filterKeys - }) + }); // Store new day data - await storeData(filteredData, doctype, filterKeys) - return filteredData + await storeData(filteredData, doctype, filterKeys); + return filteredData; } /** @@ -250,31 +248,31 @@ async function processData(data, doctype, filterKeys) { async function agregateMonthAndYearData(data) { // Sum year and month values into object with year or year-month as keys if (data && data.length > 0) { - let monthData = {} - let yearData = {} + let monthData = {}; + let yearData = {}; data.forEach(element => { - element.year + '-' + element.month in monthData - ? (monthData[element.year + '-' + element.month] += element.load) - : (monthData[element.year + '-' + element.month] = element.load) + element.year + "-" + element.month in monthData + ? (monthData[element.year + "-" + element.month] += element.load) + : (monthData[element.year + "-" + element.month] = element.load); element.year in yearData ? (yearData[element.year] += element.load) - : (yearData[element.year] = element.load) - }) + : (yearData[element.year] = element.load); + }); // Agregation for Month data const agregatedMonthData = await buildAgregatedData( monthData, - 'com.grandlyon.enedis.month' - ) - await storeData(agregatedMonthData, 'com.grandlyon.enedis.month', [ - 'year', - 'month' - ]) + "com.grandlyon.enedis.month" + ); + await storeData(agregatedMonthData, "com.grandlyon.enedis.month", [ + "year", + "month" + ]); // Agregation for Year data const agregatedYearData = await buildAgregatedData( yearData, - 'com.grandlyon.enedis.year' - ) - await storeData(agregatedYearData, 'com.grandlyon.enedis.year', ['year']) + "com.grandlyon.enedis.year" + ); + await storeData(agregatedYearData, "com.grandlyon.enedis.year", ["year"]); } } @@ -284,31 +282,31 @@ async function agregateMonthAndYearData(data) { async function agregateHourlyData(data) { // Sum year and month values into object with year or year-month as keys if (data && data.length > 0) { - let hourData = {} + let hourData = {}; data.forEach(element => { let key = element.year + - '-' + + "-" + element.month + - '-' + + "-" + element.day + - '-' + - element.hour + "-" + + element.hour; key in hourData ? (hourData[key] += element.load) - : (hourData[key] = element.load) - }) + : (hourData[key] = element.load); + }); // Agregation for Month data const agregatedMonthData = await buildAgregatedData( hourData, - 'com.grandlyon.enedis.hour' - ) - await storeData(agregatedMonthData, 'com.grandlyon.enedis.hour', [ - 'year', - 'month', - 'day', - 'hour' - ]) + "com.grandlyon.enedis.hour" + ); + await storeData(agregatedMonthData, "com.grandlyon.enedis.hour", [ + "year", + "month", + "day", + "hour" + ]); } } @@ -316,11 +314,11 @@ async function agregateHourlyData(data) { * Save data in the right doctype db and prevent duplicated keys */ async function storeData(data, doctype, filterKeys) { - log('debug', doctype, 'Store into') + log("debug", doctype, "Store into"); const filteredDocuments = await hydrateAndFilter(data, doctype, { keys: filterKeys - }) - return await addData(filteredDocuments, doctype) + }); + return await addData(filteredDocuments, doctype); } /** @@ -328,27 +326,27 @@ async function storeData(data, doctype, filterKeys) { * Remove bad data */ async function formateData(data, doctype) { - log('info', 'Formating data') + log("info", "Formating data"); return data.map(record => { - let date = moment(record.date, 'YYYY/MM/DD h:mm:ss') + let date = moment(record.date, "YYYY/MM/DD h:mm:ss"); if (record.value != -2) { const load = - doctype === 'com.grandlyon.enedis.minute' + doctype === "com.grandlyon.enedis.minute" ? record.value / 2 - : record.value - if (doctype === 'com.grandlyon.enedis.minute') { - date = date.subtract(30, 'minute') + : record.value; + if (doctype === "com.grandlyon.enedis.minute") { + date = date.subtract(30, "minute"); } return { load: parseFloat(load / 1000), - year: parseInt(date.format('YYYY')), - month: parseInt(date.format('M')), - day: parseInt(date.format('D')), - hour: parseInt(date.format('H')), - minute: parseInt(date.format('m')) - } + year: parseInt(date.format("YYYY")), + month: parseInt(date.format("M")), + day: parseInt(date.format("D")), + hour: parseInt(date.format("H")), + minute: parseInt(date.format("m")) + }; } - }) + }); } /** @@ -356,14 +354,14 @@ async function formateData(data, doctype) { * Return an Array of agregated data */ async function buildAgregatedData(data, doctype) { - let agregatedData = [] + let agregatedData = []; for (let [key, value] of Object.entries(data)) { - const data = await buildDataFromKey(doctype, key, value) - const oldValue = await resetInProgressAggregatedData(data, doctype) - data.load += oldValue - agregatedData.push(data) + const data = await buildDataFromKey(doctype, key, value); + const oldValue = await resetInProgressAggregatedData(data, doctype); + data.load += oldValue; + agregatedData.push(data); } - return agregatedData + return agregatedData; } /** @@ -373,24 +371,24 @@ async function buildAgregatedData(data, doctype) { * For month doctype: key = "YYYY-MM" */ async function buildDataFromKey(doctype, key, value) { - let year, month, day, hour - if (doctype === 'com.grandlyon.enedis.year') { - year = key - month = 1 - day = 0 - hour = 0 - } else if (doctype === 'com.grandlyon.enedis.month') { - const split = key.split('-') - year = split[0] - month = split[1] - day = 0 - hour = 0 + let year, month, day, hour; + if (doctype === "com.grandlyon.enedis.year") { + year = key; + month = 1; + day = 0; + hour = 0; + } else if (doctype === "com.grandlyon.enedis.month") { + const split = key.split("-"); + year = split[0]; + month = split[1]; + day = 0; + hour = 0; } else { - const split = key.split('-') - year = split[0] - month = split[1] - day = split[2] - hour = split[3] + const split = key.split("-"); + year = split[0]; + month = split[1]; + day = split[2]; + hour = split[3]; } return { load: Math.round(value * 10000) / 10000, @@ -399,30 +397,30 @@ async function buildDataFromKey(doctype, key, value) { day: parseInt(day), hour: parseInt(hour), minute: 0 - } + }; } /** * Function checking if the history is loaded */ async function isHistoryLoaded(doctype) { - log('debug', doctype, 'Retrieve data') - const result = await cozyClient.data.findAll(doctype) + log("debug", doctype, "Retrieve data"); + const result = await cozyClient.data.findAll(doctype); if (result && result.length > 0) { const filtered = result.filter(function(el) { return ( el.year <= startLoadDate.year && el.month <= startLoadDate.month && el.day <= startLoadDate.day - ) - }) + ); + }); if (filtered.length > 0) { - return false + return false; } else { - return true + return true; } } - return false + return false; } /** @@ -434,21 +432,21 @@ async function isHistoryLoaded(doctype) { */ async function resetInProgressAggregatedData(data, 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) + log("debug", doctype, "Remove aggregated data for"); + const result = await cozyClient.data.findAll(doctype); if (result && result.length > 0) { // Filter data to remove - var filtered = [] - if (doctype === 'com.grandlyon.enedis.year') { + var filtered = []; + if (doctype === "com.grandlyon.enedis.year") { // Yearly case filtered = result.filter(function(el) { - return el.year == data.year - }) - } else if (doctype === 'com.grandlyon.enedis.month') { + return el.year == data.year; + }); + } else if (doctype === "com.grandlyon.enedis.month") { // Monthly case filtered = result.filter(function(el) { - return el.year == data.year && el.month == data.month - }) + return el.year == data.year && el.month == data.month; + }); } else { // Hourly case filtered = result.filter(function(el) { @@ -457,19 +455,19 @@ async function resetInProgressAggregatedData(data, doctype) { el.month == data.month && el.day == data.day && el.hour == data.hour - ) - }) + ); + }); } // Remove data - let sum = 0.0 + let sum = 0.0; for (const doc of filtered) { - sum += doc.load - log('debug', doc, 'Removing this entry for ' + doctype) - await cozyClient.data.delete(doctype, doc) + sum += doc.load; + log("debug", doc, "Removing this entry for " + doctype); + await cozyClient.data.delete(doctype, doc); } - return sum + return sum; } - return 0.0 + return 0.0; } -module.exports = new BaseKonnector(start) +module.exports = new BaseKonnector(start);