Skip to content
Snippets Groups Projects
index.js 8.31 KiB
Newer Older
  • Learn to ignore specific revisions
  • Romain CREY's avatar
    Romain CREY committed
    const {
      BaseKonnector,
      log,
      errors,
      addData,
      hydrateAndFilter,
      cozyClient
    
    Hugo's avatar
    a  
    Hugo committed
    } = require('cozy-konnector-libs')
    const rp = require('request-promise')
    const moment = require('moment')
    require('moment-timezone')
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    Hugo's avatar
    a  
    Hugo committed
    moment.locale('fr') // set the language
    moment.tz.setDefault('Europe/Paris') // set the timezone
    
    Romain CREY's avatar
    Romain CREY committed
    
    const startDate = moment()
    
    Hugo's avatar
    a  
    Hugo committed
      .startOf('year')
    
    Hugo's avatar
    Hugo committed
      // .subtract(3, 'year') TODO PUT TERNARY EXPRESSION FOR MANUAL LAUNCH
    
    Hugo's avatar
    a  
    Hugo committed
      .subtract(1, 'year')
      .subtract(1, 'day')
      .format('MM/DD/YYYY')
    const endDate = moment().format('MM/DD/YYYY')
    const timeRange = ['day', 'month', 'year']
    
    const rangeDate = {
      day: {
    
    Hugo's avatar
    a  
    Hugo committed
        doctype: 'com.grandlyon.egl.day',
        keys: ['year', 'month', 'day']
    
    Hugo's avatar
    a  
    Hugo committed
        doctype: 'com.grandlyon.egl.month',
        keys: ['year', 'month']
    
    Hugo's avatar
    a  
    Hugo committed
        doctype: 'com.grandlyon.egl.year',
        keys: ['year']
    
    Hugo's avatar
    a  
    Hugo committed
    }
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    Hugo's avatar
    a  
    Hugo committed
    module.exports = new BaseKonnector(start)
    
    Romain CREY's avatar
    Romain CREY committed
    
    // 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) {
      try {
        // resetting data for demo only
        // await resetData()
    
    Hugo's avatar
    a  
    Hugo committed
        const baseUrl = cozyParameters.secret.eglBaseURL
        const apiAuthKey = cozyParameters.secret.eglAPIAuthKey
        log('info', 'Authenticating ...')
    
    Romain CREY's avatar
    Romain CREY committed
        const response = await authenticate(
          fields.login,
          fields.password,
          baseUrl,
          apiAuthKey
    
    Hugo's avatar
    a  
    Hugo committed
        )
        log('info', 'Successfully logged in')
    
        Promise.all(
          timeRange.map(timeStep =>
            processData(timeStep, response, baseUrl, apiAuthKey)
          )
    
    Hugo's avatar
    a  
    Hugo committed
        )
    
    Romain CREY's avatar
    Romain CREY committed
      } catch (error) {
    
    Hugo's avatar
    a  
    Hugo committed
        throw new Error(error.message)
    
    Romain CREY's avatar
    Romain CREY committed
      }
    }
    
    async function processData(timeStep, response, baseUrl, apiAuthKey) {
    
    Hugo's avatar
    a  
    Hugo committed
      const doctype = rangeDate[timeStep].doctype
      log('info', 'Getting data TIMESTEP : ' + timeStep)
      const loadProfile = await getData(response, baseUrl, apiAuthKey)
      log('info', 'Saving data to Cozy')
    
      if (doctype === rangeDate.day.doctype) {
    
    Hugo's avatar
    a  
    Hugo committed
        log('info', 'Saving daily data' + loadProfile)
        await storeData(loadProfile, rangeDate.day.doctype, rangeDate.day.keys)
    
      } else if (doctype === rangeDate.month.doctype) {
    
    Hugo's avatar
    a  
    Hugo committed
        await resetInProgressAggregatedData(rangeDate.month.doctype)
        const monthlyData = processMonthlyAggregation(loadProfile, rangeDate.month)
        log('info', 'Saving monthly data' + monthlyData)
        await storeData(monthlyData, rangeDate.month.doctype, rangeDate.month.keys)
    
      } else if (doctype === rangeDate.year.doctype) {
    
    Hugo's avatar
    a  
    Hugo committed
        await resetInProgressAggregatedData(rangeDate.year.doctype)
    
        const yearlyData = processYearAggregation(
          loadProfile,
          rangeDate.year.doctype
    
    Hugo's avatar
    a  
    Hugo committed
        )
        log('info', 'Saving yearly data' + yearlyData)
        await storeData(yearlyData, rangeDate.year.doctype, rangeDate.year.keys)
    
    Hugo's avatar
    a  
    Hugo committed
        throw new Error('Unkonw range type: ' + doctype)
    
    Romain CREY's avatar
    Romain CREY committed
    
    async function authenticate(login, password, baseUrl, apiAuthKey) {
      const authRequest = {
    
    Hugo's avatar
    a  
    Hugo committed
        method: 'POST',
        uri: baseUrl + '/connect.aspx',
    
    Romain CREY's avatar
    Romain CREY committed
        headers: {
          AuthKey: apiAuthKey,
    
    Hugo's avatar
    a  
    Hugo committed
          'Content-Type': 'application/x-www-form-urlencoded'
    
    Romain CREY's avatar
    Romain CREY committed
        },
        form: {
          login: login,
          pass: password
        },
        json: true
    
    Hugo's avatar
    a  
    Hugo committed
      }
      const response = await rp(authRequest)
    
    Yoan VALLET's avatar
    Yoan VALLET committed
      if (response.codeRetour === 100) {
    
    Hugo's avatar
    a  
    Hugo committed
        return response
    
    Yoan VALLET's avatar
    Yoan VALLET committed
      } else {
    
    Hugo's avatar
    a  
    Hugo committed
        throw new Error(errors.LOGIN_FAILED)
    
    Romain CREY's avatar
    Romain CREY committed
      }
    }
    
    async function getData(response, baseUrl, apiAuthKey) {
    
    Hugo's avatar
    a  
    Hugo committed
      log('debug', 'Start date : ' + startDate)
      log('debug', 'End date : ' + endDate)
    
    Romain CREY's avatar
    Romain CREY committed
      const dataRequest = {
    
    Hugo's avatar
    a  
    Hugo committed
        method: 'POST',
        uri: baseUrl + '/getAllAgregatsByAbonnement.aspx',
    
    Romain CREY's avatar
    Romain CREY committed
        headers: {
          AuthKey: apiAuthKey,
    
    Hugo's avatar
    a  
    Hugo committed
          'Content-Type': 'application/x-www-form-urlencoded'
    
    Romain CREY's avatar
    Romain CREY committed
        },
        form: {
          token: response.resultatRetour.token,
          num_abt: response.resultatRetour.num_abt,
          date_debut: startDate,
          date_fin: endDate
        },
        json: true
    
    Hugo's avatar
    a  
    Hugo committed
      }
    
    Romain CREY's avatar
    Romain CREY committed
      try {
    
    Hugo's avatar
    a  
    Hugo committed
        const responseEgl = await rp(dataRequest)
    
    Yoan VALLET's avatar
    Yoan VALLET committed
        switch (responseEgl.codeRetour) {
    
    Romain CREY's avatar
    Romain CREY committed
          case 100:
    
    Hugo's avatar
    a  
    Hugo committed
            return format(responseEgl)
    
    Romain CREY's avatar
    Romain CREY committed
          case -2:
    
    Hugo's avatar
    a  
    Hugo committed
            throw new Error(errors.LOGIN_FAILED)
    
    Romain CREY's avatar
    Romain CREY committed
          case -1:
    
    Hugo's avatar
    a  
    Hugo committed
            throw new Error(errors.VENDOR_DOWN)
    
    Romain CREY's avatar
    Romain CREY committed
          default:
    
    Hugo's avatar
    a  
    Hugo committed
            throw new Error(errors.UNKNOWN_ERROR)
    
    Romain CREY's avatar
    Romain CREY committed
        }
      } catch (error) {
    
    Hugo's avatar
    a  
    Hugo committed
        throw new Error(errors.VENDOR_DOWN)
    
    Romain CREY's avatar
    Romain CREY committed
      }
    }
    
    function format(response) {
    
    Hugo's avatar
    Hugo committed
      const data = response.resultatRetour
        .slice(1)
    
    Hugo's avatar
    a  
    Hugo committed
        .filter(value => value.ValeurIndex)
      const dataLen = data.length
    
      data.map((value, index) => {
    
    Hugo's avatar
    a  
    Hugo committed
        const time = moment(value.DateReleve, moment.ISO_8601)
    
        if (index + 1 < dataLen) {
    
    Hugo's avatar
    Hugo committed
          log(
    
    Hugo's avatar
    a  
    Hugo committed
            'info',
            'date -> ' +
    
    Hugo's avatar
    Hugo committed
              value.DateReleve +
    
    Hugo's avatar
    a  
    Hugo committed
              ' SUBSTRACTING : ' +
    
    Hugo's avatar
    Hugo committed
              value.ValeurIndex +
    
    Hugo's avatar
    a  
    Hugo committed
              ' - ' +
    
    Hugo's avatar
    Hugo committed
              response.resultatRetour[index].ValeurIndex +
    
    Hugo's avatar
    a  
    Hugo committed
              '\n'
          )
    
          return {
            load: data[index + 1].ValeurIndex - value.ValeurIndex,
    
    Hugo's avatar
    a  
    Hugo committed
            year: parseInt(time.format('YYYY')),
            month: parseInt(time.format('M')),
            day: parseInt(time.format('D')),
    
            hour: 0,
            minute: 0,
            type: value.TypeAgregat
    
    Hugo's avatar
    a  
    Hugo committed
          }
    
        } else {
    
    Hugo's avatar
    Hugo committed
          return {
    
            load: null,
    
    Hugo's avatar
    a  
    Hugo committed
            year: parseInt(time.format('YYYY')),
            month: parseInt(time.format('M')),
            day: parseInt(time.format('D')),
    
    Hugo's avatar
    Hugo committed
            hour: 0,
            minute: 0,
            type: value.TypeAgregat
    
    Hugo's avatar
    a  
    Hugo committed
          }
    
    Hugo's avatar
    a  
    Hugo committed
      })
      log('info', 'DATALOAD LIST\n')
    
    Hugo's avatar
    Hugo committed
      data.map(value => {
        log(
    
    Hugo's avatar
    a  
    Hugo committed
          'info',
    
    Hugo's avatar
    Hugo committed
          value.month +
    
    Hugo's avatar
    a  
    Hugo committed
            '-' +
    
    Hugo's avatar
    Hugo committed
            value.day +
    
    Hugo's avatar
    a  
    Hugo committed
            '-' +
    
    Hugo's avatar
    Hugo committed
            value.year +
    
    Hugo's avatar
    a  
    Hugo committed
            ' : ' +
    
    Hugo's avatar
    Hugo committed
            value.load +
    
    Hugo's avatar
    a  
    Hugo committed
            '\n'
        )
      })
      return data
    
    function processYearAggregation(data, doctype) {
    
    Hugo's avatar
    a  
    Hugo committed
      log('info', 'Start aggregation for : ' + doctype)
      const grouped = data.reduce(reduceYearFunction, {})
      return Object.values(grouped)
    
    }
    
    function processMonthlyAggregation(data, range) {
    
    Hugo's avatar
    a  
    Hugo committed
      log('info', 'Start aggregation for : ' + range.doctype)
    
      // Filter by year
    
    Hugo's avatar
    a  
    Hugo committed
      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
    
    Hugo's avatar
    a  
    Hugo committed
        var monthlyData = tmpData[keys[index]]
    
        // Monthly aggregation
    
    Hugo's avatar
    a  
    Hugo committed
        var aggregatedData = monthlyData.reduce(reduceMonthFunction, {})
    
    Hugo's avatar
    a  
    Hugo committed
        dataToStore = dataToStore.concat(Object.values(aggregatedData))
    
    Hugo's avatar
    a  
    Hugo committed
      return dataToStore
    
    }
    
    function groupBy(xs, key) {
      return xs.reduce(function(rv, x) {
    
    Hugo's avatar
    a  
    Hugo committed
        ;(rv[x[key]] = rv[x[key]] || []).push(x)
        return rv
      }, {})
    
    }
    
    function reduceYearFunction(acc, x) {
    
    Hugo's avatar
    a  
    Hugo committed
      var id = acc[x.year]
    
    Hugo's avatar
    a  
    Hugo committed
        id.load += x.load
    
    Hugo's avatar
    a  
    Hugo committed
        acc[x.year] = x
    
    Hugo's avatar
    a  
    Hugo committed
      return acc
    
    }
    
    function reduceMonthFunction(acc, x) {
    
    Hugo's avatar
    a  
    Hugo committed
      var id = acc[x.month]
    
    Hugo's avatar
    a  
    Hugo committed
        id.load += x.load
    
    Hugo's avatar
    a  
    Hugo committed
        acc[x.month] = x
    
    Hugo's avatar
    a  
    Hugo committed
      return acc
    
    }
    
    async function storeData(data, doctype, keys) {
    
    Hugo's avatar
    a  
    Hugo committed
      log('debug', 'Store into ' + doctype)
      log('debug', keys, 'Store into keys ')
    
      return hydrateAndFilter(data, doctype, {
        keys: keys
    
    Romain CREY's avatar
    Romain CREY committed
      }).then(filteredDocuments => {
    
    Hugo's avatar
    a  
    Hugo committed
        addData(filteredDocuments, doctype)
      })
    
    /**
     * Function handling special case.
     * The temporary aggregated data need to be remove in order for the most recent one te be saved.
    
     * ex for com.grandlyon.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
    
    Hugo's avatar
    a  
    Hugo committed
      log('debug', doctype, 'Remove aggregated data for')
      const result = await cozyClient.data.findAll(doctype)
    
      if (result.error || result.length <= 0) {
    
    Romain CREY's avatar
    Romain CREY committed
        // eslint-disable-next-line no-console
    
    Hugo's avatar
    a  
    Hugo committed
        console.warn('Error while fetching loads, doctype not found ')
    
    Hugo's avatar
    a  
    Hugo committed
        const currentDate = moment()
    
        // Filter data to remove
    
    Hugo's avatar
    a  
    Hugo committed
        var filtered = []
    
        if (doctype === rangeDate.year.doctype) {
          // Yearly case
          filtered = result.filter(function(el) {
    
    Hugo's avatar
    a  
    Hugo committed
            return el.year == currentDate.year()
          })
    
        } else {
          // Monthly case
          filtered = result.filter(function(el) {
            return (
              el.year == currentDate.year() &&
    
    Hugo's avatar
    a  
    Hugo committed
              el.month == parseInt(moment().format('M'))
            )
          })
    
        }
        // Remove data
        for (const doc of filtered) {
    
    Hugo's avatar
    a  
    Hugo committed
          log('debug', doc, 'Removing this entry for ' + doctype)
          await cozyClient.data.delete(doctype, doc)
    
    Romain CREY's avatar
    Romain CREY committed
      }
    }