Skip to content
Snippets Groups Projects
queryRunnerService.ts 7.98 KiB
Newer Older
Hugo NOUTS's avatar
Hugo NOUTS committed
import { Client, QueryDefinition } from 'cozy-client'
import { DateTime, Interval } from 'luxon'
import {
  ENEDIS_DAY_DOCTYPE,
  ENEDIS_MONTH_DOCTYPE,
  ENEDIS_YEAR_DOCTYPE,
  GRDF_DAY_DOCTYPE,
  GRDF_MONTH_DOCTYPE,
  GRDF_YEAR_DOCTYPE,
  EGL_DAY_DOCTYPE,
  EGL_MONTH_DOCTYPE,
  EGL_YEAR_DOCTYPE,
  ENEDIS_MINUTE_DOCTYPE,
  GRDF_HOUR_DOCTYPE,
} from 'doctypes'
import { FluidType } from 'enum/fluid.enum'
import { TimeStep, ITimePeriod, IDataload } from './dataConsumptionContracts'

export class QueryRunner {
  // TODO to be clean up
  private readonly _max_limit = 1000
  private readonly _default_months = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
  private readonly _default_days = [
    1,
    2,
    3,
    4,
    5,
    6,
    7,
    8,
    9,
    10,
    11,
    12,
    13,
    14,
    15,
    16,
    17,
    18,
    19,
    20,
    21,
    22,
    23,
    24,
    25,
    26,
    27,
    28,
    29,
    30,
    31,
  ]

  private readonly _client: Client

  constructor(_client: Client) {
    this._client = _client
  }

  public async fetchFluidData(
    timePeriod: ITimePeriod,
    timeStep: TimeStep,
    fluidType: FluidType
  ): Promise<IDataload[] | null> {
    const result = await this.fetchData(
      this.buildListQuery(timeStep, timePeriod, fluidType, this._max_limit)
    )

    if (result && result.data) {
      const filteredResult = this.filterDataList(result, timePeriod)
      const mappedResult = this.mapDataList(filteredResult)
      return mappedResult
    }

    return null
  }

  public async getLastDateData(fluidType: FluidType): Promise<DateTime | null> {
    const result = await this.fetchData(this.buildLastDateQuery(fluidType, 1))

    if (
      result &&
      result.data &&
      result.data[0] &&
      result.data[0].year &&
      result.data[0].month &&
      result.data[0].day
    ) {
      return DateTime.local(
        result.data[0].year,
        result.data[0].month,
        result.data[0].day
      )
    }

    return null
  }

  private async fetchData(query: QueryDefinition) {
    let result = null
    try {
      result = await this._client.query(query)
    } catch (error) {
      // log stuff
      //throw new Error("Cozy Cient Error");

      return null
    }
    return result
  }

Hugo NOUTS's avatar
Hugo NOUTS committed
  public async getEntries(fluidType: FluidType, timeStep: TimeStep) {
    let result = null
Hugo NOUTS's avatar
Hugo NOUTS committed
    const doctype = this.getRelevantDoctype(fluidType, timeStep)
Hugo NOUTS's avatar
Hugo NOUTS committed
      result = await this._client.query(this._client.find(doctype).where({}))
    } catch (error) {
      return null
    }
    return result
  }

