fleetmap-reports 2.0.364 → 2.0.366
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
const axios = require('axios').default
|
|
1
2
|
const traccarHelper = require('../util/traccar')
|
|
2
3
|
const { getDriverData, devicesByDriver } = require('../util/driver')
|
|
3
4
|
const { getCanDriverStyleMessages, parseCanDriverStyleMessage } = require('../util/xpert')
|
|
4
5
|
const { calculateRPMSections } = require('./performance-report')
|
|
5
|
-
const { getTranslations, isClientSide, convertToLocaleString, convertMS,
|
|
6
|
+
const { getTranslations, isClientSide, convertToLocaleString, convertMS, getServerHost } = require('../util/utils')
|
|
6
7
|
const jsPDF = require('jspdf')
|
|
7
8
|
const { headerFromUser, addTable } = require('../util/pdfDocument')
|
|
8
9
|
const { getStyle } = require('../reportStyle')
|
|
@@ -87,25 +88,8 @@ async function create (from, to, userData, traccar) {
|
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
if (isClientSide() && (userData.drivers.length > 50 || ((new Date(to).getTime() - new Date(from).getTime()) > (1000 * 60 * 60 * 24 * 15)))) {
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
userData.byDriver = true
|
|
93
|
-
// Narrow the devices sent per-slice to just the ones those drivers actually used, so a single
|
|
94
|
-
// server invocation doesn't have to hold the whole fleet's route data in memory at once (was
|
|
95
|
-
// causing Runtime.OutOfMemory on large fleets).
|
|
96
|
-
const allDriversData = await executeServerSide('driverranking-report', sliced, from, to, userData, userData.drivers.length, traccar, 5, (driversSlice) => {
|
|
97
|
-
const deviceIds = new Set()
|
|
98
|
-
driversSlice.forEach(d => {
|
|
99
|
-
const ids = deviceIdsByDriver.get(d.uniqueId)
|
|
100
|
-
if (ids) {
|
|
101
|
-
ids.forEach(id => deviceIds.add(id))
|
|
102
|
-
}
|
|
103
|
-
})
|
|
104
|
-
return { devices: userData.devices.filter(device => deviceIds.has(device.id)) }
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
allDriversData.sort((a, b) => b.overallScore - a.overallScore)
|
|
108
|
-
allDriversData.forEach((d, index) => { d.rank = index + 1 })
|
|
91
|
+
const rawRows = await runServerSideGrid(from, to, userData, deviceIdsByDriver, traccar)
|
|
92
|
+
const allDriversData = scoreAndRankDrivers(mergeRawRows(rawRows), userData)
|
|
109
93
|
|
|
110
94
|
reportData.drivers = allDriversData
|
|
111
95
|
reportData.target = buildTargets(userData)
|
|
@@ -159,7 +143,25 @@ async function create (from, to, userData, traccar) {
|
|
|
159
143
|
}
|
|
160
144
|
}
|
|
161
145
|
|
|
162
|
-
const
|
|
146
|
+
const rawRows = Array.from(driversData.values())
|
|
147
|
+
|
|
148
|
+
// When invoked as one cell of the client's (driverSlice x dateChunk) grid, return the raw
|
|
149
|
+
// accumulated totals for just this chunk - the client sums them across chunks for the same
|
|
150
|
+
// driver before computing scores, since scores are relative to the whole requested period.
|
|
151
|
+
if (userData.rawMode) {
|
|
152
|
+
reportData.drivers = rawRows
|
|
153
|
+
return reportData
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
reportData.drivers = scoreAndRankDrivers(rawRows, userData)
|
|
157
|
+
reportData.target = buildTargets(userData)
|
|
158
|
+
reportData.coefficient = buildCoefficients()
|
|
159
|
+
|
|
160
|
+
return reportData
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function scoreAndRankDrivers (rawRows, userData) {
|
|
164
|
+
const allDriversData = rawRows.filter(d => d.distance > 0)
|
|
163
165
|
allDriversData.forEach(d => {
|
|
164
166
|
d.avgFuelConsumption = getCanAvgConsumption(d.distance, d.spentFuel).byKms
|
|
165
167
|
|
|
@@ -185,12 +187,86 @@ async function create (from, to, userData, traccar) {
|
|
|
185
187
|
})
|
|
186
188
|
allDriversData.sort((a, b) => b.overallScore - a.overallScore)
|
|
187
189
|
allDriversData.forEach((d, index) => { d.rank = index + 1 })
|
|
190
|
+
return allDriversData
|
|
191
|
+
}
|
|
188
192
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
193
|
+
const mergedFields = ['distance', 'highEngineRPM', 'hardBraking', 'hardAcceleration', 'hardCornering',
|
|
194
|
+
'overspeed', 'spentFuel', 'geofenceAlarm', 'continuesDrivingAlarm', 'reverseAlarm', 'otherAlarm']
|
|
195
|
+
|
|
196
|
+
// Sums each driver's raw totals across every (driverSlice x dateChunk) grid cell they appeared in
|
|
197
|
+
function mergeRawRows (rawRows) {
|
|
198
|
+
const merged = new Map()
|
|
199
|
+
rawRows.forEach(row => {
|
|
200
|
+
let acc = merged.get(row.driverId)
|
|
201
|
+
if (!acc) {
|
|
202
|
+
acc = { driverId: row.driverId, name: row.name }
|
|
203
|
+
mergedFields.forEach(field => { acc[field] = 0 })
|
|
204
|
+
merged.set(row.driverId, acc)
|
|
205
|
+
}
|
|
206
|
+
mergedFields.forEach(field => { acc[field] += row[field] || 0 })
|
|
207
|
+
})
|
|
208
|
+
return Array.from(merged.values())
|
|
209
|
+
}
|
|
192
210
|
|
|
193
|
-
|
|
211
|
+
// Fans the report out across a grid of (driverSlice x dateChunk) requests to the backend, so no
|
|
212
|
+
// single Lambda invocation has to hold more than a handful of drivers' worth of one week of route
|
|
213
|
+
// data in memory at once, and progress feedback arrives once per grid cell rather than once per
|
|
214
|
+
// driver-slice (which could take minutes on its own with a long date range).
|
|
215
|
+
async function runServerSideGrid (from, to, userData, deviceIdsByDriver, traccar) {
|
|
216
|
+
const driverSlices = automaticReports.sliceArray(userData.drivers, 5)
|
|
217
|
+
const dateChunks = splitDateRange(from, to, 7)
|
|
218
|
+
|
|
219
|
+
const cells = []
|
|
220
|
+
driverSlices.forEach(driverSlice => {
|
|
221
|
+
dateChunks.forEach(dateChunk => cells.push({ driverSlice, dateChunk }))
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
const cookie = await axios.get('/pinmeapi/cookie/get', { withCredentials: true }).then(d => d.data)
|
|
225
|
+
const url = `https://${getServerHost()}/pinmeapi/reports/driverranking-report`
|
|
226
|
+
const rawRows = []
|
|
227
|
+
let completed = 0
|
|
228
|
+
const maxConcurrent = 10
|
|
229
|
+
|
|
230
|
+
for (let i = 0; i < cells.length; i += maxConcurrent) {
|
|
231
|
+
const batch = cells.slice(i, i + maxConcurrent)
|
|
232
|
+
await Promise.all(batch.map(async ({ driverSlice, dateChunk }) => {
|
|
233
|
+
const deviceIds = new Set()
|
|
234
|
+
driverSlice.forEach(d => {
|
|
235
|
+
const ids = deviceIdsByDriver.get(d.uniqueId)
|
|
236
|
+
if (ids) {
|
|
237
|
+
ids.forEach(id => deviceIds.add(id))
|
|
238
|
+
}
|
|
239
|
+
})
|
|
240
|
+
const slicedDevices = userData.devices.filter(device => deviceIds.has(device.id))
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
const response = await axios.post(url, {
|
|
244
|
+
from: dateChunk.from,
|
|
245
|
+
to: dateChunk.to,
|
|
246
|
+
userData: {
|
|
247
|
+
...userData,
|
|
248
|
+
drivers: driverSlice,
|
|
249
|
+
devices: slicedDevices,
|
|
250
|
+
geofences: [],
|
|
251
|
+
requestToRunServerSide: true,
|
|
252
|
+
rawMode: true
|
|
253
|
+
},
|
|
254
|
+
cookie
|
|
255
|
+
}, { withCredentials: true })
|
|
256
|
+
const result = response.data[0]
|
|
257
|
+
if (result) {
|
|
258
|
+
rawRows.push(...(result.drivers || []))
|
|
259
|
+
}
|
|
260
|
+
} catch (e) {
|
|
261
|
+
console.error(e)
|
|
262
|
+
}
|
|
263
|
+
completed++
|
|
264
|
+
console.log('LOADING_MESSAGE:' + driverSlice[0].name)
|
|
265
|
+
console.log(`PROGRESS_PERC:${(completed / cells.length) * 100}`)
|
|
266
|
+
}))
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return rawRows
|
|
194
270
|
}
|
|
195
271
|
|
|
196
272
|
function buildTargets (userData) {
|
|
@@ -241,6 +317,7 @@ async function processDevice (driversData, device, userData, allInOne, alarmEven
|
|
|
241
317
|
let driverData = driversData.get(d.id)
|
|
242
318
|
if (!driverData) {
|
|
243
319
|
driverData = {
|
|
320
|
+
driverId: d.id,
|
|
244
321
|
name: d.name,
|
|
245
322
|
distance: 0,
|
|
246
323
|
highEngineRPM: 0,
|