Newer
Older
const soapRequest = require('easy-soap-request')
const moment = require('moment')
require('moment-timezone')
const xml2js = require('xml2js')
const { buildAgregatedData } = require('./aggregate')
const {
parseSgeXmlData,
parseSgeXmlTechnicalData,
formateDataForDoctype,
parseTags,
parseValue,
} = require('./parsing')
userMesureDetailles,
searchUser,
activateDataCollect,
stopDataCollect,
getInseeCode,
updateBoConsent,
createBoConsent,
getBoConsent,
deleteBoConsent,
moment.locale('fr') // set the language
moment.tz.setDefault('Europe/Paris') // set the timezone
/*** Connector Constants ***/
const manualExecution =
process.env.COZY_JOB_MANUAL_EXECUTION === 'true' ? true : false
? moment().subtract(12, 'month')
: moment().subtract(6, 'month')
let startDailyDateString = startDailyDate.format('YYYY-MM-DD')
const startLoadDate = moment().subtract(7, 'day')
const endDate = moment()
const endDateString = endDate.format('YYYY-MM-DD')
module.exports = new BaseKonnector(start)
// 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
// cozyParameters are static parameters, independents from the account. Most often, it can be a
// secret api key.
async function start(fields, cozyParameters) {
log('info', 'Gathering data ...')
let baseUrl = fields.wso2BaseUrl
let apiAuthKey = fields.apiToken
let loginUtilisateur = fields.loginUtilisateur
//TODO: Verify if condition is working in local and on build version
if (cozyParameters && Object.keys(cozyParameters).length !== 0) {
log('debug', 'Found COZY_PARAMETERS')
baseUrl = cozyParameters.secret.wso2BaseUrl
apiAuthKey = cozyParameters.secret.apiToken
loginUtilisateur = cozyParameters.secret.loginUtilisateur
}
/**
* If it's first start we have to do the following operations:
* - verify pdl are matching
* - BO: create backoffice consent
* - get contract start date and store it
* - activate half-hour
* - BO: update consent with service ID
*/
log('info', 'User Logging...')
if (await isFirstStart()) {
if (!(await verifyUserIdentity(fields))) {
throw errors.LOGIN_FAILED
}
await createBoConsent()
//TODO: remove because useless ? Done later in code
// const startDate = await getDataStartDate(
// baseUrl,
// apiAuthKey,
// loginUtilisateur,
// fields.pointId
// )
await activateDataCollect()
await updateBoConsent()
} else {
await getBoConsent()
if (!(await verifyUserIdentity(fields))) {
await deleteBoConsent()
await stopDataCollect()
throw errors.TERMS_VERSION_MISMATCH
}
}
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
await gatherData(baseUrl, apiAuthKey, loginUtilisateur, fields.pointId)
}
/**
* Verify user identity
* @param {object} fields
*/
async function verifyUserIdentity(fields) {
const inseeCode = getInseeCode(fields.postalCode)
const user = await findUser(
fields.name,
fields.addresse,
fields.postalCode,
inseeCode
)
if (fields.pointId !== user.pointId) {
log('error', 'PointId does not match')
return false
}
return true
}
/**
* Main method for gathering data
* @param {string} baseUrl
* @param {string} apiAuthKey
* @param {string} loginUtilisateur
* @param {number} pointId
*/
async function gatherData(baseUrl, apiAuthKey, loginUtilisateur, pointId) {
await getDataStartDate(
`${baseUrl}/enedis_SGE_ConsultationDonneesTechniquesContractuelles/1.0`,
apiAuthKey,
loginUtilisateur,
await getData(
`${baseUrl}/enedis_SGE_ConsultationMesuresDetaillees/1.0`,
apiAuthKey,
loginUtilisateur,
await getMaxPowerData(
`${baseUrl}/enedis_SGE_ConsultationMesuresDetaillees/1.0`,
apiAuthKey,
loginUtilisateur,
await getDataHalfHour(
`${baseUrl}/enedis_SGE_ConsultationMesuresDetaillees/1.0`,
apiAuthKey,
loginUtilisateur,
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/**
*
* @param {string} url
* @param {string} apiAuthKey
* @param {string} userLogin
* @param {number} pointId
*/
async function getDataStartDate(url, apiAuthKey, userLogin, pointId) {
log('info', 'Fetching data start date')
const sampleHeaders = {
'Content-Type': 'text/xml;charset=UTF-8',
apikey: apiAuthKey,
}
const { response } = await soapRequest({
url: url,
headers: sampleHeaders,
xml: userTechnicalData(pointId, userLogin),
}).catch(err => {
log('error', 'technicalDataResponse')
log('error', err)
return err
})
xml2js.parseString(
response.body,
{
tagNameProcessors: [parseTags],
valueProcessors: [parseValue],
explicitArray: false,
},
processStartDate()
)
}
* @param {string} url
* @param {string} apiAuthKey
* @param {string} userLogin
* @param {number} pointId
*/
async function getData(url, apiAuthKey, userLogin, pointId) {
log('info', 'Fetching data')
const sampleHeaders = {
'Content-Type': 'text/xml;charset=UTF-8',
apikey: apiAuthKey,
}
const { response } = await soapRequest({
url: url,
headers: sampleHeaders,
xml: userMesureDetailles(
pointId,
userLogin,
startDailyDateString,
endDateString
),
}).catch(err => {
tagNameProcessors: [parseTags],
valueProcessors: [parseValue],
explicitArray: false,
/**
* Get Max power data
* @param {string} url
* @param {string} apiAuthKey
* @param {string} userLogin
* @param {number} pointId
*/
async function getMaxPowerData(url, apiAuthKey, userLogin, pointId) {
log('info', 'Fetching Max Power data')
const sampleHeaders = {
'Content-Type': 'text/xml;charset=UTF-8',
apikey: apiAuthKey,
}
const { response } = await soapRequest({
url: url,
headers: sampleHeaders,
xml: userMaxPower(pointId, userLogin, startDailyDateString, endDateString),
}).catch(err => {
log('error', 'getMaxPowerData')
log('error', err)
return err
})
xml2js.parseString(
response.body,
{
tagNameProcessors: [parseTags],
valueProcessors: [parseValue],
explicitArray: false,
},
processData('com.grandlyon.enedis.maxpower')
)
}
/**
* If start date exceed the maximum amount of data we can get with one query
* get only 36 month
*/
function setStartDate() {
if (moment(endDate).diff(startDailyDate, 'months', true) > 36) {
log(
'info',
'Start date exceed 36 month, setting start date to current date minus 36 month'
)
startDailyDate = moment(endDate).subtract(36, 'month')
startDailyDateString = startDailyDate.format('YYYY-MM-DD')
}
}
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
/**
* Get half-hour data
* @param {string} url
* @param {string} apiAuthKey
* @param {string} userLogin
* @param {number} pointId
*/
async function getDataHalfHour(url, apiAuthKey, userLogin, pointId) {
log('info', 'Fetching data')
const sampleHeaders = {
'Content-Type': 'text/xml;charset=UTF-8',
apikey: apiAuthKey,
}
let MAX_HISTO = 4
// If manual execution, retrieve only 1 week
if (manualExecution) {
MAX_HISTO = 1
}
for (var i = 0; i < MAX_HISTO; i++) {
log('info', 'launch process with history')
const increamentedStartDateString = moment(startLoadDate)
.subtract(7 * i, 'day')
.format('YYYY-MM-DD')
const incrementedEndDateString = moment(endDate)
.subtract(7 * i, 'day')
.format('YYYY-MM-DD')
const { response } = await soapRequest({
url: url,
headers: sampleHeaders,
xml: userMesureDetailles(
pointId,
userLogin,
increamentedStartDateString,
incrementedEndDateString,
'COURBE',
'PA'
),
}).catch(err => {
log('error', 'userMesureDetailles half-hour')
log('error', err)
return err
})
xml2js.parseString(
response.body,
{
tagNameProcessors: [parseTags],
valueProcessors: [parseValue],
explicitArray: false,
},
processData('com.grandlyon.enedis.minute')
)
}
}
function processData(doctype = 'com.grandlyon.enedis.day') {
return async (err, result) => {
if (err) {
log('error', err)
throw err
}
// Return only needed part of info
const data = parseSgeXmlData(result)
doctype,
['year', 'month', 'day', 'hour', 'minute']
log('info', 'Agregate enedis daily data for month and year')
if (doctype === 'com.grandlyon.enedis.day') {
await agregateMonthAndYearData(processedDailyData)
}
*/
function processStartDate() {
return async (err, result) => {
if (err) {
log('error', err)
throw err
}
// update start Date with contract openning date
try {
startDailyDate = moment(parseSgeXmlTechnicalData(result), 'YYYY-MM-DD')
startDailyDateString = startDailyDate.format('YYYY-MM-DD')
} catch (err) {
log('error', err)
//TODO: custom error ?
throw err
}
/**
* Save data in the right doctype db and prevent duplicated keys
* @param {EnedisKonnectorData[]} data
* @param {string} doctype
* @param {string[]} filterKeys
*/
async function storeData(data, doctype, filterKeys) {
log('debug', doctype, 'Store into')
const filteredDocuments = await hydrateAndFilter(data, doctype, {
keys: filterKeys,
})
await addData(filteredDocuments, doctype)
return filteredDocuments
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
/**
* Agregate data from daily data to monthly and yearly data
*/
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 = {}
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 in yearData
? (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',
])
// Agregation for Year data
const agregatedYearData = await buildAgregatedData(
yearData,
'com.grandlyon.enedis.year'
)
await storeData(agregatedYearData, 'com.grandlyon.enedis.year', ['year'])
}
}
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
/**
* @returns {boolean}
*/
function isFirstStart() {
//TODO: Implement
return false
}
/**
* @return {User}
*/
async function findUser(
url,
apiAuthKey,
appLogin,
name,
addresse,
postalCode,
inseeCode
) {
log('info', 'Fetching user data')
const sampleHeaders = {
'Content-Type': 'text/xml;charset=UTF-8',
apikey: apiAuthKey,
}
const { response } = await soapRequest({
url: url,
headers: sampleHeaders,
xml: searchUser(appLogin, name, addresse, postalCode, inseeCode),
}).catch(err => {
log('error', 'rechercherPointResponse')
log('error', err)
throw errors.LOGIN_FAILED
//TODO: handling code error SGT4F6 and SGT432 into USER_ACTIon_NEEDED
})
//TODO: handle reply
xml2js.parseString(
response.body,
{
tagNameProcessors: [parseTags],
valueProcessors: [parseValue],
explicitArray: false,
},
processStartDate()
)
}