Skip to content
Snippets Groups Projects
initialization.service.ts 22.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • import { Client, Q, QueryDefinition } from 'cozy-client'
    import {
      ECOGESTURE_DOCTYPE,
      PROFILE_DOCTYPE,
      EGL_DAY_DOCTYPE,
      EGL_MONTH_DOCTYPE,
      EGL_YEAR_DOCTYPE,
      ENEDIS_DAY_DOCTYPE,
      ENEDIS_MINUTE_DOCTYPE,
      ENEDIS_MONTH_DOCTYPE,
      ENEDIS_YEAR_DOCTYPE,
      GRDF_DAY_DOCTYPE,
      GRDF_MONTH_DOCTYPE,
      GRDF_YEAR_DOCTYPE,
      CHALLENGE_DOCTYPE,
      DUEL_DOCTYPE,
      QUIZ_DOCTYPE,
      EXPLORATION_DOCTYPE,
    } from 'doctypes'
    
    import { FluidType } from 'enum/fluid.enum'
    import { Dataload, FluidStatus, Profile, UserChallenge } from 'models'
    
    import EcogestureService from 'services/ecogesture.service'
    import ChallengeService from 'services/challenge.service'
    import ecogestureData from 'db/ecogestureData.json'
    import challengeEntityData from 'db/challengeEntity.json'
    import duelEntityData from 'db/duelEntity.json'
    import quizEntityData from 'db/quizEntity.json'
    import explorationEntityData from 'db/explorationEntity.json'
    
    import ProfileService from 'services/profile.service'
    import profileData from 'db/profileData.json'
    import KonnectorStatusService from 'services/konnectorStatus.service'
    import KonnectorService from 'services/konnector.service'
    import AccountService from 'services/account.service'
    import FluidService from 'services/fluid.service'
    import DuelService from 'services/duel.service'
    import QuizService from 'services/quiz.service'
    import ExplorationService from 'services/exploration.service'
    
    import { hashFile } from 'utils/hash'
    import { getActualAnalysisDate } from 'utils/date'
    import { TimeStep } from 'enum/timeStep.enum'
    import ConsumptionDataManager from './consumption.service'
    import { UserChallengeUpdateFlag } from 'enum/userChallenge.enum'
    import { getRoundFloat } from 'utils/math'
    import { DateTime } from 'luxon'
    
    export default class InitializationService {
      private readonly _client: Client
    
      constructor(_client: Client) {
        this._client = _client
      }
    
      /*
       * Call a query with where clause to create the index if not exist
       */
      private async createIndex(
        doctype: string,
        timestep: TimeStep
      ): Promise<object> {
        const getMongoSelector = () => {
          switch (timestep) {
            case TimeStep.YEAR:
              return {
                year: {
                  $lte: 9999,
                },
              }
            case TimeStep.MONTH:
              return {
                year: {
                  $lte: 9999,
                },
                month: {
                  $lte: 12,
                },
              }
            case TimeStep.DAY:
            case TimeStep.HALF_AN_HOUR:
              return {
                year: {
                  $lte: 9999,
                },
                month: {
                  $lte: 12,
                },
                day: {
                  $lte: 31,
                },
              }
            default:
              return {}
          }
        }
        const query: QueryDefinition = Q(doctype)
          .where(getMongoSelector())
          .limitBy(1)
        return await this._client.query(query)
      }
    
      /*
       * create index for each Doctype
       * sucess return: true
       * failure throw error
       */
      public async initIndex(): Promise<boolean> {
        try {
          const accountService = new AccountService(this._client)
          const konnectorService = new KonnectorService(this._client)
          await Promise.all([
            this.createIndex(EGL_YEAR_DOCTYPE, TimeStep.YEAR),
            this.createIndex(EGL_MONTH_DOCTYPE, TimeStep.MONTH),
            this.createIndex(EGL_DAY_DOCTYPE, TimeStep.DAY),
            this.createIndex(ENEDIS_YEAR_DOCTYPE, TimeStep.YEAR),
            this.createIndex(ENEDIS_MONTH_DOCTYPE, TimeStep.MONTH),
            this.createIndex(ENEDIS_DAY_DOCTYPE, TimeStep.DAY),
            this.createIndex(ENEDIS_MINUTE_DOCTYPE, TimeStep.HALF_AN_HOUR),
            this.createIndex(GRDF_YEAR_DOCTYPE, TimeStep.YEAR),
            this.createIndex(GRDF_MONTH_DOCTYPE, TimeStep.MONTH),
            this.createIndex(GRDF_DAY_DOCTYPE, TimeStep.DAY),
            konnectorService.createIndexKonnector(),
            accountService.createIndexAccount(),
          ])
          console.log(
            '%c Initialization: Indexes created',
            'background: #222; color: white'
          )
          return true
        } catch (error) {
          console.log('Initialization error - initIndex: ', error)
          throw error
        }
      }
    
      /*
       * 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 {
          const loadedProfile = await profileService.getProfile()
          if (!loadedProfile) {
            // Population with the data
            const { data: newProfile } = await this._client.create(
              PROFILE_DOCTYPE,
              profileData[0]
            )
            if (newProfile) {
              console.log(
                '%c Initialization: Profile created',
                'background: #222; color: white'
              )
            } else {
              throw new Error('initProfile: Profile not created')
            }
          } else {
            console.log(
              '%c Initialization: Profile loaded',
              'background: #222; color: white'
            )
          }
          const updatedProfile = await profileService.updateProfile({
            lastConnectionDate: DateTime.local().setZone('utc', {
              keepLocalTime: true,
            }),
          })
          return updatedProfile
        } catch (error) {
          console.log('Initialization error - initProfile: ', error)
          throw error
        }
      }
    
      public async initEcogesture(hash: string): Promise<string> {
        const hashEcogestureType = hashFile(ecogestureData)
        const ecogestureService = new EcogestureService(this._client)
        // Populate data if none ecogesture exists
        const loadedEcogestures = await ecogestureService.getAllEcogestures()
        if (
          !loadedEcogestures ||
          (loadedEcogestures && loadedEcogestures.length === 0)
        ) {
          // Populate the doctype with data
          try {
    
            for (let i = 0; i <= ecogestureData.length - 1; i++) {
              await this._client.create(ECOGESTURE_DOCTYPE, ecogestureData[i])
            }
    
            // Check of created document based on count
            const checkCount = await ecogestureService.getAllEcogestures()
            if (
              !checkCount ||
              (checkCount && checkCount.length !== ecogestureData.length)
            ) {
              throw new Error(
                'initEcogesture: Created ecogesture type entities does not match'
              )
            }
            console.log(
              '%c Initialization: Ecogesture created',
              'background: #222; color: white'
            )
            return hashEcogestureType
          } catch (error) {
            console.log('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
            await Promise.all(
              ecogestureData.map(async ecogesture => {
                await this._client.create(ECOGESTURE_DOCTYPE, ecogesture)
              })
            )
            // Check of created document based on count
            const checkCount = await ecogestureService.getAllEcogestures()
            if (
              !checkCount ||
              (checkCount && checkCount.length !== ecogestureData.length)
            ) {
              throw new Error(
                'initEcogesture: Created ecogesture type entities does not match'
              )
            }
            console.log(
              '%c Initialization: Ecogesture updated',
              'background: #222; color: white'
            )
            return hashEcogestureType
          } catch (error) {
            console.log('Initialization error - initEcogesture: ', error)
            throw error
          }
        } else {
          // Doctype already up to date
          console.log(
            '%c Initialization: Ecogesture loaded',
            'background: #222; color: white'
          )
          return hashEcogestureType
        }
      }
    
      public async initChallengeEntity(hash: string): Promise<string> {
        const challengeHash = hashFile(challengeEntityData)
        const challengeService = new ChallengeService(this._client)
        // Populate data if none challengeEntity exists
        const loadedChallengeEntity = await challengeService.getAllChallengeEntities()
        if (
          !loadedChallengeEntity ||
          (loadedChallengeEntity && loadedChallengeEntity.length === 0)
        ) {
          // Populate the doctype with data
          try {
    
            for (let i = 0; i <= challengeEntityData.length - 1; i++) {
              await this._client.create(CHALLENGE_DOCTYPE, challengeEntityData[i])
            }
    
    
            // Check of created document
            const checkCount = await challengeService.getAllChallengeEntities()
            if (
              !checkCount ||
              (checkCount && checkCount.length !== challengeEntityData.length)
            ) {
              throw new Error(
                'initChallengeEntity: Created challenge entities does not match'
              )
            }
            console.log(
              '%c Initialization: Challenge entities created',
              'background: #222; color: white'
            )
            return challengeHash
          } catch (error) {
            console.log('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
            await Promise.all(
              challengeEntityData.map(async challengeEntity => {
                await this._client.create(CHALLENGE_DOCTYPE, challengeEntity)
              })
            )
            // Check of created document
            const checkCount = await challengeService.getAllChallengeEntities()
            if (
              !checkCount ||
              (checkCount && checkCount.length !== challengeEntityData.length)
            ) {
              throw new Error(
                'initChallengeEntity: Created challenge entities does not match'
              )
            }
            console.log(
              '%c Initialization: Challenge entities updated',
              'background: #222; color: white'
            )
            return challengeHash
          } catch (error) {
            console.log('Initialization error - initChallengeEntity: ', error)
            throw error
          }
        } else {
          // Doctype already up to date
          console.log(
            '%c Initialization: Challenge Entity loaded',
            'background: #222; color: white'
          )
          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 && loadedDuelTypes.length === 0)) {
          // Populate the doctype with data
          try {
    
            for (let i = 0; i <= duelEntityData.length - 1; i++) {
              await this._client.create(DUEL_DOCTYPE, duelEntityData[i])
            }
    
            // Check of created document
            const checkCount = await duelService.getAllDuelEntities()
            if (
              !checkCount ||
              (checkCount && checkCount.length !== duelEntityData.length)
            ) {
              throw new Error(
                'initDuelEntity: Created duel entities does not match'
              )
            }
            console.log(
              '%c Initialization: UserDuel entities created',
              'background: #222; color: white'
            )
            return hashDuelEntity
          } catch (error) {
            console.log('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
            await Promise.all(
              duelEntityData.map(async duelEntity => {
                await this._client.create(DUEL_DOCTYPE, duelEntity)
              })
            )
            // Check of created document
            const checkCount = await duelService.getAllDuelEntities()
            if (
              !checkCount ||
              (checkCount && checkCount.length !== duelEntityData.length)
            ) {
              throw new Error(
                'initDuelEntity: Created duel entities does not match'
              )
            }
            console.log(
              '%c Initialization: UserDuel entities updated',
              'background: #222; color: white'
            )
            return hashDuelEntity
          } catch (error) {
            console.log('Initialization error - initDuelEntity: ', error)
            throw error
          }
        } else {
          // Doctype already up to date
          console.log(
            '%c Initialization: Challenge Entity loaded',
            'background: #222; color: white'
          )
          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 && loadedQuizEntity.length === 0)
        ) {
          // Populate the doctype with data
          try {
    
            for (let i = 0; i <= quizEntityData.length - 1; i++) {
              await this._client.create(QUIZ_DOCTYPE, quizEntityData[i])
            }
    
            // Check of created document
            const checkCount = await quizService.getAllQuizEntities()
            if (
              !checkCount ||
              (checkCount && checkCount.length !== quizEntityData.length)
            ) {
              throw new Error(
                'initQuizEntity: Created quiz entities does not match'
              )
            }
    
            console.log(
              '%c Initialization: Quiz entities created',
              'background: #222; color: white'
            )
            return quizHash
          } catch (error) {
            console.log('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
            await Promise.all(
              quizEntityData.map(async quizEntity => {
                await this._client.create(QUIZ_DOCTYPE, quizEntity)
              })
            )
            // Check of created document
            const checkCount = await quizService.getAllQuizEntities()
            if (
              !checkCount ||
              (checkCount && checkCount.length !== quizEntityData.length)
            ) {
              throw new Error(
                'initQuizEntity: Created quiz entities does not match'
              )
            }
            console.log(
              '%c Initialization: Quiz entities updated',
              'background: #222; color: white'
            )
            return quizHash
          } catch (error) {
            console.log('Initialization error - initQuizEntity: ', error)
            throw error
          }
        } else {
          // Doctype already up to date
          console.log(
            '%c Initialization: Quiz Entity loaded',
            'background: #222; color: white'
          )
          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 && loadedExplorationEntity.length === 0)
        ) {
          // Populate the doctype with data
          try {
            for (let i = 0; i <= explorationEntityData.length - 1; i++) {
              await this._client.create(
                EXPLORATION_DOCTYPE,
                explorationEntityData[i]
              )
            }
            // Check of created document
            const checkCount = await explorationService.getAllExplorationEntities()
            if (
              !checkCount ||
              (checkCount && checkCount.length !== explorationEntityData.length)
            ) {
              throw new Error(
                'initExplorationEntity: Created exploration entities does not match'
              )
            }
            console.log(
              '%c Initialization: Exploration entities created',
              'background: #222; color: white'
            )
            return explorationHash
          } catch (error) {
            console.log('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
            await Promise.all(
              explorationEntityData.map(async explorationEntity => {
                await this._client.create(EXPLORATION_DOCTYPE, explorationEntity)
              })
            )
            // Check of created document
            const checkCount = await explorationService.getAllExplorationEntities()
            if (
              !checkCount ||
              (checkCount && checkCount.length !== explorationEntityData.length)
            ) {
              throw new Error(
                'initExplorationEntity: Created exploration entities does not match'
              )
            }
            console.log(
              '%c Initialization: Exploration entities updated',
              'background: #222; color: white'
            )
            return explorationHash
          } catch (error) {
            console.log('Initialization error - initExplorationEntity: ', error)
            throw error
          }
        } else {
          // Doctype already up to date
          console.log(
            '%c Initialization: Exploration Entity loaded',
            'background: #222; color: white'
          )
          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 {
            console.log(
              '%c Initialization: Analysis information from profile updated',
              'background: #222; color: white'
            )
            return {
              monthlyAnalysisDate: actualAnalysisDate,
              haveSeenLastAnalysis: profile.isFirstConnection ? true : false,
            }
          }
        } catch (error) {
          console.log('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) {
            console.log(
              '%c Initialization: Fluid Types loaded',
              'background: #222; color: white'
            )
            return fluidtypes
          } else {
            throw new Error('initFluidTypes: FluidTypes not found')
          }
        } catch (error) {
          console.log('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 {
          const fluidStatus = await fs.getFluidStatus()
          if (fluidStatus) {
            console.log(
              '%c Initialization: Fluid Status loaded',
              'background: #222; color: white'
            )
            return fluidStatus
          } else {
            throw new Error('initFluidStatus: fluidStatus not found')
          }
        } catch (error) {
          console.log('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) {
            console.log(
              '%c Initialization: Challenges loaded',
              'background: #222; color: white'
            )
            return userChallengeList
          } else {
            throw new Error('initUserChallenges: userChallengeList not found')
          }
        } catch (error) {
          console.log('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)
        const consumptionService = new ConsumptionDataManager(this._client)
        try {
          const dataloads: Dataload[] = await challengeService.getUserChallengeDataload(
            userChallenge
          )
          const userConsumption: number = getRoundFloat(
            consumptionService.calculatePerformanceIndicatorValue(dataloads)
          )
          const _userChallenge: UserChallenge = {
            ...userChallenge,
            duel: {
              ...userChallenge.duel,
              userConsumption: userConsumption,
            },
          }
          const updatedUserChallenge: UserChallenge = await challengeService.updateUserChallenge(
            _userChallenge,
            UserChallengeUpdateFlag.DUEL_CONSUMPTION
          )
          return { updatedUserChallenge, dataloads }
        } catch (error) {
          console.log('Initialization error: ', error)
          throw error
        }
      }
    }