Skip to content
Snippets Groups Projects
index.js 8.58 KiB
Newer Older
// @ts-check
Hugo SUBTIL's avatar
Hugo SUBTIL committed
const {
  BaseKonnector,
  log,
  hydrateAndFilter,
  addData,
Hugo SUBTIL's avatar
Hugo SUBTIL committed
} = require('cozy-konnector-libs')
const soapRequest = require('easy-soap-request')
const moment = require('moment')
require('moment-timezone')
const xml2js = require('xml2js')
moment.locale('fr') // set the language
moment.tz.setDefault('Europe/Paris') // set the timezone
Hugo SUBTIL's avatar
Hugo SUBTIL committed

/*** Connector Constants ***/
const manualExecution =
  process.env.COZY_JOB_MANUAL_EXECUTION === 'true' ? true : false
Hugo SUBTIL's avatar
Hugo SUBTIL committed
let startDailyDate = manualExecution
  ? moment().subtract(12, 'month')
  : moment().subtract(6, 'month')
Hugo SUBTIL's avatar
Hugo SUBTIL committed
let 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')
Hugo SUBTIL's avatar
Hugo SUBTIL committed

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
Hugo SUBTIL's avatar
Hugo SUBTIL committed
  log('info', 'Authenticating ...')
  if (cozyParameters && Object.keys(cozyParameters).length !== 0) {
    log('debug', 'Found COZY_PARAMETERS')
    baseUrl = cozyParameters.secret.wso2BaseUrl
    apiAuthKey = cozyParameters.secret.apiToken
    loginUtilisateur = cozyParameters.secret.loginUtilisateur
  }
  //TODO: authentification ?
Hugo SUBTIL's avatar
Hugo SUBTIL committed
  log('info', 'Successfully logged in')

  //TODO: get compteur start data
Hugo SUBTIL's avatar
Hugo SUBTIL committed

  await getDataStartDate(
    `${baseUrl}/enedis_SGE_ConsultationDonneesTechniquesContractuelles/1.0`,
    apiAuthKey,
    loginUtilisateur,
    fields.pointId
  )
  await getData(
    `${baseUrl}/enedis_SGE_ConsultationMesuresDetaillees/1.0`,
    apiAuthKey,
    loginUtilisateur,
    fields.pointId
  )
  log('info', 'Konnector process end')
Hugo SUBTIL's avatar
Hugo SUBTIL committed
}
Hugo SUBTIL's avatar
Hugo SUBTIL committed
/**
 *
 * @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()
  )
}
Hugo SUBTIL's avatar
Hugo SUBTIL committed

/**
 *
 * @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 => {
Hugo SUBTIL's avatar
Hugo SUBTIL committed
    log('error', 'userMesureDetailles')
    log('error', err)
    return err
  xml2js.parseString(
    response.body,
Hugo SUBTIL's avatar
Hugo SUBTIL committed
    {
      tagNameProcessors: [parseTags],
      valueProcessors: [parseValue],
      explicitArray: false,
Hugo SUBTIL's avatar
Hugo SUBTIL committed
    },
    processData()
/**
 * Format tag in order to be manipulated easly
 * @param {string} name
 * @returns {string} name
 */
function parseTags(name) {
  if (name.split(':')[1] !== undefined) {
    return name.split(':')[1]
  }
  return name
}

/**
 *
 * @param {string} value
 * @param {string} name
 * @returns {string|number} value
 */
function parseValue(value, name) {
  // Wh => KWh
  if (name === 'v') {
    return parseFloat((parseInt(value) / 1000).toFixed(2))
  }
  return value
}

/**
 * Parse data
 */
function processData() {
  return async (err, result) => {
    if (err) {
      log('error', err)
      throw err
    }
    // Return only needed part of info
    const data = parseSgeXmlData(result)
    return storeData(
      await formateDataForDoctype(data),
      'com.grandlyon.enedis.day',
      ['year', 'month', 'day']
    )
  }
}

Hugo SUBTIL's avatar
Hugo SUBTIL committed
/**
 * Parse data
 */
function processStartDate() {
  return async (err, result) => {
    if (err) {
      log('error', err)
      throw err
    }
    // update start Date with contract openning date
    startDailyDate = moment(parseSgeXmlTechnicalData(result), 'YYYY-MM-DD')
    startDailyDateString = startDailyDate.format('YYYY-MM-DD')
  }
}

