Source

services/history-service.js

import historyAPI from '@/api/historyApi.js'
import api from '@/api'
import moment from 'moment'
import { formatSeconds, formatDatetimeWithSeconds } from '@/utils/dates.js'
import normalizePositionEntity from './entities/position-entity'
import createSensorConfigEntity from '@/services/entities/sensor-config.js'
import { getFormattedAddress } from '@/utils/address'
import { normalizeObject } from '@/utils/object.js'
import normalizeTripHistoryStep from '@/services/entities/trip-history-step.js'
import { splitOperation } from '@/utils/promise.js'
import { isMomentToday } from '@/utils/dates.js'
import APIUrls, {
  APIV3RequestDatetimeFormat,
  APIV2RequestDatetimeFormat,
} from '@/config/simpliciti-apis.js'
import sensorsService from './sensors-service'

const R = require('ramda')

export default {
  getVehicleHistoryFromDate,
  getVehicleHistoryStepsFromDate,
  normalizeVehicleHistoryPositions,
}

/**
 * Used by Diagnostic module to fetch vehicle overview information about the trip history for a particular date.
 *
 * @param {Number} vehicleId
 * @param {Date|Moment} date
 * @returns {Object} Overview information about the trip history
 */
export async function getVehicleHistoryFromDate(vehicleId, date, options = {}) {
  return await historyAPI.getVehicleHistoryInfosFromAPIV2(
    vehicleId,
    date,
    options
  )
}

/**
 * @warn unused
 * @param {*} vehicleId
 * @param {*} date
 * @version APIV2
 * @returns
 */
export async function getVehicleHistoryStepsFromDate(vehicleId, date) {
  let formattedDateFrom = moment(date)
    .hour(0)
    .minute(0)
    .seconds(0)
    .format(APIV2RequestDatetimeFormat)
  let formattedDateTo = moment(date)
    .hour(23)
    .minute(59)
    .seconds(59)
    .format(APIV2RequestDatetimeFormat)
  let response = (
    await api.v2.post(
      `${APIUrls.APIV2_HISTORIQUE_TRONCONS_DETAILS}?vehicule_id=${vehicleId}&dateheure_debut=${formattedDateFrom}&dateheure_fin=${formattedDateTo}&linestring=true`
    )
  ).data

  let historySteps = []
  if (response instanceof Array && response.length >= 1) {
    historySteps = response[0].troncons && response[0].troncons
  }
  return historySteps.map(normalizeTripHistoryStep)
}

/**
 * Used by Diagnostic module
 *
 * @todo Use properties[positions][]= to reduce size of nested objects (It doesn't work with arrays like tor/ana)
 * @param {*} vehicleId
 * @param {*} date
 * @param {*} options
 * @version APIV3
 * @returns
 */
export async function getVehiclePositions(
  vehicleId,
  startDatetime,
  endDatetime
) {
  if (moment(startDatetime).day() == moment(endDatetime).day()) {
    return fetchHandler(startDatetime, endDatetime)
  } else {
    let arr = await splitOperation({
      sequential: false,
      generateSubsets() {
        return [
          {
            startDatetime: moment(startDatetime),
            endDatetime: moment(startDatetime).hour(23).minute(59).second(59),
          },
          {
            startDatetime: moment(endDatetime).hour(0).minute(0).second(0),
            endDatetime: moment(endDatetime),
          },
        ]
      },
      async handleSubset(subset) {
        console.log('getVehiclePositions::splitOperation::handleSubset', {
          subset,
        })
        return fetchHandler(subset.startDatetime, subset.endDatetime)
      },
    })
    console.log('getVehiclePositions::splitOperation', {
      arr,
    })
    return arr.reduce((a, v) => {
      a = [...a, ...v]
      return a
    }, [])
  }

  async function fetchHandler(startDatetime, endDatetime) {
    let data = []
    startDatetime = moment(startDatetime).format(APIV3RequestDatetimeFormat)
    endDatetime = moment(endDatetime).format(APIV3RequestDatetimeFormat)
    data = (
      await api.v3.get(APIUrls.APIV3_GET_POSITIONS, {
        vehicleIds: [vehicleId],
        startDatetime,
        endDatetime,
        groups: ['sensor_details', 'can'],
        properties: ['positions', 'vehicleRegistrationPlate', 'vehicleName'],
      })
    ).data
    data = data.length > 0 ? data[0] : { positions: [] }
    data = data.positions.map((item) =>
      normalizePositionEntity(item, {
        vehicleId,
        datetimeInputFormat: 'YYYY-MM-DDTHH:mm:ss[Z]',
      })
    )
    data = data.filter((item) => {
      return !!item.lng && !!item.lat
    })
    return data
  }
}

/**
 * Used by Diagnostic module
 * @todo Remove when unused
 *
 * @param {*} vehicleId
 * @param {*} datetime
 * @version APIV2
 * @returns
 */
export async function getVehicleHistoryPositionDetailsFromDateLegacy(
  vehicleId,
  datetime
) {
  datetime = datetime._isAMomentObject ? datetime : moment(new Date(datetime))
  datetime = datetime.format(APIV2RequestDatetimeFormat)
  let items = (
    await api.v2.get(
      `${APIUrls.APIV2_POSITIONS_GPS}?vehicule_id=${vehicleId}&dateheure_debut=${datetime}&dateheure_fin=${datetime}&groups=infos_complementaires,capteurs,capteurs_details,can,localisation_adresse`
    )
  ).data

  if (items instanceof Array && items.length > 0) {
    let item = items[0]
    let headers = R.omit(['positions'], item)
    item = {
      ...headers,
      ...(item.positions.length > 0 ? item.positions[0] : {}),
    }
    return normalizePositionEntity(item)
  } else {
    return null
  }
}

