From 8f3af96aa800963ae1a390d570bcec5b0c89ba5f Mon Sep 17 00:00:00 2001
From: "guilhem.carron" <gcarron@grandlyon.com>
Date: Mon, 25 Apr 2022 16:59:37 +0200
Subject: [PATCH] test new usageEvent

---
 scripts/exportDataFromInstance.bat            |  13 +++
 scripts/importOrDropDataFromInstance.bat      |  14 +++
 src/components/Connection/ConnectionOAuth.tsx |   2 +
 .../ConnectionOAuthNoPartnerAccount.tsx       |   6 +-
 .../ConnectionOAuthWithPartnerAccount.tsx     |   5 +-
 src/components/Connection/FormLogin.tsx       |   8 ++
 src/components/Connection/FormOAuth.tsx       |  19 +++-
 src/components/Konnector/KonnectorModal.tsx   |   8 +-
 .../Konnector/KonnectorViewerCard.tsx         | 103 +++++++++---------
 src/db/ecogestureData.json                    |   6 +-
 src/enum/dacc.enum.ts                         |   1 +
 src/enum/usageEvent.enum.ts                   |   1 +
 src/services/partnersInfo.service.ts          |   7 +-
 src/services/usageEvent.service.ts            |  41 +++++++
 src/targets/services/aggregatorUsageEvents.ts |  92 +++++++++++++---
 15 files changed, 244 insertions(+), 82 deletions(-)
 create mode 100644 scripts/exportDataFromInstance.bat
 create mode 100644 scripts/importOrDropDataFromInstance.bat