Hugo NOUTS's avatar
Hugo NOUTS committed
  private filterDataList(data, timePeriod: ITimePeriod) {
    const filteredResult = data.data.filter(entry =>
      this.withinDateBoundaries(
        DateTime.local(
          entry.year,
          entry.month === 0 ? 1 : entry.month,
          entry.day === 0 ? 1 : entry.day
        ),
        timePeriod
      )
    )

    return filteredResult
  }

  private mapDataList(data) {
    const mappedResult = data.map(entry => ({
      date: DateTime.local(
        entry.year,
        entry.month == 0 ? 1 : entry.month,
        entry.day === 0 ? 1 : entry.day,
        entry.hour,
        entry.minute
      ),
      value: entry.load,
    }))

    return mappedResult
  }

  private buildListQuery(
    timeStep: TimeStep,
    timePeriod: ITimePeriod,
    fluidType: FluidType,
    limit: number
  ) {
    const doctype = this.getRelevantDoctype(fluidType, timeStep)

    return this._client
      .find(doctype)
      .where(this.getPredicate(timePeriod, timeStep))
      .limitBy(limit)
  }

  private withinDateBoundaries(dateTime: DateTime, timePeriod: ITimePeriod) {
    return dateTime <= timePeriod.endDate && dateTime >= timePeriod.startDate
  }

  private getInBetweenMonths(timePeriod: ITimePeriod) {
    const intervalCount = Interval.fromDateTimes(
      timePeriod.startDate,
      timePeriod.endDate
    ).count('month')
    if (intervalCount >= 12) return this._default_months
    const monthList = []
    let offset = 0

    if (timePeriod.startDate.year !== timePeriod.endDate.year) offset = 12

    for (
      let m = timePeriod.startDate.month;
      m <= timePeriod.endDate.month + offset;
      m++
    )
      monthList.push(m > offset ? m - offset : m)

    return monthList
  }

  private getInBetweenDays(timePeriod: ITimePeriod) {
    const intervalCount = Interval.fromDateTimes(
      timePeriod.startDate,
      timePeriod.endDate
    ).count('day')
    if (intervalCount >= 31) return this._default_days
    const dayList = []
    let offset = 0

    if (timePeriod.startDate.month !== timePeriod.endDate.month) offset = 31

    for (
      let d = timePeriod.startDate.day;
      d <= timePeriod.endDate.day + offset;
      d++
    )
      dayList.push(d > offset ? d - offset : d)

    return dayList
  }

  private buildLastDateQuery(fluidType: FluidType, limit: number) {
    const doctype = this.getRelevantDoctype(fluidType, TimeStep.DAY)

    return this._client
      .find(doctype)
      .where({})
      .indexFields(['year', 'month', 'day'])
      .sortBy([{ year: 'desc' }, { month: 'desc' }, { day: 'desc' }])
      .limitBy(1)
  }

  private getPredicate(timePeriod: ITimePeriod, timeStep: TimeStep) {
    let predicate = {}

    switch (timeStep) {
      case TimeStep.HALF_AN_HOUR:
        predicate = {
          year: {
            $eq: timePeriod.startDate.year,
          },
          month: {
            $eq: timePeriod.startDate.month,
          },
          day: {
            $eq: timePeriod.startDate.day,
          },
        }
        break

      case TimeStep.DAY:
        predicate = {
          year: {
            $lte: timePeriod.endDate.year,
            $gte: timePeriod.startDate.year,
          },
          month: {
            $in: this.getInBetweenMonths(timePeriod),
          },
          day: {
            $in: this.getInBetweenDays(timePeriod),
          },
        }
        break
      case TimeStep.MONTH:
        predicate = {
          year: {
            $lte: timePeriod.endDate.year,
            $gte: timePeriod.startDate.year,
          },
          month: {
            $in: this.getInBetweenMonths(timePeriod),
          },
        }
        break
      case TimeStep.YEAR:
        predicate = {
          year: {
            $lte: timePeriod.endDate.year,
            $gte: timePeriod.startDate.year,
          },
        }
        break
    }
    return predicate
  }

  private getRelevantDoctype(fluidType: FluidType, timeStep: TimeStep) {
    let doctype = ''

    switch (fluidType) {
      case FluidType.ELECTRICITY:
        {
          switch (timeStep) {
            case TimeStep.HALF_AN_HOUR:
              doctype = ENEDIS_MINUTE_DOCTYPE
              break
            case TimeStep.DAY:
              doctype = ENEDIS_DAY_DOCTYPE
              break
            case TimeStep.MONTH:
              doctype = ENEDIS_MONTH_DOCTYPE
              break
            case TimeStep.YEAR:
              doctype = ENEDIS_YEAR_DOCTYPE
              break
            default:
              doctype = ENEDIS_DAY_DOCTYPE
          }
        }
        break

      case FluidType.WATER:
        {
          switch (timeStep) {
            case TimeStep.DAY:
              doctype = EGL_DAY_DOCTYPE
              break
            case TimeStep.MONTH:
              doctype = EGL_MONTH_DOCTYPE
              break
            case TimeStep.YEAR:
              doctype = EGL_YEAR_DOCTYPE
              break
            default:
              doctype = EGL_DAY_DOCTYPE
          }
        }
        break

      case FluidType.GAS:
        {
          switch (timeStep) {
            case TimeStep.HOUR:
              doctype = GRDF_HOUR_DOCTYPE
              break
            case TimeStep.DAY:
              doctype = GRDF_DAY_DOCTYPE
              break
            case TimeStep.MONTH:
              doctype = GRDF_MONTH_DOCTYPE
              break
            case TimeStep.YEAR:
              doctype = GRDF_YEAR_DOCTYPE
              break
            default:
              doctype = GRDF_DAY_DOCTYPE
          }
        }
        break

      default:
        break
    }

    return doctype
  }
}