Skip to content
Snippets Groups Projects
aggregatorUsageEvents.ts 42.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • import logger from 'cozy-logger'
    import { Client } from 'cozy-client'
    import { runService } from './service'
    import UsageEventService from 'services/usageEvent.service'
    import ProfileService from 'services/profile.service'
    import ConsumptionService from 'services/consumption.service'
    import { FluidStatus, PerformanceIndicator, UsageEvent } from 'models'
    import { UsageEventType } from 'enum/usageEvent.enum'
    import { DateTime } from 'luxon'
    import { toNumber, uniq } from 'lodash'
    import FluidService from 'services/fluid.service'
    import { FluidState, FluidType } from 'enum/fluid.enum'
    import { getFluidType } from 'utils/utils'
    import { TimeStep } from 'enum/timeStep.enum'
    
    import EnvironmentService from 'services/environment.service'
    
    import { DaccEvent } from 'enum/dacc.enum'
    import { UsageEventProperties } from 'enum/usageEventProperties.enum'
    import ChallengeService from 'services/challenge.service'
    import { UserChallengeState } from 'enum/userChallenge.enum'
    
    import ProfileTypeEntityService from 'services/profileTypeEntity.service'
    
    import TermsService from 'services/terms.service'
    
    import { WarmingType } from 'enum/profileType.enum'
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
    import { FluidSlugType } from 'enum/fluidSlug.enum'
    
    
    const log = logger.namespace('aggregatorUsageEvents')
    
    interface AggregatorUsageEventsProps {
      client: Client
    }
    
    interface Indicator {
      createdBy: string
      measureName: string
      startDate: string
      value: number | null
      group1?: object
      group2?: object
      group3?: object
    }
    
    // Store all id of events read during the process
    const readUsageEvents: UsageEvent[] = []
    
    
    // Store error events
    const errorEvent: { error: any; doctype: string }[] = []
    
    
    const sendIndicator = async (
      indicator: Indicator,
      client: Client
    ): Promise<boolean> => {
      try {
    
        const environmentService = new EnvironmentService()
    
          environmentService.isProduction()
    
            ? 'Sending data to dacc'
            : 'Sending data to dacc-dev'
        )
    
        // /!\ In order to test locally, please replace /remote/cc.cozycloud.dacc with http://localhost:8081
    
        await client
          .getStackClient()
          .fetchJSON(
            'POST',
    
            environmentService.isProduction()
    
              ? '/remote/cc.cozycloud.dacc'
              : '/remote/cc.cozycloud.dacc.dev',
            {
              data: JSON.stringify(indicator),
            }
          )
        return true
      } catch (error) {
        log(
          'error',
    
          `Error while sending indicator ${indicator.measureName} to remote doctype: ${error.message}`
    
        )
        throw error
      }
    }
    
    const reduceEvents = (
      events: UsageEvent[]
    ): { [key: string]: UsageEvent[] } => {
      return events.reduce(function(
        acc: {
          [key: string]: UsageEvent[]
        },
        event: UsageEvent
      ) {
        const key = `${event.eventDate.startOf('day').toString()}|${event.target}|${
          event.result
        }|${event.context}`
        if (!acc[key]) {
          acc[key] = []
        }
        acc[key].push(event)
        readUsageEvents.push(event)
        return acc
      }, {})
    }
    
    const sendAggregatedEventByDay = async (
      events: UsageEvent[],
      eventType: DaccEvent,
      client: Client,
      groupsKeys: { group1: string; group2?: string; group3?: string },
      groupsIndexes: number[],
      customValues?: (string | null)[]
    ) => {
      const reducedEvents = reduceEvents(events)
      for (const item of Object.keys(reducedEvents)) {
        const splitedKey = item.split('|')
        let group1 = {}
        let group2 = {}
        let group3 = {}
        if (
          groupsKeys.group1 &&
          (groupsIndexes[0] !== 0 || (customValues && customValues[0]))
        ) {
          group1 = {
            [groupsKeys.group1]:
              customValues && customValues[0]
                ? customValues[0]
                : splitedKey[groupsIndexes[0]],
          }
        }
        if (
          groupsKeys.group2 &&
          (groupsIndexes[1] !== 0 || (customValues && customValues[1]))
        ) {
          group2 = {
            [groupsKeys.group2]:
              customValues && customValues[1]
                ? customValues[1]
                : splitedKey[groupsIndexes[1]],
          }
        }
        if (
          groupsKeys.group3 &&
          (groupsIndexes[2] !== 0 || (customValues && customValues[2]))
        ) {
          group3 = {
            [groupsKeys.group3]:
              customValues && customValues[2]
                ? customValues[2]
                : splitedKey[groupsIndexes[2]],
          }
        }
        const indicator: Indicator = {
          createdBy: 'ecolyo',
          measureName: eventType,
          startDate: DateTime.fromISO(splitedKey[0]).toISODate(),
          value: reducedEvents[item].length,
          group1: group1,
          ...(Object.keys(group2).length > 0 && { group2: group2 }),
          ...(Object.keys(group3).length > 0 && { group3: group3 }),
        }
        const result = await sendIndicator(indicator, client)
        if (result) {
          readUsageEvents.push(...reducedEvents[item])
        }
      }
    }
    
    const handleQuizStars = async (events: UsageEvent[], client: Client) => {
      events.forEach(async event => {
        const indicator: Indicator = {
          createdBy: 'ecolyo',
          measureName: DaccEvent.QUIZ_STARS,
          startDate: event.startDate ? event.startDate.toISODate() : '',
          value: parseInt(event.result ? event.result : '0'),
          // eslint-disable-next-line @typescript-eslint/camelcase
          group1: { challenge_id: event.context ? event.context : '' },
          // eslint-disable-next-line @typescript-eslint/camelcase
          group2: { quiz_id: event.target ? event.target : '' },
        }
        const result = await sendIndicator(indicator, client)
        if (result) {
          readUsageEvents.push(event)
        }
      })
    }
    
    const calculSessionTime = async (events: UsageEvent[], client: Client) => {
      let startSessionDate: DateTime | null = null
      let isFirstConnection = false
      let navigationCount = 0
      for (const [index, event] of events.entries()) {
        if (event.type === UsageEventType.CONNECTION_EVENT) {
          if (
            startSessionDate &&
            index > 0 &&
            events[index - 1].type !== UsageEventType.CONNECTION_EVENT
          ) {
            const endDate = events[index - 1].eventDate
            const duration = endDate.diff(startSessionDate, ['seconds']).toObject()
              .seconds
            const sessionIndicator: Indicator = {
              createdBy: 'ecolyo',
              measureName: DaccEvent.SESSION_DURATION,
              startDate: startSessionDate.toISODate(),
              value: duration === undefined ? 0 : duration,
              // eslint-disable-next-line @typescript-eslint/camelcase
              group1: { number_pages: navigationCount.toString() },
              // eslint-disable-next-line @typescript-eslint/camelcase
              group2: { session_type: isFirstConnection ? 'first' : 'any' },
            }
            await sendIndicator(sessionIndicator, client)
            navigationCount = 0
          }
          startSessionDate = event.eventDate
          isFirstConnection =
            event.result && event.result === 'firstConnection' ? true : false
        } else if (event.type === UsageEventType.NAVIGATION_EVENT) {
          if (startSessionDate) {
            navigationCount += 1
          }
        } else if (index === events.length - 1) {
          if (startSessionDate) {
            const endDate = event.eventDate
            const duration = endDate.diff(startSessionDate, ['seconds']).toObject()
              .seconds
            const sessionIndicator: Indicator = {
              createdBy: 'ecolyo',
              measureName: DaccEvent.SESSION_DURATION,
              startDate: startSessionDate.toISODate(),
              value: duration === undefined ? 0 : duration,
              // eslint-disable-next-line @typescript-eslint/camelcase
              group1: { number_pages: navigationCount.toString() },
              // eslint-disable-next-line @typescript-eslint/camelcase
              group2: { session_type: isFirstConnection ? 'first' : 'any' },
            }
            await sendIndicator(sessionIndicator, client)
          }
        }
      }
    }
    
    const calculPeriodBetweenFirstConnectionAndFirstChallenge = async (
      events: UsageEvent[],
      firstConnectionEvent: UsageEvent,
      client: Client
    ) => {
      const challengeLaunchEvents: UsageEvent[] = events.filter(
        (event: UsageEvent) => event.type === UsageEventType.CHALLENGE_LAUNCH_EVENT
      )
      if (
        challengeLaunchEvents.length > 0 &&
        challengeLaunchEvents[0].target === 'CHALLENGE0001'
      ) {
        const periodIndicator: Indicator = {
          createdBy: 'ecolyo',
          measureName: DaccEvent.EVENT_DURATION,
          startDate: firstConnectionEvent.eventDate.toISODate(),
          value: challengeLaunchEvents[0].eventDate.diff(
            firstConnectionEvent.eventDate
          ).seconds,
          // eslint-disable-next-line @typescript-eslint/camelcase
          group1: { start_event: 'first_session' },
          // eslint-disable-next-line @typescript-eslint/camelcase
          group2: { end_event: 'first_challenge' },
    
        }
        const result = await sendIndicator(periodIndicator, client)
        if (result) {
          readUsageEvents.push(challengeLaunchEvents[0])
        }
      }
    }
    
    const calculPeriodBetweenChallenge = async (
      events: UsageEvent[],
      client: Client
    ) => {
      const challengeLaunchEvents: UsageEvent[] = events.filter(
        (event: UsageEvent) => event.type === UsageEventType.CHALLENGE_LAUNCH_EVENT
      )
      if (challengeLaunchEvents.length > 0) {
        const allEndedChallengeEvents: UsageEvent[] = await UsageEventService.getEvents(
          client,
          {
            type: UsageEventType.CHALLENGE_END_EVENT,
          }
        )
        for (const event of challengeLaunchEvents) {
          if (event.target && event.target !== 'CHALLENGE0001') {
            const challengeId: number = toNumber(
              event.target.substr(event.target.length - 4)
            )
            const prevChallengeId = `CHALLENGE${(challengeId - 1)
              .toString()
              .padStart(4, '0')}`
            const previousEndedChallengeIndex: number = allEndedChallengeEvents.findIndex(
              (endedEvent: UsageEvent) => endedEvent.target === prevChallengeId
            )
            if (previousEndedChallengeIndex > -1) {
              const periodChallengeIndicator: Indicator = {
                createdBy: 'ecolyo',
                measureName: DaccEvent.EVENT_DURATION,
                startDate: allEndedChallengeEvents[
                  previousEndedChallengeIndex
                ].eventDate.toISODate(),
                value: event.eventDate.diff(
                  allEndedChallengeEvents[previousEndedChallengeIndex].eventDate
                ).seconds,
                // eslint-disable-next-line @typescript-eslint/camelcase
                group1: { start_event: 'first_session' },
                // eslint-disable-next-line @typescript-eslint/camelcase
                group2: { end_event: 'first_challenge' },
                group3: { params: event.target + ':' + prevChallengeId },
              }
              await sendIndicator(periodChallengeIndicator, client)
            }
          }
        }
      }
    }
    
    /**
     * Format a string with all konnectors in success state
     * @param konnectorfluidTypes FluidType[]
     * @returns string
     */
    const getConnectedKonnectorSlug = (
      konnectorfluidTypes: FluidType[]
    ): string => {
      let slug = ''
      if (konnectorfluidTypes.includes(FluidType.ELECTRICITY)) {
        slug += 'electricity'
      }
      if (konnectorfluidTypes.includes(FluidType.GAS)) {
        if (slug.length > 0) {
          slug += ':'
        }
        slug += 'gas'
      }
      if (konnectorfluidTypes.includes(FluidType.WATER)) {
        if (slug.length > 0) {
          slug += ':'
        }
        slug += 'water'
      }
      return slug
    }
    
    const calculateConnectedKonnectorPerDay = async (client: Client) => {
      const fluidService: FluidService = new FluidService(client)
      const fluidStatus: FluidStatus[] = await fluidService.getFluidStatus()
      const connectedKonnectors = fluidStatus.filter(
        fluid => fluid.status === FluidState.DONE
      )
      log('info', 'calculateConnectedKonnectorPerDay')
      if (connectedKonnectors.length > 0) {
        const konnectorfluidTypes: FluidType[] = []
        for (const konnector of connectedKonnectors) {
          konnectorfluidTypes.push(konnector.fluidType)
        }
        const KonnectorConnectedPerDayIndicator: Indicator = {
          createdBy: 'ecolyo',
          measureName: DaccEvent.KONNECTOR_CONNECTED_PER_DAY,
          startDate: DateTime.local()
            .setZone('utc', {
              keepLocalTime: true,
            })
            .startOf('day')
            .toISODate(),
          value: connectedKonnectors.length,
          group1: { categories: getConnectedKonnectorSlug(konnectorfluidTypes) },
        }
        await sendIndicator(KonnectorConnectedPerDayIndicator, client)
      }
    }
    
    /**
     * Build indicator group string with handling of : ECS, heating for gas and electricity.
     *
     * @param client Client
     * @param fluidType FluidType
     * @returns Promise<string>
     */
    const buildProfileWithFuildType = async (
      client: Client,
    
      fluidType: FluidType,
      monthToAggregate?: number
    
    ): Promise<string> => {
      let formatedProfile = ''
      const profile = await new ProfileService(client).getProfile()
      // If profile is not filled, return empty string
    
    
      if (profile && !profile.isProfileTypeCompleted) return formatedProfile
      const date = monthToAggregate
        ? DateTime.local()
            .setZone('utc', {
              keepLocalTime: true,
            })
            .set({ month: monthToAggregate })
        : null
      const profileType = await new ProfileTypeEntityService(client).getProfileType(
        date
      )
    
      if (fluidType === FluidType.ELECTRICITY) {
        if (
          profile &&
    
          profileType.warmingFluid === WarmingType.ELECTRICITY
    
        ) {
          formatedProfile = 'chauffage élec'
        }
    
        if (
          profile &&
          profileType &&
          profileType.warmingFluid === WarmingType.WOOD
        ) {
          formatedProfile = 'chauffage bois'
        }
        if (
          profile &&
          profileType &&
          profileType.warmingFluid === WarmingType.FUEL
        ) {
          formatedProfile = 'chauffage fuel'
        }
    
          profileType &&
          profileType.hotWaterFluid === FluidType.ELECTRICITY
    
        ) {
          if (formatedProfile.length === 0) {
            formatedProfile = 'ECS'
          } else {
            formatedProfile += ':ECS'
          }
        }
        if (
          profile &&
    
          profileType &&
          profileType.cookingFluid === FluidType.ELECTRICITY
    
        ) {
          if (formatedProfile.length === 0) {
            formatedProfile = 'cuisine élec'
          } else {
            formatedProfile += ':cuisine élec'
          }
        }
        if (formatedProfile.length === 0) {
          return 'électricité spécifique'
        } else {
          return formatedProfile
        }
      } else if (fluidType === FluidType.GAS) {
    
        if (
          profile &&
          profileType &&
          profileType.warmingFluid === WarmingType.GAS
        ) {
    
          formatedProfile = 'chauffage gaz'
        }
    
        if (profile && profileType && profileType.hotWaterFluid === FluidType.GAS) {
    
          if (formatedProfile.length === 0) {
            formatedProfile = 'ECS'
          } else {
            formatedProfile += ':ECS'
          }
        }
    
        if (profile && profileType && profileType.cookingFluid === FluidType.GAS) {
    
          if (formatedProfile.length === 0) {
            formatedProfile = 'cuisine gaz'
          } else {
            formatedProfile += ':cuisine gaz'
          }
        }
        if (formatedProfile.length === 0) {
          return 'autre'
        } else {
          return formatedProfile
        }
      }
      return formatedProfile
    }
    
    const getConsumptionValue = async (
      client: Client,
    
      fluidType: FluidType[],
      monthToAggregate?: number
    
    ): Promise<PerformanceIndicator[]> => {
      const consumptionService = new ConsumptionService(client)
    
      const analysisDate = monthToAggregate
        ? DateTime.local()
            .setZone('utc', { keepLocalTime: true })
            .set({ month: monthToAggregate })
        : DateTime.local().setZone('utc', { keepLocalTime: true })
    
      const periods = {
        timePeriod: {
          startDate: analysisDate.minus({ month: 1 }).startOf('month'),
          endDate: analysisDate.minus({ month: 1 }).endOf('month'),
        },
        comparisonTimePeriod: {
          startDate: analysisDate.minus({ month: 2 }).startOf('month'),
          endDate: analysisDate.minus({ month: 2 }).endOf('month'),
        },
      }
      const fetchedPerformanceIndicators = await consumptionService.getPerformanceIndicators(
        periods.timePeriod,
        TimeStep.MONTH,
        fluidType,
        periods.comparisonTimePeriod
      )
      return fetchedPerformanceIndicators
    }
    
    /**
     * Send an indicator on the consumption variation in % for each fluid type.
     * @param client
     * @group [{ slug }, { seniority (in month) }, { profile (ECS, chauffage, etc...) }],
     */
    
    const calculateConsumptionVariation = async (
      client: Client,
      monthToAggregate?: number
    ) => {
    
      log('info', `calculateConsumptionVariation`)
    
      const consumptionData = await getConsumptionValue(
        client,
        [FluidType.ELECTRICITY, FluidType.GAS, FluidType.WATER],
        monthToAggregate
      )
    
      for (const fluidType in [
        FluidType.ELECTRICITY,
        FluidType.GAS,
        FluidType.WATER,
      ]) {
        if (fluidType < FluidType.MULTIFLUID.toFixed()) {
          // Seniority process
          const [firstConnectionEvent] = await UsageEventService.getEvents(client, {
            type: UsageEventType.CONNECTION_EVENT,
            result: 'firstConnection',
          })
    
          if (firstConnectionEvent) {
    
            const seniority = 0
            // Seniority is set to 0, otherwise the indicator is too specific
            // monthToAggregate
            //   ? DateTime.local()
            //       .setZone('utc', {
            //         keepLocalTime: true,
            //       })
            //       .set({ month: monthToAggregate })
            //       .diff(firstConnectionEvent.eventDate, 'month')
            //       .get('month')
            //   : DateTime.local()
            //       .setZone('utc', {
            //         keepLocalTime: true,
            //       })
            //       .diff(firstConnectionEvent.eventDate, 'month')
            //       .get('month')
    
            const consumptionVariationIndicator: Indicator = {
    
              createdBy: 'ecolyo',
              measureName: DaccEvent.CONSUMPTION_VARIATION_MONTHLY,
    
              startDate: monthToAggregate
                ? DateTime.local()
                    .setZone('utc', {
                      keepLocalTime: true,
                    })
                    .set({ month: monthToAggregate })
                    .startOf('day')
                    .toISODate()
                : DateTime.local()
                    .setZone('utc', {
                      keepLocalTime: true,
                    })
                    .startOf('day')
                    .toISODate(),
    
              value:
                consumptionData[fluidType] &&
                consumptionData[fluidType].percentageVariation
                  ? consumptionData[fluidType].percentageVariation
                  : 0, // in percent
              // eslint-disable-next-line @typescript-eslint/camelcase
    
              group1: { fluid_type: FluidType[fluidType].toLowerCase() },
    
              // eslint-disable-next-line @typescript-eslint/camelcase
    
              group2: { seniority: Math.round(seniority).toString() },
    
                // eslint-disable-next-line @typescript-eslint/camelcase
                fluid_usage: await buildProfileWithFuildType(
    
                  getFluidType(FluidType[fluidType]),
                  monthToAggregate
    
                ),
              },
            }
    
            // if user wasnt connected during current month, dont send indicator
            const events: UsageEvent[] = await UsageEventService.getEvents(client, {
    
              eventDate: monthToAggregate
                ? {
                    $lt: DateTime.local()
                      .setZone('utc', {
                        keepLocalTime: true,
                      })
                      .set({ month: monthToAggregate })
                      .endOf('month')
                      .minus({ month: 1 })
                      .toString(),
                    $gt: DateTime.local()
                      .setZone('utc', {
                        keepLocalTime: true,
                      })
                      .set({ month: monthToAggregate })
                      .startOf('month')
                      .minus({ month: 1 })
                      .toString(),
                  }
                : {
                    $lt: DateTime.local()
                      .setZone('utc', {
                        keepLocalTime: true,
                      })
                      .endOf('month')
                      .minus({ month: 1 })
                      .toString(),
                    $gt: DateTime.local()
                      .setZone('utc', {
                        keepLocalTime: true,
                      })
                      .startOf('month')
                      .minus({ month: 1 })
                      .toString(),
                  },
    
            if (events.length > 0 && consumptionVariationIndicator.value !== 0) {
    
              log(
                'info',
                `Send variation indicator for ${monthToAggregate} : ${JSON.stringify(
                  consumptionVariationIndicator
                )}`
              )
    
              await sendIndicator(consumptionVariationIndicator, client)
    
    const sendConnectionCount = async (
      client: Client,
      monthToAggregate?: number
    ) => {
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
      log('info', `sendConnectionCount`)
      // Get daily connexion
      const events: UsageEvent[] = await UsageEventService.getEvents(client, {
        type: UsageEventType.CONNECTION_EVENT,
    
        eventDate: monthToAggregate
          ? {
              $lt: DateTime.local()
                .setZone('utc', {
                  keepLocalTime: true,
                })
                .set({ month: monthToAggregate })
                .minus({ month: 1 })
                .endOf('month')
                .toString(),
              $gt: DateTime.local()
                .setZone('utc', {
                  keepLocalTime: true,
                })
                .set({ month: monthToAggregate })
                .minus({ month: 1 })
                .startOf('month')
                .toString(),
            }
          : {
              $lt: DateTime.local()
                .setZone('utc', {
                  keepLocalTime: true,
                })
                .endOf('month')
                .minus({ month: 1 })
                .toString(),
              $gt: DateTime.local()
                .setZone('utc', {
                  keepLocalTime: true,
                })
                .startOf('month')
                .minus({ month: 1 })
                .toString(),
            },
    
      let uniqueDates = 0
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
      // If there is at least one connection, filter each unique connection in order to send it
      if (events.length > 0) {
    
        uniqueDates = events
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
          .map(s => s.eventDate.day)
    
          .filter((s, i, a) => a.indexOf(s) == i).length
      }
    
      const connectionMonthly: Indicator = {
        createdBy: 'ecolyo',
        measureName: DaccEvent.CONNECTION_COUNT_MONTHLY,
    
        startDate: monthToAggregate
          ? DateTime.local()
              .setZone('utc', {
                keepLocalTime: true,
              })
              .set({ month: monthToAggregate })
              .startOf('day')
              .toISODate()
          : DateTime.local()
              .setZone('utc', {
                keepLocalTime: true,
              })
              .startOf('day')
              .toISODate(),
    
        value: uniqueDates,
    
      log(
        'info',
        `Send connectionMonthly indicator for ${monthToAggregate} : ${JSON.stringify(
          connectionMonthly
        )}`
      )
    
      await sendIndicator(connectionMonthly, client)
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
    }
    
    const sendProfileCount = async (client: Client) => {
      log('info', `sendProfileCount`)
      // Get profile setEvents
      const events: UsageEvent[] = await UsageEventService.getEvents(client, {
        type: UsageEventType.PROFILE_SET_EVENT,
      })
    
      // If there is at least one connection, filter each unique connection in order to send it
      if (events.length > 0) {
        const profileSet: Indicator = {
          createdBy: 'ecolyo',
          measureName: DaccEvent.PROFILE_COUNT_MONTHLY,
          startDate: DateTime.local()
            .setZone('utc', {
              keepLocalTime: true,
            })
            .startOf('day')
            .toISODate(),
          value: events.length,
        }
        await sendIndicator(profileSet, client)
      }
    }
    
    
    const sendEmailSubscription = async (client: Client) => {
      log('info', `sendEmailSubscription`)
      const profile = await new ProfileService(client).getProfile()
      if (profile && profile.sendAnalysisNotification) {
        const cameBackFromEmail: Indicator = {
          createdBy: 'ecolyo',
          measureName: DaccEvent.SUMMARY_SUBSCRIPTION_MONTHLY,
          startDate: DateTime.local()
            .setZone('utc', {
              keepLocalTime: true,
            })
            .startOf('day')
            .toISODate(),
          value: profile.sendAnalysisNotification ? 1 : 0,
          // eslint-disable-next-line @typescript-eslint/camelcase
          group1: { event_type: 'subscribed' },
        }
        await sendIndicator(cameBackFromEmail, client)
      }
    }
    
    
    /**
     * Send indicator to dacc, if user has half-hour data.
     * @param client CozyClient
     */
    const sendHalfHourConsumption = async (client: Client) => {
      log('info', `sendHalfHourConsumption`)
      const consumptionService = new ConsumptionService(client)
    
      const data = await consumptionService.getLastHourData(
        client,
        DateTime.local()
          .minus({ month: 1 })
          .startOf('month').month
      )
    
      const halfHourConsumption: Indicator = {
        createdBy: 'ecolyo',
    
        measureName: DaccEvent.FLUID_DATA_GRANULARITY,
        // eslint-disable-next-line @typescript-eslint/camelcase
        group1: { fluid_type: 'electricity' },
        // eslint-disable-next-line @typescript-eslint/camelcase
        group2: { granularity: 'half_hour' },
    
        startDate: DateTime.local()
          .setZone('utc', {
            keepLocalTime: true,
          })
          .startOf('day')
          .toISODate(),
        value: data && data.length > 0 ? 1 : 0,
      }
      await sendIndicator(halfHourConsumption, client)
    }
    
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
    /**
     * Send indicator to dacc, each month send if user has succed to configure a connector with the number of try.
     * @param client CozyClient
     */
    const sendKonnectorEvents = async (client: Client) => {
      log('info', `sendKonnectorEvents`)
      const slugs = Object.values(FluidSlugType)
      const today = DateTime.local().setZone('utc', {
        keepLocalTime: true,
      })
      slugs.forEach(async slug => {
        let successEvents: UsageEvent[] = await UsageEventService.getEvents(
          client,
          {
            type: UsageEventType.KONNECTOR_CONNECT_EVENT,
            target: slug,
            result: 'success',
            eventDate: {
              $lte: today
                .endOf('month')
                .minus({ month: 1 })
                .toString(),
              $gte: today
                .startOf('month')
                .minus({ month: 1 })
                .toString(),
            },
          },
          true
        )
    
        // If there is no success in month, send nothing
        if (successEvents && successEvents.length > 0) {
          // Get all success events
          successEvents = await UsageEventService.getEvents(
            client,
            {
              type: UsageEventType.KONNECTOR_CONNECT_EVENT,
              target: slug,
              result: 'success',
            },
            true
          )
          // Remove success from other month, they should have been already proceced
          // successEvents.length = successEventsOfCurrentMonth
    
          for (let index = 0; index < successEvents.length; index++) {
            const successEvent = successEvents[index]
            let query = null
    
            // If there is a previous value take it as reference for the query
            // Else get all previous because it's the first one
            if (index + 1 < successEvents.length) {
              query = {
                type: UsageEventType.KONNECTOR_CONNECT_EVENT,
                target: slug,
                result: 'error',
                eventDate: {
                  $lte: successEvent.eventDate,
                  $gte: successEvents[index + 1].eventDate,
                },
              }
            } else {
              query = {
                type: UsageEventType.KONNECTOR_CONNECT_EVENT,
                target: slug,
                result: 'error',
                eventDate: {
                  $lte: successEvent.eventDate,
                },
              }
            }
    
            const allConnectionEvents: UsageEvent[] = await UsageEventService.getEvents(
              client,
              query,
              true
            )
    
            const konnectorSuccess: Indicator = {
              createdBy: 'ecolyo',
              measureName: DaccEvent.PARTNER_SUCESS_MONTHLY,
              // eslint-disable-next-line @typescript-eslint/camelcase
              group1: { fluid_type: slug },
              startDate: DateTime.local()
                .setZone('utc', {
                  keepLocalTime: true,
                })
                .startOf('day')
                .toISODate(),
              value: allConnectionEvents.length + 1, //+1 in order to count the success
            }
            // Send indicator if it's in current month
            if (successEvent.eventDate.month === today.minus({ month: 1 }).month) {
              await sendIndicator(konnectorSuccess, client)
            }
          }
        }
      })
    }
    
    
    /**
     * Send the total number of partner connection attempts and the number of success
     * @param client CozyClient
     */
    const sendKonnectorAttemptsMonthly = async (client: Client) => {
      log('info', `sendkonnectorAttemptsMonthly`)
      const slugs = Object.values(FluidSlugType)
      const today = DateTime.local().setZone('utc', {
        keepLocalTime: true,
      })
      // Count the number of connection and refresh events
      slugs.forEach(async slug => {
        const konnectorEvents: UsageEvent[] = await UsageEventService.getEvents(
          client,
          {
            type: UsageEventType.KONNECTOR_ATTEMPT_EVENT,
            target: slug,
            eventDate: {
              $lte: today
                .endOf('month')
                .minus({ month: 1 })
                .toString(),
              $gte: today
                .startOf('month')
                .minus({ month: 1 })
                .toString(),
            },
          },
          true
        )
        log('info', ` : ${JSON.stringify(konnectorEvents)}`)
    
        // Check if there is a success (will be false or true since the event is triggered only for the first connexion)
        const success: boolean =
          konnectorEvents.filter(event => event.result == 'success').length > 0
    
        const konnectorAttempts: Indicator = {
          createdBy: 'ecolyo',
          measureName: DaccEvent.UNINITIALIZED_KONNECTOR_ATTEMPTS_MONTHLY,
          // eslint-disable-next-line @typescript-eslint/camelcase
          group1: { slug: slug },
          group2: { success: success },
          startDate: DateTime.local()
            .setZone('utc', {
              keepLocalTime: true,
            })
            .startOf('day')
            .toISODate(),
          value: konnectorEvents.length,
        }
        // Send indicator if there is connection events
        if (konnectorEvents.length > 0) {
          await sendIndicator(konnectorAttempts, client)
        }
      })
    }
    
    
    const aggregateEvents = async (
      events: UsageEvent[],
      eventType: UsageEventType,
      firstConnectionEvent: UsageEvent,
      client: Client
    ) => {
      let reducedEvents = null // Used to store reducedEvents during calculation
      switch (eventType) {
        case UsageEventType.CONNECTION_EVENT:
          await sendAggregatedEventByDay(
            events,
            DaccEvent.CONNECTION_COUNT_DAILY,
            client,
            { group1: 'device' },
            [UsageEventProperties.CONTEXT]
          )
          break
        case UsageEventType.KONNECTOR_CONNECT_EVENT:
          await sendAggregatedEventByDay(
            events,
            DaccEvent.KONNECTOR_EVENT_DAILY,
            client,
            { group1: 'slug', group2: 'event_type', group3: 'status' },
            [UsageEventProperties.TARGET, 0, UsageEventProperties.RESULT],
            [null, 'connexion', null]
          )
          break
        case UsageEventType.KONNECTOR_REFRESH_EVENT:
          await sendAggregatedEventByDay(
            events,
            DaccEvent.KONNECTOR_EVENT_DAILY,
            client,
            { group1: 'slug', group2: 'event_type', group3: 'status' },
            [UsageEventProperties.TARGET, 0, UsageEventProperties.RESULT],
            [null, 'refresh', null]
          )
          break
        case UsageEventType.NAVIGATION_EVENT: