Source

services/circuit-service.js

import APIUrls, {
  APIV2RequestDatetimeFormat,
} from '@/config/simpliciti-apis.js'
import moment from 'moment'
import { getNestedValue } from '@/utils/object.js'
import api from '@/api'
import FixturesService from '@/api/fixtures.js'
import { getAPIV3Pooling } from '@/api'

const R = require('ramda')

/**
 *
 * Used by:
 * Location - Main search -> History by Circuit
 * Location - Main search -> History by Vehicle/Driver -> Circuit Tab
 *
 * @param {Number} circuitId [OPTIONAL]
 * @param {String} date Circuit execution start date
 * @returns Normalized circuit execution detail item
 */
export async function getCircuitExecutionDetails({
  circuitId,
  date,
  ...params
} = {}) {
  let m = moment(date)
  date = m.format('YYYY-MM-DD')
  let from = m.format('HH:mm')
  let to = m.hour(23).minute(59).second(59).format('HH:mm')
  circuitId = circuitId ? `&circuit_id=${circuitId}` : ''
  let apiResponse = (
    await api.v2.post(
      `${
        APIUrls.APIV2_HISTORIQUE_BY_CIRCUIT_DATES
      }?dates=${date}&heure_debut=${encodeURI(from)}&heure_fin=${encodeURI(
        to
      )}&infos_gps=false&linestring=false&troncons=true${circuitId}`
    )
  ).data

  let rawItem = getFirstProperty(getFirstProperty(apiResponse))
  let historyItem = getFirstProperty(rawItem.historique)

  //As API doesn't let us filter by execution, sometimes it could bring two executions and we need to filter (i.g location/history by circuit: details)
  if (params.circuitExecutionId) {
    Object.keys(rawItem.historique).forEach((circuitExecutionId) => {
      if (
        circuitExecutionId.toString() == params.circuitExecutionId.toString()
      ) {
        historyItem = rawItem.historique[circuitExecutionId]
      }
    })
  }

  let result = normalizeCircuitExecutionDetailsItem({
    ...rawItem,
    ...historyItem,
  })

  result.troncons = (result.troncons || []).sort((a, b) =>
    parseInt(a.id) > parseInt(b.id) ? 1 : -1
  )
  return result
}

/**
 *
 * Used by:
 * Location - Main search -> Real-time by Vehicle/Driver/Circuit
 *
 * Get last executed circuit infos from fixtures or API.
 * @warn APIV2 response: Attribute "historique" will always contain one item
 * @todo Remove fixtures feature without braking unit test
 * @param {Object} {vehicleId}
 * @returns Normalized circuit execution detail item
 */
export async function getLastCircuitExecutionDetails({ vehicleId }) {
  if (!vehicleId) {
    return {}
  }
  let extraParams = '&troncons=true'
  let apiResponse =
    (await FixturesService.loadFixtures(
      `${APIUrls.APIV2_HISTORIQUE_DERNIER_CIRCUIT.substring(1)}/${vehicleId}`
    )) ||
    (
      await api.v2.post(
        `${APIUrls.APIV2_HISTORIQUE_DERNIER_CIRCUIT}?vehicule_id=${vehicleId}&execution_en_cours=true${extraParams}`
      )
    ).data

  if (apiResponse.length !== undefined && apiResponse.length === 0) {
    apiResponse = null
  }

  if (apiResponse) {
    let rawItem = getFirstProperty(getFirstProperty(apiResponse))
    return normalizeCircuitExecutionDetailsItem({
      ...rawItem,
      ...getFirstProperty(rawItem.historique),
    })
  } else {
    return {}
  }
}

/**
 * Used by:
 * Location - Main search -> Real-time by Vehicle/Driver/Circuit
 * @param {*} object
 * @returns
 */
function getFirstProperty(object) {
  return (
    (Object.keys(object || {}).length > 0 && object[Object.keys(object)[0]]) ||
    {}
  )
}

/**
 * Transform/Presenter function for circuit execution details item
 *
 * @param {*} item Details item from APIV2 Historique by_circuit_dates or APIV2 dernier_circuit
 * @returns
 */
