Newer
Older
const {
BaseKonnector,
log,
errors,
addData,
hydrateAndFilter,
const moment = require('moment')
require('moment-timezone')
moment.locale('fr') // set the language
moment.tz.setDefault('Europe/Paris') // set the timezone
const Sentry = require('@sentry/node')
// eslint-disable-next-line
const Tracing = require('@sentry/tracing') // Needed for tracking performance in Sentry
const { version } = require('../package.json')
const { isDev } = require('./helpers/env')
process.env.COZY_JOB_MANUAL_EXECUTION === 'true' ? true : false
doctype: 'com.grandlyon.egl.day',
keys: ['year', 'month', 'day'],
doctype: 'com.grandlyon.egl.month',
keys: ['year', 'month'],
doctype: 'com.grandlyon.egl.year',
keys: ['year'],
},
}
module.exports = new BaseKonnector(start)
/**
* Sentry
*/
Sentry.init({
dsn:
'https://3f97baf46c2b44c2bd9e0c371abe3e05@grandlyon.errors.cozycloud.cc/2',
// Set tracesSampleRate to 1.0 to capture 100%
// of transactions for performance monitoring.
// We recommend adjusting this value in production
tracesSampleRate: 1.0,
release: version,
environment: isDev() ? 'development' : 'production',
debug: isDev(),
integrations: [
// enable HTTP calls tracing
new Sentry.Integrations.Http({ tracing: true }),
],
})
// The start function is run by the BaseKonnector instance only when it got all the account
// information (fields). When you run this connector yourself in 'standalone' mode or 'dev' mode,
// the account information come from ./konnector-dev-config.json file
async function start(fields, cozyParameters) {
const transaction = Sentry.startTransaction({
op: 'konnector',
name: 'EGL Konnector',
})
transaction.startChild({ op: 'Konnector starting' })
// Local debug data
// const baseUrl = fields.eglBaseURL
// const apiAuthKey = fields.eglAPIAuthKey
const baseUrl = cozyParameters.secret.eglBaseURL
const apiAuthKey = cozyParameters.secret.eglAPIAuthKey
log('info', 'Authenticating ...')
const response = await authenticate(
fields.login,
fields.password,
baseUrl,
apiAuthKey
const eglData = await getData(response, baseUrl, apiAuthKey)
const processedLoadData = await processData(
eglData,
rangeDate.day.doctype,
rangeDate.day.keys
)
log('debug', 'Aggregate egl load data for month and year')
await aggregateMonthAndYearData(processedLoadData)
transaction.setStatus(Tracing.SpanStatus.Ok)
transaction.finish()
log('error', error)
Sentry.captureException(error)
transaction.setStatus(Tracing.SpanStatus.Aborted)
transaction.finish()
await Sentry.flush()
throw error
/**
* Parse data
* Remove existing data from DB using hydrateAndFilter
* Store filtered data
* Return the list of filtered data
*/
async function processData(data, doctype, filterKeys) {
// Remove data for existing days into the DB
const filteredData = await hydrateAndFilter(data, doctype, {
keys: filterKeys,
})
log('debug', 'processData - data filtered')
await storeData(filteredData, doctype, filterKeys)
return filteredData
* Aggregate data from daily data to monthly and yearly data
// Sum year and month values into object with year or year-month as keys
if (data && data.length !== 0) {
const monthDataKey = element.year + '-' + element.month
if (monthDataKey in monthData) {
monthData[monthDataKey] += element.load
} else {
monthData[monthDataKey] = element.load
}
const yearDataKey = element.year
if (yearDataKey in yearData) {
yearData[yearDataKey] += element.load
} else {
yearData[yearDataKey] = element.load
}
})
// Aggregation for Month data
const aggregatedMonthData = await buildAggregatedData(
'com.grandlyon.egl.month'
)
await storeData(aggregatedMonthData, 'com.grandlyon.egl.month', [
'year',
'month',
])
// Aggregation for Year data
const aggregatedYearData = await buildAggregatedData(
'com.grandlyon.egl.year'
)
await storeData(aggregatedYearData, 'com.grandlyon.egl.year', ['year'])
async function buildAggregatedData(data, doctype) {
log('info', 'entering buildAggregatedData')
let aggregatedData = []
const data = await buildDataFromKey(doctype, key, value)
const oldValue = await resetInProgressAggregatedData(data, doctype)
log('info', 'Data load + old value is ' + data.load + ' + ' + oldValue)
data.load += oldValue
aggregatedData.push(data)
async function authenticate(login, password, baseUrl, apiAuthKey) {
const authRequest = {
method: 'post',
url: baseUrl + '/connect.aspx',
'Content-Type': 'application/x-www-form-urlencoded',
const resp = await axios(authRequest)
if (resp.data.codeRetour === 100) {
return resp.data
Sentry.captureException(JSON.stringify(resp.data))
Sentry.captureException(JSON.stringify(error))
}
}
async function getData(response, baseUrl, apiAuthKey) {
log('debug', 'Start date : ' + startDate)
log('debug', 'End date : ' + endDate)
method: 'post',
url: baseUrl + '/getAllAgregatsByAbonnement.aspx',
'Content-Type': 'application/x-www-form-urlencoded',
token: response.resultatRetour.token,
num_abt: response.resultatRetour.num_abt,
date_debut: startDate,
// Sort data by date
const resp = await axios(dataRequest)
resp.data.resultatRetour.sort(function(a, b) {
return new Date(a.DateReleve) - new Date(b.DateReleve)
log('debug', 'Error from getAllAgregatsByAbonnement')
throw new Error(errors.VENDOR_DOWN)
log('info', 'origin response size is : ' + response.resultatRetour.length)
// Store first value as reference for index processing
// Create copy of data without first value
.filter(value => value.ValeurIndex)
log('info', 'filtered size is : ' + data.length)
return data.map(value => {
const time = moment(value.DateReleve, moment.ISO_8601)
const processedLoad = value.ValeurIndex - refValue.ValeurIndex
if (processedLoad < 0) {
'error',
`processing load for day ${parseInt(time.format('D'))}/${parseInt(
time.format('M')
)}/${parseInt(time.format('YYYY'))}, value is : ${processedLoad}`
)
throw errors.VENDOR_DOWN
}
// Change index ref value
load: processedLoad,
year: parseInt(time.format('YYYY')),
month: parseInt(time.format('M')),
day: parseInt(time.format('D')),
hour: 0,
minute: 0,
/**
* Save data in the right doctype db and prevent duplicated keys
*/
async function storeData(data, doctype, filterKeys) {
log('debug', 'Store into ' + doctype)
log('debug', 'Store into keys : ' + filterKeys)
// log('info', 'Saving data ' + v.load + ' for ' + v.day + '/' + v.month + '/' + v.year)
// })
const filteredDocuments = await hydrateAndFilter(data, doctype, {
keys: filterKeys,
})
return await addData(filteredDocuments, doctype)
* For year doctype: key = 'YYYY'
* For month doctype: key = 'YYYY-MM'
let year, month, day, hour
if (doctype === 'com.grandlyon.egl.year') {
year = key
month = 1
day = 0
hour = 0
} else if (doctype === 'com.grandlyon.egl.month') {
const split = key.split('-')
year = split[0]
month = split[1]
day = 0
hour = 0
const split = key.split('-')
year = split[0]
month = split[1]
day = split[2]
hour = split[3]
}
return {
load: Math.round(value * 10000) / 10000,
year: parseInt(year),
month: parseInt(month),
day: parseInt(day),
hour: parseInt(hour),
/**
* Function handling special case.
* The temporary aggregated data need to be remove in order for the most recent one te be saved.
* { load: 76.712, month: 2020, ... } need to be replace by
// /!\ Warning: cannot use mongo queries because not supported for dev by cozy-konnectors-libs
log('debug', 'Remove aggregated data for ' + doctype)
const result = await cozyClient.data.findAll(doctype)
let filtered = []
if (doctype === 'com.grandlyon.egl.year') {
// Yearly case
filtered = result.filter(function(el) {
return el.year == data.year
})
} else if (doctype === 'com.grandlyon.egl.month') {
return el.year == data.year && el.month == data.month
})
filtered = result.filter(function(el) {
return (
el.year == data.year &&
el.month == data.month &&
el.day == data.day &&
el.hour == data.hour
sum += doc.load
log('debug', 'Removing this entry for ' + doc.load)
await cozyClient.data.delete(doctype, doc)