/**
 * Return start date
 * @param {string} result
 * @returns {string}
 */
function parseSgeXmlTechnicalData(result) {
  log('info', 'Parsing technical data')
  let json = JSON.stringify(result)
  return JSON.parse(json)['Envelope']['Body'][
    'consulterDonneesTechniquesContractuellesResponse'
  ]['point']['donneesGenerales'][
    'dateDerniereModificationFormuleTarifaireAcheminement'
  ]
}

Hugo SUBTIL's avatar
Hugo SUBTIL committed
 * @param {string} result
 * @returns {SGEData[]}
 */
function parseSgeXmlData(result) {
  log('info', 'Parsing list of documents')
  let json = JSON.stringify(result)
  return JSON.parse(json)['Envelope']['Body'][
    'consulterMesuresDetailleesResponse'
  ]['grandeur']['mesure']
}

/**
 * Save data in the right doctype db and prevent duplicated keys
 * @param {EnedisKonnectorData[]} data
 * @param {string} doctype
 * @param {string[]} filterKeys
Hugo SUBTIL's avatar
Hugo SUBTIL committed
 * @returns {Promise<*>}
 */
async function storeData(data, doctype, filterKeys) {
  log('debug', doctype, 'Store into')
  const filteredDocuments = await hydrateAndFilter(data, doctype, {
    keys: filterKeys,
  })
  return await addData(filteredDocuments, doctype)
}

/**
 * Query SGE in order to get info
 * @param {number} pointId
 * @param {string} userLogin
 * @param {string} startDt
 * @param {string} endDt
 * @returns {string}
 */
function userMesureDetailles(pointId, userLogin, startDt, endDt) {
  log('info', `Query data between ${startDt} and ${endDt}`)
  return `<?xml version='1.0' encoding='utf-8'?>
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
     xmlns:v2="http://www.enedis.fr/sge/b2b/services/consultationmesuresdetaillees/v2.0"
     xmlns:v1="http://www.enedis.fr/sge/b2b/technique/v1.0">
     <soapenv:Header/>
     <soapenv:Body>
        <v2:consulterMesuresDetaillees>
           <demande>
              <initiateurLogin>${userLogin}</initiateurLogin>
              <pointId>${pointId}</pointId>
              <mesuresTypeCode>ENERGIE</mesuresTypeCode>
              <grandeurPhysique>EA</grandeurPhysique>
              <soutirage>true</soutirage>
              <injection>false</injection>
              <dateDebut>${startDt}</dateDebut>
              <dateFin>${endDt}</dateFin>
              <mesuresCorrigees>false</mesuresCorrigees>
              <accordClient>true</accordClient>
           </demande>
        </v2:consulterMesuresDetaillees>
     </soapenv:Body>
  </soapenv:Envelope>
  `
}

Hugo SUBTIL's avatar
Hugo SUBTIL committed
/**
 * Get user technical data
 * @param {string} pointId
 * @param {string} userLogin
 * @returns {string}
 */
function userTechnicalData(pointId, userLogin) {
  log('info', `Query userMesureDetailles`)
  return `<?xml version='1.0' encoding='utf-8'?>
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
     xmlns:v2="http://www.enedis.fr/sge/b2b/services/consulterdonneestechniquescontractuelles/v1.0"
     xmlns:v1="http://www.enedis.fr/sge/b2b/technique/v1.0">
     <soapenv:Header/>
     <soapenv:Body>
        <v2:consulterDonneesTechniquesContractuelles>
           <pointId>${pointId}</pointId>
           <loginUtilisateur>${userLogin}</loginUtilisateur>
           <autorisationClient>true</autorisationClient>
        </v2:consulterDonneesTechniquesContractuelles>
     </soapenv:Body>
  </soapenv:Envelope>
  `
}

/**
 * Format data for DB storage
 * @param {SGEData[]} data
 * @returns {Promise<EnedisKonnectorData[]>} Parsed timestamp array
 */
async function formateDataForDoctype(data) {
  log('info', 'Formating data')
  // record
  return data.map(record => {
    let date = moment(record.d, 'YYYY/MM/DD h:mm:ss')

    return {
      load: record.v,
      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')),
    }
  })
Hugo SUBTIL's avatar
Hugo SUBTIL committed
}