function normalizeCircuitExecutionDetailsItem(item) {
  item = {
    //raw response
    ...item,
    //transform fields (use these when possible)
    circuit_nom_court: item.circuit_nom_court,
    circuit_nom_long: item.circuit_nom_long,
    circuit_categorie: item.circuit_categorie,
    circuit_id: item.circuit_id,
    dateFrom: getNestedValue(item, ['dh_exec_circuit_debut']),
    dateTo: getNestedValue(item, ['dh_exec_circuit_fin']),
  }
  return R.omit(['historique'], item)
}

/**
 * @deprecated: Last "last circuit" API already brings troncons attribute if &trocons=1 is supplied (see getLastCircuitExecutionDetails)
 * Circuit execution steps/troncons
 *
 * @param {Number} vehicleId
 * @param {Number} circuitId
 * @param {String} fromDate Circuit execution start date
 * @param {String} toDate Circuit execution end date
 * @returns
 */
export async function getCircuitExecutionSteps({
  vehicleId,
  circuitId,
  fromDate,
  toDate,
}) {
  console.debug('getCircuitExecutionSteps', {
    fromDate,
    toDate,
  })
  let fromDateStr = encodeURI(
    moment(fromDate, APIV2RequestDatetimeFormat).format(
      APIV2RequestDatetimeFormat
    )
  )
  toDate = encodeURI(
    moment(toDate, APIV2RequestDatetimeFormat).format(
      APIV2RequestDatetimeFormat
    )
  )
  let data = (
    await api.v2.post(
      `${APIUrls.APIV2_HISTORIQUE_CIRCUIT_TRONCONS}?dateheure_debut=${fromDateStr}&dateheure_fin=${toDate}&vehicule_id=${vehicleId}&circuit_id=${circuitId}`
    )
  ).data
  return data.length >= 1 ? data[0] : {}
}

export async function fetchCircuitCategories(transform = null) {
  return await getAPIV3Pooling({
    uri: `${APIUrls.APIV3_ROUND_CATEGORIES}?page=1&itemsPerPage=500&order[label]=asc`,
    transform: transform,
  })
}

/**
 * @warn Steven: transform/normalization should be not extracted from circuit-service. If you need two differents normalization, instead, split into two different functions: fetchCircuitsV3/fetchCircuitsV2
 * @todo SearchModule: Move the normalization from search_module into this function (default normalization)
 * @param {*} transform
 * @returns
 */
export async function fetchCircuits(transform = null) {
  return await getAPIV3Pooling({
    uri: `${APIUrls.APIV3_ROUNDS}?page=1&itemsPerPage=500&order[shortName]=asc`,
    transform: transform,
  })
}

export function normalizeRoundCategoryToV2(c) {
  const parentId = getNestedValue(c, 'parentId')
  let result = {
    id: getNestedValue(c, 'id'),
    libelle: getNestedValue(c, 'label'),
    parent_id: parentId == '' ? null : parentId,
  }

  return result
}

export function normalizeRoundToV2(c, categoryList) {
  let categoryId = null

  if (!c?._links?.category?.href) {
    //tries to set "sans category" category
    categoryId = (
      categoryList.find(
        (ctg) => ctg.libelle.toLowerCase().indexOf('sans ') !== -1
      ) || {}
    ).id
  } else {
    categoryId = getNestedValue(c, '_links.category.href').match(/\d+/)[0]
  }

  const category = categoryList.find((c) => c.id == categoryId)
  let result = {
    id: getNestedValue(c, 'id'),
    circuit_id: getNestedValue(c, 'id'),
    nom_court: getNestedValue(c, 'shortName'),
    category_id: categoryId,
    categorie_nom: category.libelle,
  }

  if (category?.parent_id) {
    const parentCategory =
      categoryList.find((c) => c.id === category.parent_id) || null
    result = {
      ...result,
      parentCategoryName: parentCategory ? parentCategory.libelle : '',
    }
  }

  return result
}

export default {
  getLastCircuitExecutionDetails,
  getCircuitExecutionDetails,
  getCircuitExecutionSteps,
  fetchCircuitCategories,
  fetchCircuits,
  normalizeRoundCategoryToV2,
  normalizeRoundToV2,
}