/**
 * Unused
 * @param {Array} positions
 * @param {String} vehicleId
 * @returns
 */
export function normalizeVehicleHistoryPositions(positions, vehicleId) {
  return (positions || []).map((item) => {
    return {
      lat: item.latitude,
      lng: item.longitude,
      datetime: item.datetime,
      timestamp: moment(item.datetime)._d.getTime(),
      vehicleId,
    }
  })
}

/***
 * Used by Diagnostic module
 * @version APIV3
 * Will compare two positions using APIV3_POSITIONS_CUMULATION
 */
export async function analyzeHistorySegment(
  vehicleId,
  datetimeFrom,
  datetimeTo
) {
  let res = await api.v3.get(APIUrls.APIV3_POSITIONS_CUMULATION, {
    vehicleId,
    startDatetime: moment(datetimeFrom).format(APIV3RequestDatetimeFormat),
    endDatetime: moment(datetimeTo).format(APIV3RequestDatetimeFormat),
  })
  return res.data instanceof Array && res.data.length > 0
    ? normalizeAnalyzeHistorySegment(res.data[0])
    : null
}

/**
 * Used by Diagnostic module
 * @todo Refactor/Move into entity "diagnostic-analysis-entity"
 * @param {*} response
 * @returns
 */
export function normalizeAnalyzeHistorySegment(response) {
  //response key --> normalized key
  let normalizedTable = {
    vehicleId: 'vehicleId',
    clientId: 'clientId',
    vehicleCategoryId: 'vehicleCategoryId',
    vehicleName: 'vehicleName',
    clientName: 'clientName',
    vehicleRegistrationPlate: 'vehicleRegistrationPlate',
    vehicleCategoryName: 'vehicleCategoryName',
    startDatetime: {
      key: 'startDatetime',
      transformKey: 'startDatetimeFormatted',
      transform: (v) =>
        formatDatetimeWithSeconds(v, {
          inputFormat: APIV3RequestDatetimeFormat,
        }),
    },
    endDatetime: {
      key: 'endDatetime',
      transformKey: 'endDatetimeFormatted',
      transform: (v) =>
        formatDatetimeWithSeconds(v, {
          inputFormat: APIV3RequestDatetimeFormat,
        }),
    },
    duration: {
      key: 'duration',
      transformKey: 'durationFormatted',
      transform: (v) => formatSeconds(v),
    },
    effectiveDuration: {
      key: 'effectiveDuration',
      transformKey: 'effectiveDurationFormatted',
      transform: (v) => formatSeconds(v),
    },
    distance: {
      key: 'distance',
      transformKey: 'distanceFormatted',
      transform: (v) => `${(v / 1000).toFixed(2)} Km`,
    },
    speed: {
      key: 'speed',
      transformKey: 'speedFormatted',
      transform: (v) => `${v} Km/h`,
    },
    totalCo2: 'totalCo2',
    averageCo2: 'averageCo2',
    totalConsumption: 'totalConsumption',
    averageConsumption: {
      key: 'averageConsumption',
      /*
      transformKey: 'averageConsumptionPerc',
      transform: (v, rawData) => {
        return (
          (rawData.averageConsumption / rawData.referenceConsumption) *
          100
        ).toFixed(0)
      },
      */
    },
    referenceConsumption: 'referenceConsumption',
    averageEngineSpeed: 'averageEngineSpeed',
    startAddress: {
      key: 'startAddress',
      transformKey: 'startAddressFormatted',
      transform: (v, rawData = {}) =>
        getFormattedAddress(rawData, {
          streetNumber: '',
          streetName: 'startAddress',
          zipCode: 'startZipCode',
          city: 'startCity',
        }),
    },
    startZipCode: 'startZipCode',
    startCity: 'startCity',
    startLongitude: 'startLongitude',
    startLatitude: 'startLatitude',
    endAddress: {
      key: 'endAddress',
      transformKey: 'endAddressFormatted',
      transform: (v, rawData = {}) =>
        getFormattedAddress(rawData, {
          streetNumber: '',
          streetName: 'endAddress',
          zipCode: 'endZipCode',
          city: 'endCity',
        }),
    },
    endZipCode: 'endZipCode',
    endCity: 'endCity',
    endLongitude: 'endLongitude',
    endLatitude: 'endLatitude',
  }
  return normalizeObject(response, normalizedTable)
}

/**
 *
 * Used by Location module (Trip history details - table) to load positions and sensors configuration
 *
 * @param {*} vehicleId
 * @param {*} date
 * @returns {Array}
 */
export async function getVehicleSensorsConfiguration(vehicleId) {
  /*   const t = (d, h = 0, m = 0, s = 0) =>
    moment(d).hour(h).minute(m).second(s).format(APIV2RequestDatetimeFormat) */

  const startDatetime = moment()
    .startOf('day')
    .format(APIV3RequestDatetimeFormat)
  const endDatetime = moment().endOf('day').format(APIV3RequestDatetimeFormat)

  let res = await sensorsService.getSensors(
    vehicleId,
    startDatetime,
    endDatetime,
    { sensors: true }
  )

  if (res) {
    return {
      id: res?.configurationId || -1,
      name: res?.configurationName || 'Default',
      sensors: (res?.sensors || []).map(createSensorConfigEntity),
    }
  } else {
    return {}
  }
}