Skip to content
Snippets Groups Projects
initialization.service.ts 24.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • import { Client } from 'cozy-client'
    
    import challengeEntityData from 'db/challengeEntity.json'
    import duelEntityData from 'db/duelEntity.json'
    import ecogestureData from 'db/ecogestureData.json'
    import explorationEntityData from 'db/explorationEntity.json'
    import profileData from 'db/profileData.json'
    import quizEntityData from 'db/quizEntity.json'
    
    import {
    
      ECOGESTURE_DOCTYPE,
    
      QUIZ_DOCTYPE,
    } from 'doctypes'
    import { FluidType } from 'enum/fluid.enum'
    
    import {
      Dataload,
    
      FluidStatus,
      Profile,
      ProfileType,
    
      UserChallenge,
    } from 'models'
    
    import { InitSteps, InitStepsErrors } from 'models/initialisationSteps.model'
    import { ProfileEcogesture } from 'models/profileEcogesture.model'
    import React from 'react'
    import ChallengeService from 'services/challenge.service'
    
    import DuelService from 'services/duel.service'
    
    import EcogestureService from 'services/ecogesture.service'
    
    import ExplorationService from 'services/exploration.service'
    
    import FluidService from 'services/fluid.service'
    import KonnectorStatusService from 'services/konnectorStatus.service'
    import ProfileService from 'services/profile.service'
    import QuizService from 'services/quiz.service'
    
    import { getActualAnalysisDate } from 'utils/date'
    
    import log from 'utils/logger'
    
    import EnvironmentService from './environment.service'
    
    import FluidPricesService from './fluidsPrices.service'
    import ProfileEcogestureService from './profileEcogesture.service'
    import ProfileTypeEntityService from './profileTypeEntity.service'
    import TermsService from './terms.service'
    
    import logger from 'cozy-logger'
    
    import {
      REMOTE_ORG_ECOLYO_AGENT_PRICES,
      REMOTE_ORG_ECOLYO_AGENT_PRICES_REC,
    } from 'doctypes/remote/org.ecolyo.agent.prices'
    
    const cozyLog = logger.namespace('initializationService')
    
    export default class InitializationService {
      private readonly _client: Client
    
      private readonly _setinitStep: React.Dispatch<React.SetStateAction<InitSteps>>
      private readonly _setinitStepError: React.Dispatch<
        React.SetStateAction<InitStepsErrors | null>
      >
    
      constructor(
        _client: Client,
        _setinitStep: React.Dispatch<React.SetStateAction<InitSteps>>,
        _setinitStepError: React.Dispatch<
          React.SetStateAction<InitStepsErrors | null>
        >
      ) {
    
        this._client = _client
    
        this._setinitStep = _setinitStep
        this._setinitStepError = _setinitStepError
    
      }
    
      /*
       * Check if profil exist
       * If not, the profil is created
       * sucess return: profil
       * failure return: null
       */
      public async initProfile(): Promise<Profile | null> {
        const profileService = new ProfileService(this._client)
        try {
    
          this._setinitStep(InitSteps.PROFILE)
    
          const loadedProfile = await profileService.getProfile()
          if (!loadedProfile) {
            // Population with the data
            const { data: newProfile } = await this._client.create(
              PROFILE_DOCTYPE,
              profileData[0]
            )
            if (newProfile) {
              log.info('[Initialization] Profile created')
            } else {
    
              this._setinitStepError(InitStepsErrors.PROFILE_ERROR)
    
              throw new Error('initProfile: Profile not created')
            }
          } else {
            log.info('[Initialization] Profile loaded')
          }
          const updatedProfile = await profileService.updateProfile({
            lastConnectionDate: DateTime.local().setZone('utc', {
              keepLocalTime: true,
            }),
          })
          return updatedProfile
        } catch (error) {
    
          this._setinitStepError(InitStepsErrors.PROFILE_ERROR)
    
          log.error('Initialization error - initProfile: ', error)
          throw error
        }
      }
    
      /*
       * Check if profileType exist
       * If not, the profileType is created
       * sucess return: profileType
       * failure return: null
       */
      public async initProfileType(): Promise<ProfileType | null> {
        const profileTypeEntityService = new ProfileTypeEntityService(this._client)
        try {
    
          const loadedProfileType = await profileTypeEntityService.getProfileType()
          log.info('[Initialization] ProfileType loaded')
    
          return loadedProfileType
        } catch (error) {
    
          this._setinitStepError(InitStepsErrors.PROFILETYPE_ERROR)
    
          log.error('Initialization error - initProfileType: ', error)
          throw error
        }
      }
    
      public async initProfileEcogesture(): Promise<ProfileEcogesture | null> {
        const profileEcogestureService = new ProfileEcogestureService(this._client)
        try {
    
          const loadedProfileEcogesture =
            await profileEcogestureService.getProfileEcogesture()
    
          log.info('[Initialization] ProfileEcogesture loaded')
          return loadedProfileEcogesture
        } catch (error) {
    
          this._setinitStepError(InitStepsErrors.PROFILETYPE_ERROR)
    
          log.error('Initialization error - initProfileEcogesture: ', error)
          throw error
        }
      }
    
    
      public async initEcogesture(hash: string): Promise<string> {
    
        this._setinitStep(InitSteps.ECOGESTURE)
    
        const hashEcogestureType = hashFile(ecogestureData)
        const ecogestureService = new EcogestureService(this._client)
        // Populate data if none ecogesture exists
    
        const loadedEcogestures = await ecogestureService.getAllEcogestures(
          undefined,
          true
        )
    
        if (!loadedEcogestures || loadedEcogestures?.length === 0) {
    
          // Populate the doctype with data
          try {
            for (const ecogesture of ecogestureData) {
              await this._client.create(ECOGESTURE_DOCTYPE, ecogesture)
            }
            // Check of created document based on count
            const checkCount = await ecogestureService.getAllEcogestures()
    
            if (!checkCount || checkCount?.length !== ecogestureData.length) {
    
              this._setinitStepError(InitStepsErrors.ECOGESTURE_ERROR)
    
              throw new Error(
                'initEcogesture: Created ecogesture type entities does not match'
              )
            }
    
            log.info('[Initialization] Ecogesture list created')
    
            return hashEcogestureType
          } catch (error) {
    
            this._setinitStepError(InitStepsErrors.ECOGESTURE_ERROR)
    
            log.error('Initialization error - initEcogesture: ', error)
            throw error
          }
        }
        // Update if the hash is not the same as the one from profile
        if (hash !== hashEcogestureType) {
          // Update the doctype
          try {
            // Deletion of all documents
            await ecogestureService.deleteAllEcogestures()
            // Population with the data
    
            for (const [index, ecogesture] of ecogestureData.entries()) {
              const updateEcogesture = loadedEcogestures[index]
                ? {
                    ...ecogesture,
                    objective: loadedEcogestures[index].objective ? true : false,
                    doing: loadedEcogestures[index].doing ? true : false,
    
                    viewedInSelection: loadedEcogestures[index].viewedInSelection
                      ? true
                      : false,
    
                  }
                : ecogesture
              await this._client.create(ECOGESTURE_DOCTYPE, updateEcogesture)
    
            }
            // Check of created document based on count
            const checkCount = await ecogestureService.getAllEcogestures()
    
            if (!checkCount || checkCount?.length !== ecogestureData.length) {
    
              this._setinitStepError(InitStepsErrors.ECOGESTURE_ERROR)
    
              throw new Error(
                'initEcogesture: Created ecogesture type entities does not match'
              )
            }
            log.info('[Initialization] Ecogesture updated')
            return hashEcogestureType
          } catch (error) {
    
            this._setinitStepError(InitStepsErrors.ECOGESTURE_ERROR)
    
            log.error('Initialization error - initEcogesture: ', error)
            throw error
          }
        } else {
          // Doctype already up to date
    
          log.info('[Initialization]  Ecogesture already up-to-date')
    
          return hashEcogestureType
        }
      }
    
    
      public async initFluidPrices(): Promise<boolean> {
    
        const fpService = new FluidPricesService(this._client)
        // Populate data if none ecogesture exists
        const loadedPrices = await fpService.getAllPrices()
    
        if (loadedPrices?.length) {
    
          log.info('[Initialization] FluidPrices db already created')
          return true
        } else {
    
            const fluidTypes: FluidType[] = [
              FluidType.ELECTRICITY,
              FluidType.WATER,
              FluidType.GAS,
            ]
            const allPrices: FluidPrice[] = []
            const env = new EnvironmentService()
            const remoteUrl = env.isProduction()
    
              ? REMOTE_ORG_ECOLYO_AGENT_PRICES
              : REMOTE_ORG_ECOLYO_AGENT_PRICES_REC
    
    
            for (const fluid of fluidTypes) {
              const prices = await this._client
                .getStackClient()
                .fetchJSON('GET', `${remoteUrl}?fluidtype=${fluid}`)
              allPrices.push(...prices)
    
            for (const price of allPrices) {
              await fpService.createPrice(price)
    
            log.info('[Initialization] FluidPrices db created successfully')
            return true
          } catch (err) {
            log.error('Initialization error - initFluidPrices: ', err)
    
            cozyLog('error', `Initialization error - initFluidPrices: ${err}`)
    
      public async initChallengeEntity(hash: string): Promise<string> {
    
        this._setinitStep(InitSteps.CHALLENGES)
    
        const challengeHash = hashFile(challengeEntityData)
        const challengeService = new ChallengeService(this._client)
        // Populate data if none challengeEntity exists
    
        const loadedChallengeEntity =
          await challengeService.getAllChallengeEntities()
    
        if (!loadedChallengeEntity || loadedChallengeEntity?.length === 0) {
    
          // Populate the doctype with data
          try {
            for (const challengeEntity of challengeEntityData) {
              await this._client.create(CHALLENGE_DOCTYPE, challengeEntity)
            }
            // Check of created document
            const checkCount = await challengeService.getAllChallengeEntities()
    
            if (!checkCount || checkCount?.length !== challengeEntityData.length) {
    
              this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
              throw new Error(
                'initChallengeEntity: Created challenge entities does not match'
              )
            }
            log.info('[Initialization] Challenge entities created')
            return challengeHash
          } catch (error) {
    
            this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
            log.error('Initialization error - initChallengeEntity: ', error)
            throw error
          }
        }
        // Update if the hash is not the same as the one from profile
        if (hash !== challengeHash) {
          // Update the doctype
          try {
            // Deletion of all documents
            await challengeService.deleteAllChallengeEntities()
            // Population with the data
            for (const challengeEntity of challengeEntityData) {
              await this._client.create(CHALLENGE_DOCTYPE, challengeEntity)
            }
            // Check of created document
            const checkCount = await challengeService.getAllChallengeEntities()
    
            if (!checkCount || checkCount?.length !== challengeEntityData.length) {
    
              this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
              throw new Error(
                'initChallengeEntity: Created challenge entities does not match'
              )
            }
            log.info('[Initialization] Challenge entities updated')
            return challengeHash
          } catch (error) {
    
            this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
            log.error('Initialization error - initChallengeEntity: ', error)
            throw error
          }
        } else {
          // Doctype already up to date
          log.info('[Initialization] Challenge Entity loaded')
          return challengeHash
        }
      }
    
      public async initDuelEntity(hash: string): Promise<string> {
        const hashDuelEntity = hashFile(duelEntityData)
        const duelService = new DuelService(this._client)
        // Populate data if none DuelEntity exists
        const loadedDuelTypes = await duelService.getAllDuelEntities()
    
        if (!loadedDuelTypes || loadedDuelTypes?.length === 0) {
    
          // Populate the doctype with data
          try {
            for (const duelEntity of duelEntityData) {
              await this._client.create(DUEL_DOCTYPE, duelEntity)
            }
            // Check of created document
            const checkCount = await duelService.getAllDuelEntities()
    
            if (!checkCount || checkCount?.length !== duelEntityData.length) {
    
              this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
              throw new Error(
                'initDuelEntity: Created duel entities does not match'
              )
            }
            log.info('[Initialization] UserDuel entities created')
            return hashDuelEntity
          } catch (error) {
    
            this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
            log.error('Initialization error - initDuelEntity: ', error)
            throw error
          }
        }
        // Update if the hash is not the same as the one from profile
        if (hash !== hashDuelEntity) {
          // Update the doctype
          try {
            // Deletion of all documents
            await duelService.deleteAllDuelEntities()
            // Population with the data
            for (const duelEntity of duelEntityData) {
              await this._client.create(DUEL_DOCTYPE, duelEntity)
            }
            // Check of created document
            const checkCount = await duelService.getAllDuelEntities()
    
            if (!checkCount || checkCount?.length !== duelEntityData.length) {
    
              this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
              throw new Error(
                'initDuelEntity: Created duel entities does not match'
              )
            }
            log.info('[Initialization] UserDuel entities updated')
            return hashDuelEntity
          } catch (error) {
    
            this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
            log.error('Initialization error - initDuelEntity: ', error)
            throw error
          }
        } else {
          // Doctype already up to date
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
          log.info('[Initialization] Duel Entity loaded')
    
          return hashDuelEntity
        }
      }
    
      public async initQuizEntity(hash: string): Promise<string> {
        const quizHash = hashFile(quizEntityData)
        const quizService = new QuizService(this._client)
        // Populate data if none quizEntity exists
        const loadedQuizEntity = await quizService.getAllQuizEntities()
    
        if (!loadedQuizEntity || loadedQuizEntity?.length === 0) {
    
          // Populate the doctype with data
          try {
            for (const quizEntity of quizEntityData) {
              await this._client.create(QUIZ_DOCTYPE, quizEntity)
            }
            // Check of created document
            const checkCount = await quizService.getAllQuizEntities()
    
            if (!checkCount || checkCount?.length !== quizEntityData.length) {
    
              this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
              throw new Error(
                'initQuizEntity: Created quiz entities does not match'
              )
            }
    
            log.info('[Initialization] Quiz entities created')
            return quizHash
          } catch (error) {
    
            this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
            log.error('Initialization error - initQuizEntity: ', error)
            throw error
          }
        }
        // Update if the hash is not the same as the one from profile
        if (hash !== quizHash) {
          // Update the doctype
          try {
            // Deletion of all documents
            await quizService.deleteAllQuizEntities()
            // Population with the data
            for (const quizEntity of quizEntityData) {
              await this._client.create(QUIZ_DOCTYPE, quizEntity)
            }
            // Check of created document
            const checkCount = await quizService.getAllQuizEntities()
    
            if (!checkCount || checkCount?.length !== quizEntityData.length) {
    
              this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
              throw new Error(
                'initQuizEntity: Created quiz entities does not match'
              )
            }
            log.info('[Initialization] Quiz entities updated')
            return quizHash
          } catch (error) {
    
            this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
            log.error('Initialization error - initQuizEntity: ', error)
            throw error
          }
        } else {
          // Doctype already up to date
          log.info('[Initialization] Quiz Entity loaded')
          return quizHash
        }
      }
    
      public async initExplorationEntity(hash: string): Promise<string> {
        const explorationHash = hashFile(explorationEntityData)
        const explorationService = new ExplorationService(this._client)
        // Populate data if none explorationEntity exists
    
        const loadedExplorationEntity =
          await explorationService.getAllExplorationEntities()
    
        if (!loadedExplorationEntity || loadedExplorationEntity?.length === 0) {
    
          // Populate the doctype with data
          try {
            for (const explorationEntity of explorationEntityData) {
              await this._client.create(EXPLORATION_DOCTYPE, explorationEntity)
            }
            // Check of created document
            const checkCount = await explorationService.getAllExplorationEntities()
            if (
              !checkCount ||
    
              checkCount?.length !== explorationEntityData.length
    
              this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
              throw new Error(
                'initExplorationEntity: Created exploration entities does not match'
              )
            }
            log.info('[Initialization] Exploration entities created')
            return explorationHash
          } catch (error) {
    
            this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
            log.error('Initialization error - initExplorationEntity: ', error)
            throw error
          }
        }
        // Update if the hash is not the same as the one from profile
        if (hash !== explorationHash) {
          // Update the doctype
          try {
            // Deletion of all documents
            await explorationService.deleteAllExplorationEntities()
            // Population with the data
            for (const explorationEntity of explorationEntityData) {
              await this._client.create(EXPLORATION_DOCTYPE, explorationEntity)
            }
            // Check of created document
            const checkCount = await explorationService.getAllExplorationEntities()
            if (
              !checkCount ||
    
              checkCount?.length !== explorationEntityData.length
    
              this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
              throw new Error(
                'initExplorationEntity: Created exploration entities does not match'
              )
            }
            log.info('[Initialization] Exploration entities updated')
            return explorationHash
          } catch (error) {
    
            this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
            log.error('Initialization error - initExplorationEntity: ', error)
            throw error
          }
        } else {
          // Doctype already up to date
          log.info('[Initialization] Exploration Entity loaded')
          return explorationHash
        }
      }
    
    
      public async initAnalysis(profile: Profile): Promise<{
    
        monthlyAnalysisDate: DateTime
        haveSeenLastAnalysis: boolean
      }> {
        try {
          const actualAnalysisDate = getActualAnalysisDate()
          if (
            profile.monthlyAnalysisDate &&
            actualAnalysisDate <= profile.monthlyAnalysisDate
          ) {
            return {
              monthlyAnalysisDate: profile.monthlyAnalysisDate,
              haveSeenLastAnalysis: profile.haveSeenLastAnalysis,
            }
          } else {
            log.info('[Initialization] Analysis information from profile updated')
            return {
              monthlyAnalysisDate: actualAnalysisDate,
              haveSeenLastAnalysis: profile.isFirstConnection ? true : false,
            }
          }
        } catch (error) {
    
          this._setinitStepError(InitStepsErrors.ANALYSIS_ERROR)
    
          log.error('Initialization error - initAnalysis: ', error)
          throw error
        }
      }
    
      /*
       * Check if FluidTypes exist
       * sucess return: FluidType[]
       * failure throw error
       */
      public async initFluidTypes(): Promise<FluidType[]> {
        const kss = new KonnectorStatusService(this._client)
        try {
          const fluidtypes = await kss.getKonnectorAccountStatus()
          if (fluidtypes) {
            log.info('[Initialization] Fluid Types loaded')
            return fluidtypes
          } else {
    
            this._setinitStepError(InitStepsErrors.CONSOS_ERROR)
    
            throw new Error('initFluidTypes: FluidTypes not found')
          }
        } catch (error) {
    
          this._setinitStepError(InitStepsErrors.CONSOS_ERROR)
    
          log.error('Initialization error - initFluidTypes: ', error)
          throw error
        }
      }
    
      /*
       * For each fluid get the trigger status and the last data date
       * sucess return: FluidStatus[]
       * failure throw error
       */
      public async initFluidStatus(): Promise<FluidStatus[]> {
        const fs = new FluidService(this._client)
        try {
    
          this._setinitStep(InitSteps.CONSOS)
    
          const fluidStatus = await fs.getFluidStatus()
          if (fluidStatus) {
            log.info('[Initialization] Fluid Status loaded')
            return fluidStatus
          } else {
    
            this._setinitStepError(InitStepsErrors.CONSOS_ERROR)
    
            throw new Error('initFluidStatus: fluidStatus not found')
          }
        } catch (error) {
    
          this._setinitStepError(InitStepsErrors.CONSOS_ERROR)
    
          log.error('Initialization error - initFluidStatus: ', error)
          throw error
        }
      }
    
      /*
       * Build the userChallengeList
       * sucess return: UserChallenge[]
       * failure throw error
       */
      public async initUserChallenges(
        fluidStatus: FluidStatus[]
      ): Promise<UserChallenge[]> {
        const challengeService = new ChallengeService(this._client)
        try {
          const userChallengeList = await challengeService.buildUserChallengeList(
            fluidStatus
          )
          if (userChallengeList) {
            log.info('[Initialization] Challenges loaded')
            return userChallengeList
          } else {
    
            this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
            throw new Error('initUserChallenges: userChallengeList not found')
          }
        } catch (error) {
    
          this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
          log.error('Initialization error - initUserChallenges: ', error)
          throw error
        }
      }
    
      /*
       * Retrieve dataloads for ongoing duel
       * sucess return: UserChallenge, Dataload[]
       * failure throw error
       */
    
      public async initDuelProgress(userChallenge: UserChallenge): Promise<{
    
        updatedUserChallenge: UserChallenge
        dataloads: Dataload[]
      }> {
        const challengeService = new ChallengeService(this._client)
        try {
    
          const { updatedUserChallenge, dataloads } =
            await challengeService.initChallengeDuelProgress(userChallenge)
    
          return { updatedUserChallenge, dataloads }
        } catch (error) {
    
          this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
          log.error('Initialization error: ', error)
          throw error
        }
      }
    
      public async initConsent(): Promise<TermsStatus> {
        const termsStatus: TermsStatus = {
          accepted: false,
          versionType: 'init',
        }
    
        try {
    
          this._setinitStep(InitSteps.CONSENT)
    
          const termService = new TermsService(this._client)
          const isUpToDate = await termService.isConsentVersionUpToDate()
    
          const lastTerm = await termService.getLastTerm()
    
          if (lastTerm) {
            if (isUpToDate) {
              const isLastConsentValidated = await termService.isLastTermValidated()
              if (isLastConsentValidated) {
                termsStatus.accepted = true
                termsStatus.versionType = 'init'
                log.info(
                  '[Initialization] Last Consent successfully loaded and valid'
                )
              } else {
                termsStatus.versionType = 'init'
                termsStatus.accepted = false
                log.info('[Initialization] Consent not up-to-date')
              }
    
            } else {
    
              const versionType = await termService.getTermsVersionType()
              if (versionType === 'minor') {
                termsStatus.accepted = false
                termsStatus.versionType = 'minor'
                log.info('[Initialization] Minor Terms update detected')
              } else {
                termsStatus.accepted = false
                termsStatus.versionType = 'major'
                log.info('[Initialization] Major Terms update detected')
              }
    
            }
          } else {
    
            termsStatus.accepted = false
            termsStatus.versionType = 'init'
            log.info('[Initialization] Init first terms')
    
        } catch (error) {
    
          this._setinitStepError(InitStepsErrors.CONSENT_ERROR)
    
          log.error('Initialization error - initConsent: ', error)
          throw error
        }
      }
    }