diff --git a/scripts/exportDataFromInstance.bat b/scripts/exportDataFromInstance.bat
new file mode 100644
index 000000000..c92d1ef3d
--- /dev/null
+++ b/scripts/exportDataFromInstance.bat
@@ -0,0 +1,13 @@
+@echo off
+echo This script to export data from a cozy doctype
+echo Please provide cozysessid (can be found after connection in browser dev tool)
+set /p token="CozySessid ? : "
+echo Please select your cozy instance name : 'https://mon.instance.cozygrandlyon.cloud/'
+set /p instance="Instance ? : "
+echo Please select the doctype name you wish to export (ex : com.grandlyon.egl.day)
+set /p name="Choice ? : "
+echo Please select the file name for exported data
+set /p filename="Filename ? : "
+echo Execute ACH -t %token% -u %instance% export %name% %filename%
+echo Do not forget to delete tour token (AAAAA....etc.json) before executing another ACH command !
+ACH -t %token% -u %instance% export %name% %filename%
diff --git a/scripts/importOrDropDataFromInstance.bat b/scripts/importOrDropDataFromInstance.bat
new file mode 100644
index 000000000..f56423f8e
--- /dev/null
+++ b/scripts/importOrDropDataFromInstance.bat
@@ -0,0 +1,14 @@
+@echo off
+echo This script allows you to edit data in cozy alpha
+echo Please provide cozysessid (can be found after connection in browser dev tool)
+set /p token="CozySessid ? : "
+echo Please select an action between drop and import
+set /p action="Action ? : "
+echo Please select your cozy instance name : 'https://mon.instance.cozygrandlyon.cloud/'
+set /p instance="Instance ? : "
+echo Please select the doctype name you wish to drop (ex : com.grandlyon.egl.day) or the pathh to the data file you wish to import
+set /p name="Choice ? : "
+echo Execute ACH -t %token% -u %instance% %action% %name%
+echo Do not forget to delete tour token (AAAAA....etc.json) before executing another ACH command !
+
+ACH -t %token% -u %instance% %action% %name%
diff --git a/src/components/Connection/ConnectionOAuth.tsx b/src/components/Connection/ConnectionOAuth.tsx
index 1bd69c164..97e56a077 100644
--- a/src/components/Connection/ConnectionOAuth.tsx
+++ b/src/components/Connection/ConnectionOAuth.tsx
@@ -101,6 +101,7 @@ const ConnectionOAuth: React.FC<ConnectionOAuthProps> = ({
         <ConnectionOAuthWithPartnerAccount
           konnectorSlug={konnectorSlug}
           konnector={konnector}
+          fluidStatus={fluidStatus}
           handleSuccess={handleSuccess}
           togglePartnerConnectionModal={togglePartnerConnectionModal}
         />
@@ -108,6 +109,7 @@ const ConnectionOAuth: React.FC<ConnectionOAuthProps> = ({
         <ConnectionOAuthNoPartnerAccount
           konnectorSlug={konnectorSlug}
           konnector={konnector}
+          fluidStatus={fluidStatus}
           handleSuccess={handleSuccess}
           togglePartnerConnectionModal={togglePartnerConnectionModal}
         />
diff --git a/src/components/Connection/ConnectionOAuthNoPartnerAccount.tsx b/src/components/Connection/ConnectionOAuthNoPartnerAccount.tsx
index 807e20753..12df683d8 100644
--- a/src/components/Connection/ConnectionOAuthNoPartnerAccount.tsx
+++ b/src/components/Connection/ConnectionOAuthNoPartnerAccount.tsx
@@ -1,7 +1,7 @@
 import React from 'react'
 import { useI18n } from 'cozy-ui/transpiled/react/I18n'
 import './connectionOAuth.scss'
-import { Konnector } from 'models'
+import { FluidStatus, Konnector } from 'models'
 import FormOAuth from 'components/Connection/FormOAuth'
 import Button from '@material-ui/core/Button'
 
@@ -10,6 +10,7 @@ interface ConnectionOAuthNoPartnerAccountProps {
   konnector: Konnector | null
   handleSuccess: (accountId: string) => Promise<void>
   togglePartnerConnectionModal: () => void
+  fluidStatus: FluidStatus
 }
 
 const ConnectionOAuthNoPartnerAccount = ({
@@ -17,6 +18,7 @@ const ConnectionOAuthNoPartnerAccount = ({
   konnector,
   handleSuccess,
   togglePartnerConnectionModal,
+  fluidStatus,
 }: ConnectionOAuthNoPartnerAccountProps) => {
   const { t } = useI18n()
 
@@ -53,9 +55,9 @@ const ConnectionOAuthNoPartnerAccount = ({
           konnector={konnector}
           onSuccess={handleSuccess}
           highlightedStyle={false}
+          fluidStatus={fluidStatus}
         />
       </div>
-
       <div className="koauthform-infotext text-16-italic">
         {t('auth.' + `${konnectorSlug}` + '.no_account.info')}
       </div>
diff --git a/src/components/Connection/ConnectionOAuthWithPartnerAccount.tsx b/src/components/Connection/ConnectionOAuthWithPartnerAccount.tsx
index c0d9bbb26..d0a81c48e 100644
--- a/src/components/Connection/ConnectionOAuthWithPartnerAccount.tsx
+++ b/src/components/Connection/ConnectionOAuthWithPartnerAccount.tsx
@@ -1,7 +1,7 @@
 import React, { useCallback } from 'react'
 import { useI18n } from 'cozy-ui/transpiled/react/I18n'
 import './connectionOAuth.scss'
-import { Konnector } from 'models'
+import { FluidStatus, Konnector } from 'models'
 import FormOAuth from 'components/Connection/FormOAuth'
 import Button from '@material-ui/core/Button'
 
@@ -10,6 +10,7 @@ interface ConnectionOAuthWithPartnerAccountProps {
   konnector: Konnector | null
   handleSuccess: (accountId: string) => Promise<void>
   togglePartnerConnectionModal: () => void
+  fluidStatus: FluidStatus
 }
 
 const ConnectionOAuthWithPartnerAccount = ({
@@ -17,6 +18,7 @@ const ConnectionOAuthWithPartnerAccount = ({
   konnector,
   handleSuccess,
   togglePartnerConnectionModal,
+  fluidStatus,
 }: ConnectionOAuthWithPartnerAccountProps) => {
   const { t } = useI18n()
 
@@ -41,6 +43,7 @@ const ConnectionOAuthWithPartnerAccount = ({
           konnector={konnector}
           onSuccess={handleSuccess}
           highlightedStyle={true}
+          fluidStatus={fluidStatus}
         />
       </div>
       {konnectorSlug === 'grdfgrandlyon' && (
diff --git a/src/components/Connection/FormLogin.tsx b/src/components/Connection/FormLogin.tsx
index 9bfd1ddb1..e21a693bc 100644
--- a/src/components/Connection/FormLogin.tsx
+++ b/src/components/Connection/FormLogin.tsx
@@ -72,6 +72,7 @@ const FormLogin: React.FC<FormLoginProps> = ({
   const sendUsageEventError = async (
     konnectorSlug: string
   ): Promise<UsageEvent> => {
+    console.log('SHOULD SEND CONNECTION EVENT EGL')
     return UsageEventService.addEvent(client, {
       type: UsageEventType.KONNECTOR_CONNECT_EVENT,
       target: konnectorSlug,
@@ -82,6 +83,13 @@ const FormLogin: React.FC<FormLoginProps> = ({
   const connect = async () => {
     const connectionService = new ConnectionService(client)
     try {
+      // If first connexion, send the usage event
+      console.log('should CREATE ATTEMPT')
+      await UsageEventService.addEvent(client, {
+        type: UsageEventType.KONNECTOR_ATTEMPT_EVENT,
+        target: konnectorSlug,
+        result: 'error',
+      })
       const {
         account: _account,
         trigger: _trigger,
diff --git a/src/components/Connection/FormOAuth.tsx b/src/components/Connection/FormOAuth.tsx
index 3be62fdb0..fc5718c96 100644
--- a/src/components/Connection/FormOAuth.tsx
+++ b/src/components/Connection/FormOAuth.tsx
@@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useState } from 'react'
 import { useI18n } from 'cozy-ui/transpiled/react/I18n'
 import { useClient } from 'cozy-client'
 import './auth.scss'
-import { Konnector } from 'models'
+import { FluidStatus, Konnector } from 'models'
 import { OAuthWindow } from 'cozy-harvest-lib/dist/components/OAuthWindow'
 import Button from '@material-ui/core/Button'
 import StyledIcon from 'components/CommonKit/Icon/StyledIcon'
@@ -11,17 +11,21 @@ import { getPartnerPicto } from 'utils/picto'
 import { useDispatch, useSelector } from 'react-redux'
 import { AppStore } from 'store'
 import { setShouldRefreshConsent } from 'store/global/global.actions'
+import { UsageEventType } from 'enum/usageEvent.enum'
+import UsageEventService from 'services/usageEvent.service'
 
 interface FormOAuthProps {
   konnector: Konnector | null
   onSuccess: Function
   highlightedStyle?: boolean
+  fluidStatus: FluidStatus
 }
 
 const FormOAuth: React.FC<FormOAuthProps> = ({
   konnector,
   onSuccess,
   highlightedStyle = true,
+  fluidStatus,
 }: FormOAuthProps) => {
   const IDLE = 'idle'
   const WAITING = 'waiting'
@@ -39,9 +43,18 @@ const FormOAuth: React.FC<FormOAuthProps> = ({
     dispatch(setShouldRefreshConsent(false))
   }, [dispatch])
 
-  const startOAuth = useCallback(() => {
+  const startOAuth = useCallback(async () => {
+    // If first connexion, send the usage event
+    if (konnector && konnector.slug && fluidStatus.lastDataDate === null) {
+      console.log('should CREATE ATTEMPT')
+      await UsageEventService.addEvent(client, {
+        type: UsageEventType.KONNECTOR_ATTEMPT_EVENT,
+        target: konnector.slug,
+        result: 'error',
+      })
+    }
     setStatus(WAITING)
-  }, [])
+  }, [client, fluidStatus.lastDataDate, konnector])
 
   const handleAccountId = useCallback(
     (accountId: string) => {
diff --git a/src/components/Konnector/KonnectorModal.tsx b/src/components/Konnector/KonnectorModal.tsx
index 9723f507a..53a6dfeea 100644
--- a/src/components/Konnector/KonnectorModal.tsx
+++ b/src/components/Konnector/KonnectorModal.tsx
@@ -36,7 +36,7 @@ interface KonnectorModalProps {
   state: string | null
   error: string | null
   fluidType: FluidType
-  handleCloseClick: () => void
+  handleCloseClick: (isSuccess?: boolean) => void
 }
 
 const KonnectorModal: React.FC<KonnectorModalProps> = ({
@@ -77,7 +77,7 @@ const KonnectorModal: React.FC<KonnectorModalProps> = ({
       open={open}
       disableBackdropClick
       disableEscapeKeyDown
-      onClose={handleCloseClick}
+      onClose={() => handleCloseClick(state === SUCCESS_EVENT && true)}
       aria-labelledby={'accessibility-title'}
       classes={{
         root: 'modal-root',
@@ -184,7 +184,9 @@ const KonnectorModal: React.FC<KonnectorModalProps> = ({
               {state !== LOGIN_SUCCESS_EVENT && (
                 <Button
                   aria-label={t('konnector_modal.accessibility.button_close')}
-                  onClick={() => handleCloseClick()}
+                  onClick={() =>
+                    handleCloseClick(state === SUCCESS_EVENT && true)
+                  }
                   classes={{
                     root: 'btn-highlight',
                     label: 'text-16-bold',
diff --git a/src/components/Konnector/KonnectorViewerCard.tsx b/src/components/Konnector/KonnectorViewerCard.tsx
index d4c809818..ac957f988 100644
--- a/src/components/Konnector/KonnectorViewerCard.tsx
+++ b/src/components/Konnector/KonnectorViewerCard.tsx
@@ -179,46 +179,54 @@ const KonnectorViewerCard: React.FC<KonnectorViewerCardProps> = ({
     dispatch,
   ])
 
-  const handleConnectionEnd = useCallback(async () => {
-    if (
-      account &&
-      konnectorErrorDescription === 'LOGIN_FAILED' &&
-      fluidStatus !== null &&
-      fluidStatus.connection.account !== null &&
-      fluidStatus.connection.account.auth !== undefined &&
-      fluidStatus.connection.account.auth.login
-    ) {
-      fluidStatus.connection.konnectorConfig.lastKnownCredentials =
+  const handleConnectionEnd = useCallback(
+    async (isSuccess?: boolean) => {
+      if (
+        account &&
+        konnectorErrorDescription === 'LOGIN_FAILED' &&
+        fluidStatus !== null &&
+        fluidStatus.connection.account !== null &&
+        fluidStatus.connection.account.auth !== undefined &&
         fluidStatus.connection.account.auth.login
-      const accountService = new AccountService(client)
-      await accountService.deleteAccount(account)
-      await handleAccountDeletion()
-    } else {
-      if (updatedFluidStatus.length > 0) {
-        const partnersInfo: PartnersInfo = await partnersInfoService.getPartnersInfo()
-        const _updatedFluidStatus: FluidStatus[] = await fluidService.getFluidStatus(
-          partnersInfo
-        )
-        dispatch(setFluidStatus(_updatedFluidStatus))
+      ) {
+        fluidStatus.connection.konnectorConfig.lastKnownCredentials =
+          fluidStatus.connection.account.auth.login
+        const accountService = new AccountService(client)
+        await accountService.deleteAccount(account)
+        await handleAccountDeletion()
+      } else {
+        if (updatedFluidStatus.length > 0) {
+          const partnersInfo: PartnersInfo = await partnersInfoService.getPartnersInfo()
+          const _updatedFluidStatus: FluidStatus[] = await fluidService.getFluidStatus(
+            partnersInfo
+          )
+          dispatch(setFluidStatus(_updatedFluidStatus))
+        }
       }
-    }
-    setActive(false)
-    setOpenModal(false)
-    // TODO null state seems to be read before modal closing and display a success icon in modal
-    setKonnectorState(null)
-    setKonnectorErrorDescription(null)
-  }, [
-    account,
-    client,
-    dispatch,
-    fluidService,
-    fluidStatus,
-    handleAccountDeletion,
-    konnectorErrorDescription,
-    partnersInfoService,
-    setActive,
-    updatedFluidStatus.length,
-  ])
+      if (isSuccess) {
+        console.log('SUCCEESSSS')
+        await UsageEventService.udpateConnectionAttemptEvent(client, fluidSlug)
+      }
+      setActive(false)
+      setOpenModal(false)
+      // TODO null state seems to be read before modal closing and display a success icon in modal
+      setKonnectorState(null)
+      setKonnectorErrorDescription(null)
+    },
+    [
+      account,
+      client,
+      dispatch,
+      fluidService,
+      fluidStatus,
+      handleAccountDeletion,
+      konnectorErrorDescription,
+      partnersInfoService,
+      setActive,
+      updatedFluidStatus.length,
+      fluidSlug,
+    ]
+  )
 
   const sendUsageEventSuccess = useCallback(
     async (
@@ -292,8 +300,10 @@ const KonnectorViewerCard: React.FC<KonnectorViewerCardProps> = ({
         dispatch(
           updatedFluidConnection(fluidStatus.fluidType, updatedConnection)
         )
-        await refreshChallengeState()
-        await updateGlobalFluidStatus()
+        Promise.all([
+          await refreshChallengeState(),
+          await updateGlobalFluidStatus(),
+        ])
         setKonnectorState(_state)
       }
     },
@@ -322,22 +332,16 @@ const KonnectorViewerCard: React.FC<KonnectorViewerCardProps> = ({
         const connectionFlow = new ConnectionFlow(client, trigger, konnector)
         await connectionFlow.launch()
         connectionFlow.jobWatcher.on(ERROR_EVENT, () => {
-          if (subscribed) {
-            sendUsageEventError(fluidSlug, fluidStatus.lastDataDate === null)
-          }
+          sendUsageEventError(fluidSlug, fluidStatus.lastDataDate === null)
           setKonnectorErrorDescription(connectionFlow.jobWatcher.on()._error)
           callbackResponse(ERROR_EVENT)
         })
         connectionFlow.jobWatcher.on(LOGIN_SUCCESS_EVENT, () => {
-          if (subscribed) {
-            sendUsageEventSuccess(fluidSlug, fluidStatus.lastDataDate === null)
-          }
+          sendUsageEventSuccess(fluidSlug, fluidStatus.lastDataDate === null)
           callbackResponse(LOGIN_SUCCESS_EVENT)
         })
         connectionFlow.jobWatcher.on(SUCCESS_EVENT, () => {
-          if (subscribed) {
-            sendUsageEventSuccess(fluidSlug, fluidStatus.lastDataDate === null)
-          }
+          sendUsageEventSuccess(fluidSlug, fluidStatus.lastDataDate === null)
           callbackResponse(SUCCESS_EVENT)
         })
       }
@@ -365,7 +369,6 @@ const KonnectorViewerCard: React.FC<KonnectorViewerCardProps> = ({
     sendUsageEventError,
     fluidSlug,
     sendUsageEventSuccess,
-    fluidStatus.connection,
   ])
 
   useEffect(() => {
diff --git a/src/db/ecogestureData.json b/src/db/ecogestureData.json
index 272bb98c8..6b12e829f 100644
--- a/src/db/ecogestureData.json
+++ b/src/db/ecogestureData.json
@@ -1638,7 +1638,7 @@
     "fluidTypes": [0, 2],
     "shortName": "Tuyaux bien au chaud",
     "longName": "J'isole les tuyaux de mon circuit de chauffage hydraulique.",
-    "longDescription": "Isolez les circuits de distribution d‘eau de chauffage et d’eau chaude sanitaire dans les locaux non chauffés ou les faux-plafonds. Vous limiterez ainsi les déperditions de chaleur et améliorerez la protection du circuit contre le gel. Cela peut réduire de 10 % la consommation. Le plus simple est d’utiliser des manchons souples en mousse ou en fibres minérales. On peut aussi utiliser des isolants à base de laine ou de chanvre",
+    "longDescription": "Isolez les circuits de distribution d'eau de chauffage et d'eau chaude sanitaire dans les locaux non chauffés ou les faux-plafonds. Vous limiterez ainsi les déperditions de chaleur et améliorerez la protection du circuit contre le gel. Cela peut réduire de 10 % la consommation. Le plus simple est d’utiliser des manchons souples en mousse ou en fibres minérales. On peut aussi utiliser des isolants à base de laine ou de chanvre",
     "impactLevel": 6,
     "efficiency": 3,
     "difficulty": 3,
@@ -1684,7 +1684,7 @@
     "fluidTypes": [1],
     "shortName": "Nitro cuvette",
     "longName": "J'installe une chasse d'eau à double vitesse.",
-    "longDescription": "L'installation d'une chasse d'eau double est à la portée de tous. Si les mécanismes sont généralement standard, veillez malgré tout à vérifier avant de l'acheter les dimensions du trou du couvercle dans lequel viendra se positionner le double bouton poussoir, ainsi que la hauteur du réservoir. Reste à suivre le pas à pas suivant : Commencez par couper l'arrivée d'eau et tirez la chasse pour vider le réservoir. Dévissez le bouton de tirage existant et ôtez le couvercle du réservoir. Dévissez l'arrivée d'eau, retirez le robinet flotteur et le mécanisme de la chasse. Dévissez les vis de fixation du réservoir et retirez-le. Changez le joint entre le réservoir et la cuvette, puis revissez le réservoir. Installez le nouveau mécanisme de chasse (à partir de 20€ dans les enseignes de bricolage). Clipsez le flotteur de réglage de la petite chasse, puis le mécanisme au complet. Revissez l'arrivée d'eau. Refermez le couvercle et installez le double bouton poussoir.À défaut, il est possible de réduire le volume de la chasse d’eau grâce à une éco-plaquette ou à une bouteille d’eau pleine placée dans le réservoir. Pour garantir son bon fonctionnement, nettoyez régulièrement le mécanisme de chasse d'eau double, particulièrement si votre eau est très calcaire.",
+    "longDescription": "L'installation d'une chasse d'eau double est à la portée de tous. Si les mécanismes sont généralement standard, veillez malgré tout à vérifier avant de l'acheter les dimensions du trou du couvercle dans lequel viendra se positionner le double bouton poussoir, ainsi que la hauteur du réservoir. Reste à suivre le pas à pas suivant : Commencez par couper l'arrivée d'eau et tirez la chasse pour vider le réservoir. Dévissez le bouton de tirage existant et ôtez le couvercle du réservoir. Dévissez l'arrivée d'eau, retirez le robinet flotteur et le mécanisme de la chasse. Dévissez les vis de fixation du réservoir et retirez-le. Changez le joint entre le réservoir et la cuvette, puis revissez le réservoir. Installez le nouveau mécanisme de chasse (à partir de 20€ dans les enseignes de bricolage). Clipsez le flotteur de réglage de la petite chasse, puis le mécanisme au complet. Revissez l'arrivée d'eau. Refermez le couvercle et installez le double bouton poussoir. À défaut, il est possible de réduire le volume de la chasse d’eau grâce à une éco-plaquette ou à une bouteille d’eau pleine placée dans le réservoir. Pour garantir son bon fonctionnement, nettoyez régulièrement le mécanisme de chasse d'eau double, particulièrement si votre eau est très calcaire.",
     "impactLevel": 5,
     "efficiency": 2.5,
     "difficulty": 3,
@@ -1753,7 +1753,7 @@
     "fluidTypes": [0],
     "shortName": "Blanc Resplendissant",
     "longName": "Je peins mes murs avec des couleurs claires et j'installe des luminaires blancs.",
-    "longDescription": "Cela permet à la lumière naturelle de se répartir plus uniformément dans l’espace et de pénétrer plus profondément dans la pièce grâce aux jeux de réflexions. Cet effet des couleurs se remarque également sur la lumière artificielle : un intérieur foncé amène à doubler voire tripler l’intensité de l’éclairag",
+    "longDescription": "Cela permet à la lumière naturelle de se répartir plus uniformément dans l’espace et de pénétrer plus profondément dans la pièce grâce aux jeux de réflexions. Cet effet des couleurs se remarque également sur la lumière artificielle : un intérieur foncé amène à doubler voire tripler l’intensité de l’éclairage.",
     "impactLevel": 2,
     "efficiency": 1,
     "difficulty": 3,
diff --git a/src/enum/dacc.enum.ts b/src/enum/dacc.enum.ts
index 219da93b8..e05ad2d52 100644
--- a/src/enum/dacc.enum.ts
+++ b/src/enum/dacc.enum.ts
@@ -12,6 +12,7 @@ export enum DaccEvent {
   SUMMARY_SUBSCRIPTION_MONTHLY = 'summary-subscription-monthly',
   FLUID_DATA_GRANULARITY = 'fluid-data-granularity-monthly',
   PARTNER_SUCESS_MONTHLY = 'konnector-attempts-before-success',
+  KONNECTOR_ATTEMPTS_MONTHLY = 'konnector-attempts-monthly',
   CONNECTION_COUNT_MONTHLY = 'connection-count-monthly',
   PROFILE_COUNT_MONTHLY = 'profile-count',
 }
diff --git a/src/enum/usageEvent.enum.ts b/src/enum/usageEvent.enum.ts
index 6968915fc..93cbd57f7 100644
--- a/src/enum/usageEvent.enum.ts
+++ b/src/enum/usageEvent.enum.ts
@@ -1,6 +1,7 @@
 export enum UsageEventType {
   CONNECTION_EVENT = 'ConnectionEvent',
   KONNECTOR_CONNECT_EVENT = 'KonnectorConnectEvent',
+  KONNECTOR_ATTEMPT_EVENT = 'KonnectorAttemptEvent',
   KONNECTOR_REFRESH_EVENT = 'KonnectorRefreshEvent',
   NAVIGATION_EVENT = 'NavigationEvent',
   CONSUMPTION_COMPARE_EVENT = 'ConsumptionCompareEvent',
diff --git a/src/services/partnersInfo.service.ts b/src/services/partnersInfo.service.ts
index 44089bcd3..6cde2383b 100644
--- a/src/services/partnersInfo.service.ts
+++ b/src/services/partnersInfo.service.ts
@@ -5,13 +5,13 @@ import EnvironmentService from './environment.service'
 
 export default class PartnersInfoService {
   private readonly _client: Client
-  private readonly _setinitStepError: React.Dispatch<
+  private readonly _setinitStepError?: React.Dispatch<
     React.SetStateAction<InitStepsErrors | null>
   >
 
   constructor(
     _client: Client,
-    _setinitStepError: React.Dispatch<
+    _setinitStepError?: React.Dispatch<
       React.SetStateAction<InitStepsErrors | null>
     >
   ) {
@@ -35,7 +35,8 @@ export default class PartnersInfoService {
         .fetchJSON('GET', remoteUrl)
       return result as PartnersInfo
     } catch (error) {
-      this._setinitStepError(InitStepsErrors.PARTNERS_ERROR)
+      this._setinitStepError &&
+        this._setinitStepError(InitStepsErrors.PARTNERS_ERROR)
       console.error(error)
       throw new Error("Failed to get partners' info")
     }
diff --git a/src/services/usageEvent.service.ts b/src/services/usageEvent.service.ts
index 0d5cb79a9..b1ee5c7dc 100644
--- a/src/services/usageEvent.service.ts
+++ b/src/services/usageEvent.service.ts
@@ -6,6 +6,7 @@ import {
   MongoSelector,
 } from 'cozy-client'
 import { USAGEEVENT_DOCTYPE } from 'doctypes'
+import { UsageEventType } from 'enum/usageEvent.enum'
 import { DateTime } from 'luxon'
 import {
   AddEventParams,
@@ -60,6 +61,46 @@ export default class UsageEventService {
     return null
   }
 
+  /**
+   *
+   * @param {Client} client
+   * @param {string} konnectorSlug
+   * @returns
+   */
+  static async udpateConnectionAttemptEvent(
+    client: Client,
+    konnectorSlug: string
+  ) {
+    try {
+      //Get last Connection attempt Event
+      const query: QueryDefinition = Q(USAGEEVENT_DOCTYPE)
+        .where({
+          type: UsageEventType.KONNECTOR_ATTEMPT_EVENT,
+          target: konnectorSlug,
+          result: 'error',
+        })
+        .indexFields(['eventDate'])
+        .sortBy([{ eventDate: 'desc' }])
+        .limitBy(1)
+      const {
+        data: [usageEventEntity],
+      }: QueryResult<UsageEventEntity[]> = await client.query(query)
+      console.log('usageEventEntity', usageEventEntity)
+      if (usageEventEntity) {
+        const updatedEvent: UsageEventEntity = {
+          ...usageEventEntity,
+          result: 'success',
+        }
+        const saved = await client.save(updatedEvent)
+        console.log('saved', saved)
+      } else {
+        console.log('NO ATTEMPT EVENT')
+      }
+    } catch (err) {
+      console.error(err)
+    }
+  }
+
   /**
    * updateUsageEventsAggregated
    * @param {Client} client
diff --git a/src/targets/services/aggregatorUsageEvents.ts b/src/targets/services/aggregatorUsageEvents.ts
index 854e74c7a..5aea5bda9 100644
--- a/src/targets/services/aggregatorUsageEvents.ts
+++ b/src/targets/services/aggregatorUsageEvents.ts
@@ -58,17 +58,9 @@ const sendIndicator = async (
     )
 
     // /!\ 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),
-        }
-      )
+    await client.getStackClient().fetchJSON('POST', 'http://localhost:8081', {
+      data: JSON.stringify(indicator),
+    })
     return true
   } catch (error) {
     log(
@@ -805,6 +797,70 @@ const sendKonnectorEvents = async (client: Client) => {
   })
 }
 
+/**
+ * Send the total number of partner connection attempts and the number of success
+ * @param client CozyClient
+ */
+const sendKonnectorAttemptsMonthly = async (client: Client) => {
+  log('info', `sendkonnectorSuccessAndFailCount`)
+  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_REFRESH_EVENT,
+        type: {
+          $or: [
+            UsageEventType.KONNECTOR_REFRESH_EVENT,
+            UsageEventType.KONNECTOR_CONNECT_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)}`)
+
+    //count success and fail
+    const successNumber: number = konnectorEvents.filter(
+      event => event.result == 'success'
+    ).length
+
+    const konnectorAttempts: Indicator = {
+      createdBy: 'ecolyo',
+      measureName: DaccEvent.KONNECTOR_ATTEMPTS_MONTHLY,
+      // eslint-disable-next-line @typescript-eslint/camelcase
+      group1: { fluid_type: slug },
+      group2: { success: successNumber },
+      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,
@@ -1161,13 +1217,15 @@ const AggregatorUsageEvents = async ({
   // Monthly events
   const profile = await new ProfileService(client).getProfile()
   if (
-    profile &&
-    DateTime.local()
-      .setZone('utc', {
-        keepLocalTime: true,
-      })
-      .startOf('day').day === profile.monthlyAnalysisDate.day
+    profile
+    // &&
+    // DateTime.local()
+    //   .setZone('utc', {
+    //     keepLocalTime: true,
+    //   })
+    //   .startOf('day').day === profile.monthlyAnalysisDate.day
   ) {
+    sendKonnectorAttemptsMonthly(client)
     calculateConsumptionVariation(client)
     sendEmailSubscription(client)
     sendHalfHourConsumption(client)
-- 
GitLab