fleetmap-reports 1.0.956 → 1.0.958
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 +1 -1
- package/src/index.js +10 -0
- package/src/partnerReports/performance-report.js +135 -1
- package/src/tests/gpsjump.test.js +23 -0
- package/src/util/geofence.js +4 -2
- package/src/zone-report.js +26 -4
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -182,5 +182,15 @@ function Reports (config, axios, cookieJar) {
|
|
|
182
182
|
this.stopReportToExcel = (userData, reportData) => {
|
|
183
183
|
return require('./stop-report').exportStopReportToExcel(userData, reportData)
|
|
184
184
|
}
|
|
185
|
+
|
|
186
|
+
this.performanceReport = (from, to, userData) => {
|
|
187
|
+
return require('./partnerReports/performance-report').createPerformanceReport(from, to, userData, this.traccar)
|
|
188
|
+
}
|
|
189
|
+
this.performanceReportToPDF = (userData, reportData) => {
|
|
190
|
+
return require('./partnerReports/performance-report').exportPerformanceReportToPDF(userData, reportData)
|
|
191
|
+
}
|
|
192
|
+
this.performanceReportToExcel = (userData, reportData) => {
|
|
193
|
+
return require('./partnerReports/performance-report').exportPerformanceReportToExcel(userData, reportData)
|
|
194
|
+
}
|
|
185
195
|
}
|
|
186
196
|
module.exports = Reports
|
|
@@ -2,7 +2,11 @@ const automaticReports = require('../automaticReports')
|
|
|
2
2
|
const traccarHelper = require('../util/traccar')
|
|
3
3
|
const { getUncompletedTrip } = require('../util/trips')
|
|
4
4
|
const { getEndTripMessages, getCanDriverStyleMessages, parseEndTripMessage, parseCanDriverStyleMessage } = require('../util/xpert')
|
|
5
|
-
const { calculateDistance } = require('../util/utils')
|
|
5
|
+
const { calculateDistance, getTranslations, convertMS } = require('../util/utils')
|
|
6
|
+
const { addTable } = require('../util/pdfDocument')
|
|
7
|
+
const jsPDF = require('jspdf')
|
|
8
|
+
const { getStyle } = require('../reportStyle')
|
|
9
|
+
const { getUserPartner } = require('fleetmap-partners')
|
|
6
10
|
|
|
7
11
|
async function createPerformanceReport (from, to, userData, traccar) {
|
|
8
12
|
const reportData = []
|
|
@@ -214,4 +218,134 @@ function calculateRPMSections (route, min, max) {
|
|
|
214
218
|
return sections
|
|
215
219
|
}
|
|
216
220
|
|
|
221
|
+
function exportPerformanceReportToPDF (userData, reportData) {
|
|
222
|
+
const translations = getTranslations(userData)
|
|
223
|
+
|
|
224
|
+
const headers = [
|
|
225
|
+
translations.report.vehicle,
|
|
226
|
+
'Utilisation',
|
|
227
|
+
'Conduite',
|
|
228
|
+
'Ralenti',
|
|
229
|
+
'Totale (L)',
|
|
230
|
+
'Conduite (L)',
|
|
231
|
+
'Ralenti (L)',
|
|
232
|
+
'Conduite (Km)',
|
|
233
|
+
'Économique',
|
|
234
|
+
'Économique (L)',
|
|
235
|
+
'Économique (Km)',
|
|
236
|
+
'Cruise Control',
|
|
237
|
+
'Cruise Control (L)',
|
|
238
|
+
'Cruise Control (Km)',
|
|
239
|
+
'Tour moteur élevé',
|
|
240
|
+
'Accéleration brusque (Sum)',
|
|
241
|
+
'Freinage brusque (Sum)',
|
|
242
|
+
'Virages brusque'
|
|
243
|
+
]
|
|
244
|
+
|
|
245
|
+
const data = []
|
|
246
|
+
reportData.forEach(d => {
|
|
247
|
+
const row = [
|
|
248
|
+
d.device,
|
|
249
|
+
convertMS((d.drivingTime + d.idleTime) * 1000),
|
|
250
|
+
convertMS(d.drivingTime * 1000),
|
|
251
|
+
convertMS(d.idleTime * 1000),
|
|
252
|
+
d.drivingConsumption + d.idleConsumption,
|
|
253
|
+
d.drivingConsumption,
|
|
254
|
+
d.idleConsumption,
|
|
255
|
+
d.drivingDistance.toFixed(2),
|
|
256
|
+
convertMS(d.economicTime * 1000),
|
|
257
|
+
d.economicConsumption.toFixed(1),
|
|
258
|
+
d.economicDistance.toFixed(2),
|
|
259
|
+
convertMS(d.cruiseControlTime * 1000),
|
|
260
|
+
d.cruiseControlConsumption.toFixed(1),
|
|
261
|
+
d.cruiseControlDistance.toFixed(2),
|
|
262
|
+
convertMS(d.highEngineRPM * 1000),
|
|
263
|
+
d.hardAcceleration,
|
|
264
|
+
d.hardBraking,
|
|
265
|
+
d.hardCornering
|
|
266
|
+
]
|
|
267
|
+
data.push(row)
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
const doc = new jsPDF.jsPDF('l')
|
|
271
|
+
const style = getStyle(getUserPartner(userData.user))
|
|
272
|
+
|
|
273
|
+
const footValues = []
|
|
274
|
+
|
|
275
|
+
doc.setFontSize(11)
|
|
276
|
+
doc.text(new Date(userData.from).toLocaleString() + ' - ' + new Date(userData.to).toLocaleString(), 20, 33)
|
|
277
|
+
|
|
278
|
+
style.headerFontSize = 8
|
|
279
|
+
style.bodyFontSize = 7
|
|
280
|
+
const columnStyles = {}
|
|
281
|
+
for (let i = 1; i < 18; i++) {
|
|
282
|
+
columnStyles[i] = { halign: 'right' }
|
|
283
|
+
}
|
|
284
|
+
addTable(doc, headers, data, footValues, style, 40, columnStyles)
|
|
285
|
+
return doc
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function exportPerformanceReportToExcel (userData, reportData) {
|
|
289
|
+
const translations = getTranslations(userData)
|
|
290
|
+
|
|
291
|
+
const settings = {
|
|
292
|
+
sheetName: 'Rapport de Performance', // The name of the sheet
|
|
293
|
+
fileName: 'RapportPerformance' // The name of the spreadsheet
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const headers = [{ label: translations.report.vehicle, value: 'name' },
|
|
297
|
+
{ label: 'Totale Utilisation', value: 'totalUse' },
|
|
298
|
+
{ label: 'Conduite', value: 'drivingTime' },
|
|
299
|
+
{ label: 'Ralenti', value: 'idleTime' },
|
|
300
|
+
{ label: 'Totale (L)', value: 'totalConsumption' },
|
|
301
|
+
{ label: 'Conduite (L)', value: 'drivingConsumption' },
|
|
302
|
+
{ label: 'Ralenti (L)', value: 'idleConsumption' },
|
|
303
|
+
{ label: 'Conduite (Km)', value: 'drivingDistance' },
|
|
304
|
+
{ label: 'Économique', value: 'economicTime' },
|
|
305
|
+
{ label: 'Économique (L)', value: 'economicConsumption' },
|
|
306
|
+
{ label: 'Économique (Km)', value: 'economicDistance' },
|
|
307
|
+
{ label: 'Cruise Control', value: 'cruiseControlTime' },
|
|
308
|
+
{ label: 'Cruise Control (L)', value: 'cruiseControlConsumption' },
|
|
309
|
+
{ label: 'Cruise Control (Km)', value: 'cruiseControlDistance' },
|
|
310
|
+
{ label: 'Tour moteur élevé', value: 'highEngineRPM' },
|
|
311
|
+
{ label: 'Accéleration brusque (Sum)', value: 'hardAcceleration' },
|
|
312
|
+
{ label: 'Freinage brusque (Sum)', value: 'hardBraking' },
|
|
313
|
+
{ label: 'Virages brusque', value: 'hardCornering' }]
|
|
314
|
+
|
|
315
|
+
let data = []
|
|
316
|
+
data = data.concat([{}])
|
|
317
|
+
/*data = data.concat(reportData.map(d => {
|
|
318
|
+
return {
|
|
319
|
+
name: d.device,
|
|
320
|
+
totalUse: convertMS((d.drivingTime + d.idleTime) * 1000),
|
|
321
|
+
drivingTime: convertMS(d.drivingTime * 1000),
|
|
322
|
+
idleTime: convertMS(d.idleTime * 1000),
|
|
323
|
+
totalConsumption: d.drivingConsumption + d.idleConsumption,
|
|
324
|
+
drivingConsumption: d.drivingConsumption,
|
|
325
|
+
idleConsumption: d.idleConsumption,
|
|
326
|
+
drivingDistance: d.drivingDistance,
|
|
327
|
+
economicTime: convertMS(d.economicTime * 1000),
|
|
328
|
+
economicConsumption: d.economicConsumption,
|
|
329
|
+
economicDistance: d.economicDistance,
|
|
330
|
+
cruiseControlTime: convertMS(d.cruiseControlTime * 1000),
|
|
331
|
+
cruiseControlConsumption: d.cruiseControlConsumption,
|
|
332
|
+
cruiseControlDistance: d.cruiseControlDistance,
|
|
333
|
+
highEngineRPM: d.highEngineRPM,
|
|
334
|
+
hardAcceleration: d.hardAcceleration,
|
|
335
|
+
hardBraking: d.hardBraking,
|
|
336
|
+
hardCornering: d.hardCornering
|
|
337
|
+
}
|
|
338
|
+
}))*/
|
|
339
|
+
|
|
340
|
+
console.log(data)
|
|
341
|
+
|
|
342
|
+
return {
|
|
343
|
+
headers,
|
|
344
|
+
data,
|
|
345
|
+
settings
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
217
349
|
exports.createPerformanceReport = createPerformanceReport
|
|
350
|
+
exports.exportPerformanceReportToPDF = exportPerformanceReportToPDF
|
|
351
|
+
exports.exportPerformanceReportToExcel = exportPerformanceReportToExcel
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const { getReports } = require('./index')
|
|
2
|
+
const assert = require('assert')
|
|
3
|
+
const { convertMS } = require('../util/utils')
|
|
4
|
+
const { createGPSJumpReport } = require('../partnerReports/gpsjump-report')
|
|
5
|
+
|
|
6
|
+
// eslint-disable-next-line no-undef
|
|
7
|
+
describe('gpsjump', function () {
|
|
8
|
+
// eslint-disable-next-line no-undef
|
|
9
|
+
it('gpsjump report', async () => {
|
|
10
|
+
const report = await getReports()
|
|
11
|
+
const userData = await report.getUserData()
|
|
12
|
+
userData.devices = userData.devices.filter(d => d.id === 22326)
|
|
13
|
+
const data = await createGPSJumpReport(
|
|
14
|
+
new Date(Date.UTC(2023, 6, 1, 0, 0, 0, 0)),
|
|
15
|
+
new Date(Date.UTC(2023, 6, 17, 23, 59, 59, 0)),
|
|
16
|
+
userData,
|
|
17
|
+
report.traccar)
|
|
18
|
+
console.log(data)
|
|
19
|
+
assert.equal(data[0].consumption, 19.799999999999997)
|
|
20
|
+
assert.equal(data[0].distance, 326.7657)
|
|
21
|
+
assert.equal(convertMS(data[1].time * 1000, false), '08:19')
|
|
22
|
+
}, 8000000)
|
|
23
|
+
})
|
package/src/util/geofence.js
CHANGED
|
@@ -22,13 +22,15 @@ function getNearestPOIs (long, lat, geofences) {
|
|
|
22
22
|
.filter(g => g && g.area.startsWith('CIRCLE'))
|
|
23
23
|
.map(g => {
|
|
24
24
|
const str = g.area.substring('CIRCLE ('.length, g.area.indexOf(','))
|
|
25
|
+
const radius = g.area.substring(g.area.indexOf(',') + 1, g.area.indexOf(')'))
|
|
25
26
|
const coord = str.trim().split(' ')
|
|
26
27
|
return {
|
|
27
28
|
p: g,
|
|
28
|
-
distance: Math.round(coordsDistance(parseFloat(coord[1]), parseFloat(coord[0]), long, lat))
|
|
29
|
+
distance: Math.round(coordsDistance(parseFloat(coord[1]), parseFloat(coord[0]), long, lat)),
|
|
30
|
+
r: parseFloat(radius)
|
|
29
31
|
}
|
|
30
32
|
})
|
|
31
|
-
return distance.filter(a => a.distance <
|
|
33
|
+
return distance.filter(a => a.distance < a.r).sort((a, b) => (a.distance > b.distance) ? 1 : -1)
|
|
32
34
|
}
|
|
33
35
|
exports.getNearestPOIs = getNearestPOIs
|
|
34
36
|
|
package/src/zone-report.js
CHANGED
|
@@ -270,6 +270,19 @@ function getAnyNextIn (outDate, alerts, deviceRoute) {
|
|
|
270
270
|
}
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
+
function getAnyLastExit (inDate, alerts, deviceRoute) {
|
|
274
|
+
const next = alerts
|
|
275
|
+
.filter(a => a.type === 'geofenceExit')
|
|
276
|
+
.find(a => new Date(a.position.fixTime).getTime() < inDate)
|
|
277
|
+
|
|
278
|
+
if (next && !alerts.filter(a => a.type === 'geofenceExit' &&
|
|
279
|
+
new Date(a.position.fixTime).getTime() < inDate &&
|
|
280
|
+
new Date(a.position.fixTime).getTime() > new Date(next.position.fixTime).getTime()).length) {
|
|
281
|
+
return (next && next.position && new Date(next.position.fixTime).getTime()) ||
|
|
282
|
+
new Date(deviceRoute.slice(-1)[0].fixTime).getTime()
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
273
286
|
function calculateDistanceIn (zoneInOutDayData, deviceRoute, dayRoute, fromByDay, toByDay, to, device) {
|
|
274
287
|
let distanceIn = zoneInOutDayData.length && dayRoute.length ? zoneInOutDayData.reduce((a, b) => a + (b.distanceIn || 0), 0) : 0
|
|
275
288
|
|
|
@@ -297,7 +310,7 @@ function calculateDistanceOut (zoneInOutDayData, dayRoute, fromByDay, toByDay, f
|
|
|
297
310
|
const dataInsideDay = zoneInOutDayData.filter(z => z.outTime && new Date(z.outTime.fixTime) < toByDay)
|
|
298
311
|
let distanceOut = dataInsideDay.reduce((a, b) => a + (b[field] || 0), 0)
|
|
299
312
|
|
|
300
|
-
if (zoneInOutDayData[0].inTime && new Date(zoneInOutDayData[0].inTime.fixTime) > fromByDay) {
|
|
313
|
+
if (zoneInOutDayData[0].inTime && new Date(zoneInOutDayData[0].inTime.fixTime) > fromByDay && (field !== 'distanceOutAny' || zoneInOutDayData[0].anyLastExit)) {
|
|
301
314
|
// Add distanceOut before the first entry
|
|
302
315
|
const routeOut = dayRoute.filter(p =>
|
|
303
316
|
new Date(p.fixTime).getTime() < new Date(zoneInOutDayData[0].inTime.fixTime).getTime() &&
|
|
@@ -306,7 +319,8 @@ function calculateDistanceOut (zoneInOutDayData, dayRoute, fromByDay, toByDay, f
|
|
|
306
319
|
distanceOut = distanceOut + calculateDistance(routeOut, device.attributes['report.ignoreOdometer'])
|
|
307
320
|
}
|
|
308
321
|
|
|
309
|
-
if (zoneInOutDayData[zoneInOutDayData.length - 1].outTime && new Date(zoneInOutDayData[zoneInOutDayData.length - 1].outTime.fixTime) < toByDay) {
|
|
322
|
+
if (zoneInOutDayData[zoneInOutDayData.length - 1].outTime && new Date(zoneInOutDayData[zoneInOutDayData.length - 1].outTime.fixTime) < toByDay && (field !== 'distanceOutAny' || zoneInOutDayData[zoneInOutDayData.length - 1].anyNextIn)) {
|
|
323
|
+
// Add distanceOut after last exit
|
|
310
324
|
distanceOut = distanceOut - zoneInOutDayData[zoneInOutDayData.length - 1][field]
|
|
311
325
|
const routeOut = dayRoute.filter(p =>
|
|
312
326
|
new Date(p.fixTime) >= new Date(zoneInOutDayData[zoneInOutDayData.length - 1].outTime.fixTime).getTime() &&
|
|
@@ -331,6 +345,10 @@ function analyseAlerts (alerts, deviceRoute, userData, from, to, device) {
|
|
|
331
345
|
if (a.type === 'geofenceEnter') {
|
|
332
346
|
const driver = userData.drivers.find(d => a.position.attributes && d.uniqueId === a.position.attributes.driverUniqueId)
|
|
333
347
|
a.position.driverName = driver ? driver.name : ''
|
|
348
|
+
if (Object.keys(zoneInData).length === 0) {
|
|
349
|
+
a.anyLastExit = getAnyLastExit(new Date(a.position.fixTime).getTime(), alerts, deviceRoute)
|
|
350
|
+
}
|
|
351
|
+
|
|
334
352
|
zoneInData[a.geofenceId] = a
|
|
335
353
|
} else if (a.type === 'geofenceExit') {
|
|
336
354
|
const geofence = userData.geofences.find(g => g.id === a.geofenceId)
|
|
@@ -368,6 +386,8 @@ function analyseAlerts (alerts, deviceRoute, userData, from, to, device) {
|
|
|
368
386
|
distanceIn: calculateDistance(routeIn, device.attributes['report.ignoreOdometer']),
|
|
369
387
|
distanceOut,
|
|
370
388
|
distanceOutAny,
|
|
389
|
+
anyNextIn,
|
|
390
|
+
anyLastExit: a.anyLastExit,
|
|
371
391
|
geofenceName: geofence.name,
|
|
372
392
|
geofenceId: geofence.id,
|
|
373
393
|
stopped: routeIn.filter(p => !p.attributes.ignition).length > 0,
|
|
@@ -389,7 +409,9 @@ function analyseAlerts (alerts, deviceRoute, userData, from, to, device) {
|
|
|
389
409
|
distanceIn: calculateDistance(routeIn, device.attributes['report.ignoreOdometer']),
|
|
390
410
|
driverName: '',
|
|
391
411
|
distanceOut,
|
|
392
|
-
distanceOutAny
|
|
412
|
+
distanceOutAny,
|
|
413
|
+
anyNextIn,
|
|
414
|
+
anyLastExit: a.anyLastExit
|
|
393
415
|
})
|
|
394
416
|
}
|
|
395
417
|
}
|
|
@@ -417,7 +439,7 @@ function analyseAlerts (alerts, deviceRoute, userData, from, to, device) {
|
|
|
417
439
|
}
|
|
418
440
|
|
|
419
441
|
if (!zoneInOutData.length && userData.groupByDay) {
|
|
420
|
-
const geofenceIn = alerts.find(a => a.type === '
|
|
442
|
+
const geofenceIn = alerts.find(a => a.type === 'geofenceEnter')
|
|
421
443
|
zoneInOutData.push({
|
|
422
444
|
distanceIn: geofenceIn ? calculateDistance(deviceRoute, device.attributes['report.ignoreOdometer']) : 0,
|
|
423
445
|
distanceOut: !geofenceIn ? calculateDistance(deviceRoute, device.attributes['report.ignoreOdometer']) : 0,
|