Source

services/chrono-service.js

import moment from 'moment'
import api from '@/api'
import { getEnvValue } from '@/services/env-service.js'
import {
  APIV2RequestDatetimeFormat,
  APIV2RequestDatetimeFormatWithCustomTime,
} from '@/config/simpliciti-apis.js'

const API_CHRONO_IMPUTATIONS_URI =
  getEnvValue('VUE_APP_API_CHRONO_IMPUTATIONS_URI') ||
  '/v2/historique/imputation_chrono'

/**
 * Used by Location module - Chrono tab
 * Used by Diagnostics module
 * @param {String|Date|Moment} params.date Date or Moment
 * @param {String} params.vehicleId Vehicle identifier
 * @param {Date|Moment} params.startDate
 * @param {Date|Moment} params.endDate
 * @param {Function} params.apiHandler (unit-test only)
 */
export async function getVehicleChrono(params = {}) {
  if (!params.vehicleId) {
    throw new Error('Required: vehicleId')
  }
  if (!params.date && (!params.startDate || !params.endDate)) {
    throw new Error(
      `Required:  At least date or startDate/endDate must be specified (Params: ${JSON.stringify(
        params
      )})`
    )
  }
  if (params.date && params.startDate && params.endDate) {
    throw new Error(
      `Required:  Only date or startDate/endDate must be specified (Params: ${JSON.stringify(
        params
      )})`
    )
  }
  if (
    (params.startDate && !params.endDate) ||
    (!params.startDate && params.endDate)
  ) {
    throw new Error('Required:  Both startDate/endDate must be specified')
  }

  let startDate = params.startDate ? params.startDate : moment(params.date)
  startDate = startDate._isAMomentObject ? startDate : moment(startDate)

  let endDate = params.endDate ? params.endDate : params.date
  endDate = endDate._isAMomentObject ? endDate : moment(endDate)

  if (params.date && !params.startDate && !params.endDate) {
    startDate = startDate.format(
      APIV2RequestDatetimeFormatWithCustomTime('00:00:00')
    )
    endDate = endDate.format(
      APIV2RequestDatetimeFormatWithCustomTime('23:59:59')
    )
  } else {
    startDate = startDate.format(APIV2RequestDatetimeFormat)
    endDate = endDate.format(APIV2RequestDatetimeFormat)
  }

  let apiURL = `${API_CHRONO_IMPUTATIONS_URI}/${params.vehicleId}/${startDate}/${endDate}/true`
  const apiHandler = params.apiHandler
    ? () => params.apiHandler(apiURL)
    : () => api.v2.get(apiURL)

  return normalizeChronoResponse(((await apiHandler()) || {}).data || {})
}

function getChronoIconPath(imputationType) {
  let iconPath = ''

  if (imputationType) {
    switch (imputationType.toLowerCase()) {
      case 'conduite':
        iconPath = './lib/realtimeMap/assets/picto_chrono/chrono_4.svg'
        break
      case 'travail':
        iconPath = './lib/realtimeMap/assets/picto_chrono/chrono_3.svg'
        break
      case 'dispo':
        iconPath = './lib/realtimeMap/assets/picto_chrono/chrono_2.svg'
        break
      case 'repos':
        iconPath = './lib/realtimeMap/assets/picto_chrono/chrono_1.svg'
        break
      default:
        iconPath = './lib/realtimeMap/assets/picto_chrono/chrono_1.svg'
        break
    }
  }

  return iconPath
}

function normalizeChronoResponse(data) {
  if (!data) {
    return {}
  }

  return {
    synthesis: normalizeChronoResponseSynthesis(data),
    details: normalizeChronoResponseDetails(data),
  }
}

function formatObjectValue(item, value) {
  return item.hasOwnProperty(value) ? item[value] : ''
}

function formatArrayValue(array, value) {
  if (typeof array[value] === 'number') {
    return array[value]
  }

  return array[value] ? array[value] : ''
}

function formatObjectAddress(item) {
  return item.hasOwnProperty('rue') &&
    item.hasOwnProperty('cp') &&
    item.hasOwnProperty('ville')
    ? item['rue'] + ' ' + item['cp'] + ' ' + item['ville']
    : ''
}

function normalizeChronoResponseSynthesis(item) {
  let synthesis = {}

  if (item && item.hasOwnProperty('synthese')) {
    synthesis = {
      startDay: formatObjectValue(item.synthese, 'dh_debut_journee'),
      endDay: formatObjectValue(item.synthese, 'dh_fin_journee'),
      amplitude: formatObjectValue(item.synthese, 'amplitude'),
      nbStop: formatObjectValue(item.synthese, 'nb_arret_superieur_10mn'),
      distance: formatObjectValue(item.synthese, 'distance'),
      speed: formatObjectValue(item.synthese, 'vitesse'),
      periodService: formatObjectValue(item.synthese, 'duree_service'),
      periodDrive: formatObjectValue(item.synthese, 'duree_conduite'),
      percentDrive: formatObjectValue(item.synthese, 'pourcentage_conduite'),
      periodAvailable: formatObjectValue(item.synthese, 'duree_dispo'),
      percentAvailable: formatObjectValue(item.synthese, 'pourcentage_dispo'),
      periodWork: formatObjectValue(item.synthese, 'duree_travail'),
      percentWork: formatObjectValue(item.synthese, 'pourcentage_travail'),
      periodWorkAvailable: formatObjectValue(
        item.synthese,
        'duree_dispo_travail'
      ),
      percentWorkAvailable: formatObjectValue(
        item.synthese,
        'pourcentage_dispo_travail'
      ),
      maxPeriodRest: formatObjectValue(item.synthese, 'duree_max_repos'),
      periodRest: formatObjectValue(item.synthese, 'duree_repos'),
      percentRest: formatObjectValue(item.synthese, 'pourcentage_repos'),
      maxPeriodStart: formatObjectValue(
        item.synthese,
        'heuredebut_duree_max_repos'
      ),
    }
  }

  return synthesis
}

function getChronoItemType(type = 'unknown') {
  const table = {
    travail: 'working',
    repos: 'resting',
    conduite: 'driving',
    dispo: 'available',
    unknown: 'unknown',
  }
  if (!table[type.toLowerCase()]) {
    console.error('Invalid chrono type: ' + table[type.toLowerCase()])
  }
  return table[type.toLowerCase()]
}

export function getChronoColorFromType(type) {
  const table = {
    working: getEnvValue('VUE_APP_CHRONO_STATUS_COLOR_WORKING', '#1D8CC3'),
    resting: getEnvValue('VUE_APP_CHRONO_STATUS_COLOR_RESTING', '#418339'),
    driving: getEnvValue('VUE_APP_CHRONO_STATUS_COLOR_DRIVING', '#CC494D'),
    available: getEnvValue('VUE_APP_CHRONO_STATUS_COLOR_AVAILABLE', '#ECC646'),
    unknown: getEnvValue('VUE_APP_CHRONO_STATUS_COLOR_UNKNOWN', 'black'),
  }
  return table[type]
}

/**
 * @todo Map type to custom values ['driving', 'available', 'resting', 'working'],
 * @param {*} item
 * @returns
 */
function normalizeChronoResponseDetails(item) {
  let details = []

  if (item && item.hasOwnProperty('imputations')) {
    item.imputations.forEach((element) => {
      let normalizedType = getChronoItemType(element.imputation_type)
      details.push({
        startDate: formatArrayValue(element, 'dateheure_debut'),
        endDate: formatArrayValue(element, 'dateheure_fin'),
        period: formatArrayValue(element, 'duree'),
        type: formatArrayValue(element, 'imputation_type'),
        typeAlt: normalizedType,
        color: getChronoColorFromType(normalizedType),
        iconPath: getChronoIconPath(
          formatArrayValue(element, 'imputation_type')
        ),
        distance: formatArrayValue(element, 'distance'),
        service: formatArrayValue(element, 'cumul_service'),
        driverName: formatArrayValue(element, 'chauffeur_nom'),
        address: formatObjectAddress(element),
        lat: formatArrayValue(element, 'latitude'),
        lng: formatArrayValue(element, 'longitude'),
        periodDrive: formatArrayValue(element, 'duree_conduite'),
        periodWork: formatArrayValue(element, 'duree_travail'),
        periodRest: formatObjectValue(element, 'duree_repos'),
        periodAvailable: formatObjectValue(element, 'duree_dispo'),
        zone: formatObjectValue(element, 'zone'),
        speed: formatObjectValue(element, 'vitesse'),
      })
    })
  }

  return details
}