fleetmap-reports 2.0.357 → 2.0.359
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/lang/enGB.js +18 -0
- package/lang/esCL.js +18 -0
- package/lang/frFR.json +7 -1
- package/lang/ptBR.js +18 -0
- package/lang/ptPT.js +18 -0
- package/package.json +1 -1
- package/src/partnerReports/driverranking-report.js +188 -56
- package/src/util/pdfDocument.js +2 -1
package/lang/enGB.js
CHANGED
|
@@ -193,7 +193,25 @@ module.exports = {
|
|
|
193
193
|
titleIdleReport: 'Idle Report',
|
|
194
194
|
titleMachinesReport: 'Machines Report',
|
|
195
195
|
titleStopReport: 'Stop Report',
|
|
196
|
+
titleDriverRankingReport: 'Driver Ranking Report',
|
|
196
197
|
titleSensorReport: 'Sensor Report',
|
|
198
|
+
rank: 'Rank',
|
|
199
|
+
grade: 'Grade',
|
|
200
|
+
safetyScore: 'Safety',
|
|
201
|
+
efficiencyScore: 'Efficiency',
|
|
202
|
+
complianceScore: 'Compliance',
|
|
203
|
+
score: 'Overall Score',
|
|
204
|
+
avgFuelConsumption: 'Avg. L/100km',
|
|
205
|
+
highEngineRPM: 'High RPM',
|
|
206
|
+
hardBraking: 'Hard Braking',
|
|
207
|
+
hardAcceleration: 'Hard Acceleration',
|
|
208
|
+
hardCornering: 'Hard Cornering',
|
|
209
|
+
overspeedAlarm: 'Speeding',
|
|
210
|
+
geofenceAlarm: 'Seatbelt',
|
|
211
|
+
continuesDrivingAlarm: 'Continuous Driving',
|
|
212
|
+
reverseAlarm: 'Reverse Gear',
|
|
213
|
+
otherAlarm: 'Other',
|
|
214
|
+
detailedMetrics: 'Detailed metrics',
|
|
197
215
|
from: 'From',
|
|
198
216
|
to: 'to',
|
|
199
217
|
headerStartAddress: 'Start address',
|
package/lang/esCL.js
CHANGED
|
@@ -183,7 +183,25 @@ module.exports = {
|
|
|
183
183
|
titleIdleReport: 'Informe de Ralenti',
|
|
184
184
|
titleMachinesReport: 'Informe de Maquinas',
|
|
185
185
|
titleStopReport: 'Informe de Paradas',
|
|
186
|
+
titleDriverRankingReport: 'Informe de Ranking de Conductores',
|
|
186
187
|
titleSensorReport: 'Informe de Sensores',
|
|
188
|
+
rank: 'Posición',
|
|
189
|
+
grade: 'Calificación',
|
|
190
|
+
safetyScore: 'Seguridad',
|
|
191
|
+
efficiencyScore: 'Eficiencia',
|
|
192
|
+
complianceScore: 'Cumplimiento',
|
|
193
|
+
score: 'Puntuación General',
|
|
194
|
+
avgFuelConsumption: 'Consumo Prom. L/100km',
|
|
195
|
+
highEngineRPM: 'RPM Alto',
|
|
196
|
+
hardBraking: 'Frenado Brusco',
|
|
197
|
+
hardAcceleration: 'Aceleración Brusca',
|
|
198
|
+
hardCornering: 'Giro Brusco',
|
|
199
|
+
overspeedAlarm: 'Exceso de Velocidad',
|
|
200
|
+
geofenceAlarm: 'Cinturón de Seguridad',
|
|
201
|
+
continuesDrivingAlarm: 'Conducción Continua',
|
|
202
|
+
reverseAlarm: 'Marcha Atrás',
|
|
203
|
+
otherAlarm: 'Otros',
|
|
204
|
+
detailedMetrics: 'Métricas detalladas',
|
|
187
205
|
from: 'De',
|
|
188
206
|
to: 'a',
|
|
189
207
|
headerStartAddress: 'Lugar de início',
|
package/lang/frFR.json
CHANGED
|
@@ -297,7 +297,13 @@
|
|
|
297
297
|
"geofenceAlarm": "Ceinture",
|
|
298
298
|
"continuesDrivingAlarm": "Cond. Continue",
|
|
299
299
|
"reverseAlarm": "Marche Arriere",
|
|
300
|
-
"otherAlarm": "Other"
|
|
300
|
+
"otherAlarm": "Other",
|
|
301
|
+
"rank": "Rang",
|
|
302
|
+
"grade": "Note",
|
|
303
|
+
"safetyScore": "Sécurité",
|
|
304
|
+
"efficiencyScore": "Efficacité",
|
|
305
|
+
"complianceScore": "Conformité",
|
|
306
|
+
"detailedMetrics": "Métriques détaillées"
|
|
301
307
|
},
|
|
302
308
|
"layout": {
|
|
303
309
|
"deviceOnline": "Appareil en ligne",
|
package/lang/ptBR.js
CHANGED
|
@@ -185,7 +185,25 @@ module.exports = {
|
|
|
185
185
|
titleIdleReport: 'Relatórios de Ralenti',
|
|
186
186
|
titleMachinesReport: 'Relatório de Máquinas',
|
|
187
187
|
titleStopReport: 'Relatório de Paragens',
|
|
188
|
+
titleDriverRankingReport: 'Relatório de Ranking de Motoristas',
|
|
188
189
|
titleSensorReport: 'Relatório de Sensores',
|
|
190
|
+
rank: 'Posição',
|
|
191
|
+
grade: 'Nota',
|
|
192
|
+
safetyScore: 'Segurança',
|
|
193
|
+
efficiencyScore: 'Eficiência',
|
|
194
|
+
complianceScore: 'Conformidade',
|
|
195
|
+
score: 'Pontuação Geral',
|
|
196
|
+
avgFuelConsumption: 'Consumo Médio L/100km',
|
|
197
|
+
highEngineRPM: 'RPM Alta',
|
|
198
|
+
hardBraking: 'Frenagem Brusca',
|
|
199
|
+
hardAcceleration: 'Aceleração Brusca',
|
|
200
|
+
hardCornering: 'Curva Brusca',
|
|
201
|
+
overspeedAlarm: 'Excesso de Velocidade',
|
|
202
|
+
geofenceAlarm: 'Cinto de Segurança',
|
|
203
|
+
continuesDrivingAlarm: 'Condução Contínua',
|
|
204
|
+
reverseAlarm: 'Marcha à Ré',
|
|
205
|
+
otherAlarm: 'Outros',
|
|
206
|
+
detailedMetrics: 'Métricas detalhadas',
|
|
189
207
|
from: 'De',
|
|
190
208
|
to: 'a',
|
|
191
209
|
headerStartAddress: 'Local de início',
|
package/lang/ptPT.js
CHANGED
|
@@ -191,7 +191,25 @@ module.exports = {
|
|
|
191
191
|
titleIdleReport: 'Relatórios de Ralenti',
|
|
192
192
|
titleMachinesReport: 'Relatório de Máquinas',
|
|
193
193
|
titleStopReport: 'Relatório de Paragens',
|
|
194
|
+
titleDriverRankingReport: 'Relatório de Ranking de Condutores',
|
|
194
195
|
titleSensorReport: 'Relatório de Sensores',
|
|
196
|
+
rank: 'Posição',
|
|
197
|
+
grade: 'Nota',
|
|
198
|
+
safetyScore: 'Segurança',
|
|
199
|
+
efficiencyScore: 'Eficiência',
|
|
200
|
+
complianceScore: 'Conformidade',
|
|
201
|
+
score: 'Pontuação Geral',
|
|
202
|
+
avgFuelConsumption: 'Consumo Médio L/100km',
|
|
203
|
+
highEngineRPM: 'RPM Alto',
|
|
204
|
+
hardBraking: 'Travagem Brusca',
|
|
205
|
+
hardAcceleration: 'Aceleração Brusca',
|
|
206
|
+
hardCornering: 'Curva Brusca',
|
|
207
|
+
overspeedAlarm: 'Excesso de Velocidade',
|
|
208
|
+
geofenceAlarm: 'Cinto de Segurança',
|
|
209
|
+
continuesDrivingAlarm: 'Condução Contínua',
|
|
210
|
+
reverseAlarm: 'Marcha-atrás',
|
|
211
|
+
otherAlarm: 'Outros',
|
|
212
|
+
detailedMetrics: 'Métricas detalhadas',
|
|
195
213
|
from: 'De',
|
|
196
214
|
to: 'a',
|
|
197
215
|
headerStartAddress: 'Local de início',
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@ const traccarHelper = require('../util/traccar')
|
|
|
2
2
|
const { getDriverData, devicesByDriver } = require('../util/driver')
|
|
3
3
|
const { getCanDriverStyleMessages, parseCanDriverStyleMessage } = require('../util/xpert')
|
|
4
4
|
const { calculateRPMSections } = require('./performance-report')
|
|
5
|
-
const { getTranslations, isClientSide, convertToLocaleString, convertMS } = require('../util/utils')
|
|
5
|
+
const { getTranslations, isClientSide, convertToLocaleString, convertMS, executeServerSide } = require('../util/utils')
|
|
6
6
|
const jsPDF = require('jspdf')
|
|
7
7
|
const { headerFromUser, addTable } = require('../util/pdfDocument')
|
|
8
8
|
const { getStyle } = require('../reportStyle')
|
|
@@ -24,6 +24,39 @@ const continuesDrivingCoefficient = 0.1
|
|
|
24
24
|
const reverseCoefficient = 0.1
|
|
25
25
|
const otherCoefficient = 0.1
|
|
26
26
|
|
|
27
|
+
const grades = [
|
|
28
|
+
{ min: 90, grade: 'A' },
|
|
29
|
+
{ min: 75, grade: 'B' },
|
|
30
|
+
{ min: 60, grade: 'C' },
|
|
31
|
+
{ min: 40, grade: 'D' },
|
|
32
|
+
{ min: -Infinity, grade: 'F' }
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
// Converts a "the lower the better" metric into a 0-100 score (100 = met target, 0 = twice the target or worse)
|
|
36
|
+
function goodnessScore (actual, target) {
|
|
37
|
+
if (!target || target <= 0) {
|
|
38
|
+
return 100
|
|
39
|
+
}
|
|
40
|
+
return Math.max(0, Math.min(100, 100 * (1 - (actual / target))))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Same as goodnessScore, but for metrics where deviating from the target in either direction is bad (e.g. distance)
|
|
44
|
+
function deviationScore (actual, target) {
|
|
45
|
+
if (!target || target <= 0) {
|
|
46
|
+
return 100
|
|
47
|
+
}
|
|
48
|
+
return Math.max(0, Math.min(100, 100 * (1 - (Math.abs(actual - target) / target))))
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function weightedAverage (metrics) {
|
|
52
|
+
const totalWeight = metrics.reduce((sum, m) => sum + m.weight, 0)
|
|
53
|
+
return metrics.reduce((sum, m) => sum + (m.value * m.weight), 0) / totalWeight
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function toGrade (score) {
|
|
57
|
+
return grades.find(g => score >= g.min).grade
|
|
58
|
+
}
|
|
59
|
+
|
|
27
60
|
async function create (from, to, userData, traccar) {
|
|
28
61
|
const reportData = {
|
|
29
62
|
drivers: []
|
|
@@ -32,9 +65,28 @@ async function create (from, to, userData, traccar) {
|
|
|
32
65
|
userData.geofences = []
|
|
33
66
|
userData.roadSpeedLimits = true
|
|
34
67
|
|
|
35
|
-
|
|
36
|
-
|
|
68
|
+
if (!userData.requestToRunServerSide) {
|
|
69
|
+
const { devices, drivers } = await devicesByDriver(traccar, from, to, userData.drivers, userData.devices)
|
|
70
|
+
userData.devices = devices
|
|
71
|
+
userData.drivers = drivers
|
|
72
|
+
}
|
|
37
73
|
|
|
74
|
+
if (isClientSide() && (userData.drivers.length > 50 || ((new Date(to).getTime() - new Date(from).getTime()) > (1000 * 60 * 60 * 24 * 15)))) {
|
|
75
|
+
const sliced = automaticReports.sliceArray(userData.drivers, 5)
|
|
76
|
+
userData.requestToRunServerSide = true
|
|
77
|
+
userData.byDriver = true
|
|
78
|
+
const allDriversData = await executeServerSide('driverranking-report', sliced, from, to, userData, userData.drivers.length, traccar, 5)
|
|
79
|
+
|
|
80
|
+
allDriversData.sort((a, b) => b.overallScore - a.overallScore)
|
|
81
|
+
allDriversData.forEach((d, index) => { d.rank = index + 1 })
|
|
82
|
+
|
|
83
|
+
reportData.drivers = allDriversData
|
|
84
|
+
reportData.target = buildTargets(userData)
|
|
85
|
+
reportData.coefficient = buildCoefficients()
|
|
86
|
+
return reportData
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const devices = userData.devices
|
|
38
90
|
let deviceCount = 0
|
|
39
91
|
const driversData = new Map()
|
|
40
92
|
const overspeedEvents = await getOverspeedEvents(from, to, userData, devices, userData, traccar)
|
|
@@ -74,28 +126,40 @@ async function create (from, to, userData, traccar) {
|
|
|
74
126
|
|
|
75
127
|
const allDriversData = (Array.from(driversData.values())).filter(d => d.distance > 0)
|
|
76
128
|
allDriversData.forEach(d => {
|
|
77
|
-
const distanceScore = (Math.abs(d.distance - userData.distanceTarget) / userData.distanceTarget) * distanceCoefficient
|
|
78
|
-
const rpmScore = ((d.highEngineRPM / 60000) / userData.rpmTarget) * rpmCoefficient
|
|
79
|
-
const hardBrakingScore = (d.hardBraking / userData.hardBrakingTarget) * hardBrakingCoefficient
|
|
80
|
-
const hardAccelerationScore = (d.hardAcceleration / userData.hardAccelerationTarget) * hardAccelerationCoefficient
|
|
81
|
-
const hardCorneringScore = (d.hardCornering / userData.hardCorneringTarget) * hardCorneringCoefficient
|
|
82
|
-
const overspeedScore = (d.overspeed / userData.overspeedTarget) * overspeedCoefficient
|
|
83
|
-
const consumptionScore = (getCanAvgConsumption(d.distance, d.spentFuel).byKms / userData.fuelConsumptionTarget) * consumptionCoefficient
|
|
84
|
-
const geofenceAlarmScore = (d.geofenceAlarm / userData.geofenceTarget) * geofenceCoefficient
|
|
85
|
-
const continuesDrivingAlarmScore = (d.continuesDrivingAlarm / userData.continuesDrivingTarget) * continuesDrivingCoefficient
|
|
86
|
-
const reverseAlarmScore = (d.reverseAlarm / userData.reverseTarget) * reverseCoefficient
|
|
87
|
-
const otherAlarmScore = (d.otherAlarm / userData.otherTarget) * otherCoefficient
|
|
88
|
-
|
|
89
129
|
d.avgFuelConsumption = getCanAvgConsumption(d.distance, d.spentFuel).byKms
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
130
|
+
|
|
131
|
+
d.safetyScore = weightedAverage([
|
|
132
|
+
{ value: goodnessScore(d.hardBraking, userData.hardBrakingTarget), weight: hardBrakingCoefficient },
|
|
133
|
+
{ value: goodnessScore(d.hardAcceleration, userData.hardAccelerationTarget), weight: hardAccelerationCoefficient },
|
|
134
|
+
{ value: goodnessScore(d.hardCornering, userData.hardCorneringTarget), weight: hardCorneringCoefficient },
|
|
135
|
+
{ value: goodnessScore(d.overspeed, userData.overspeedTarget), weight: overspeedCoefficient },
|
|
136
|
+
{ value: goodnessScore(d.reverseAlarm, userData.reverseTarget), weight: reverseCoefficient }
|
|
137
|
+
])
|
|
138
|
+
d.efficiencyScore = weightedAverage([
|
|
139
|
+
{ value: deviationScore(d.distance, userData.distanceTarget), weight: distanceCoefficient },
|
|
140
|
+
{ value: goodnessScore(d.avgFuelConsumption, userData.fuelConsumptionTarget), weight: consumptionCoefficient },
|
|
141
|
+
{ value: goodnessScore(d.highEngineRPM / 60000, userData.rpmTarget), weight: rpmCoefficient }
|
|
142
|
+
])
|
|
143
|
+
d.complianceScore = weightedAverage([
|
|
144
|
+
{ value: goodnessScore(d.geofenceAlarm, userData.geofenceTarget), weight: geofenceCoefficient },
|
|
145
|
+
{ value: goodnessScore(d.continuesDrivingAlarm, userData.continuesDrivingTarget), weight: continuesDrivingCoefficient },
|
|
146
|
+
{ value: goodnessScore(d.otherAlarm, userData.otherTarget), weight: otherCoefficient }
|
|
147
|
+
])
|
|
148
|
+
d.overallScore = (d.safetyScore + d.efficiencyScore + d.complianceScore) / 3
|
|
149
|
+
d.grade = toGrade(d.overallScore)
|
|
93
150
|
})
|
|
94
|
-
allDriversData.sort((a, b) =>
|
|
151
|
+
allDriversData.sort((a, b) => b.overallScore - a.overallScore)
|
|
152
|
+
allDriversData.forEach((d, index) => { d.rank = index + 1 })
|
|
95
153
|
|
|
96
154
|
reportData.drivers.push(...allDriversData)
|
|
155
|
+
reportData.target = buildTargets(userData)
|
|
156
|
+
reportData.coefficient = buildCoefficients()
|
|
97
157
|
|
|
98
|
-
reportData
|
|
158
|
+
return reportData
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function buildTargets (userData) {
|
|
162
|
+
return {
|
|
99
163
|
distanceTarget: userData.distanceTarget,
|
|
100
164
|
rpmTarget: userData.rpmTarget,
|
|
101
165
|
hardBrakingTarget: userData.hardBrakingTarget,
|
|
@@ -108,8 +172,10 @@ async function create (from, to, userData, traccar) {
|
|
|
108
172
|
reverseTarget: userData.reverseTarget,
|
|
109
173
|
otherTarget: userData.otherTarget
|
|
110
174
|
}
|
|
175
|
+
}
|
|
111
176
|
|
|
112
|
-
|
|
177
|
+
function buildCoefficients () {
|
|
178
|
+
return {
|
|
113
179
|
distanceCoefficient,
|
|
114
180
|
rpmCoefficient,
|
|
115
181
|
hardBrakingCoefficient,
|
|
@@ -122,8 +188,6 @@ async function create (from, to, userData, traccar) {
|
|
|
122
188
|
reverseCoefficient,
|
|
123
189
|
otherCoefficient
|
|
124
190
|
}
|
|
125
|
-
|
|
126
|
-
return reportData
|
|
127
191
|
}
|
|
128
192
|
|
|
129
193
|
async function processDevice (driversData, device, userData, allInOne, alarmEvents, overspeedEvents) {
|
|
@@ -207,7 +271,13 @@ async function exportToExcel (userData, reportData) {
|
|
|
207
271
|
}
|
|
208
272
|
|
|
209
273
|
const headers = [
|
|
274
|
+
{ label: translations.report.rank, value: 'rank' },
|
|
210
275
|
{ label: translations.report.driver, value: 'name' },
|
|
276
|
+
{ label: translations.report.grade, value: 'grade' },
|
|
277
|
+
{ label: translations.report.score, value: 'overallScore' },
|
|
278
|
+
{ label: translations.report.safetyScore, value: 'safetyScore' },
|
|
279
|
+
{ label: translations.report.efficiencyScore, value: 'efficiencyScore' },
|
|
280
|
+
{ label: translations.report.complianceScore, value: 'complianceScore' },
|
|
211
281
|
{ label: translations.report.distance, value: 'distance' },
|
|
212
282
|
{ label: translations.report.avgFuelConsumption, value: 'avgFuelConsumption' },
|
|
213
283
|
{ label: translations.report.highEngineRPM, value: 'highEngineRPM' },
|
|
@@ -218,16 +288,21 @@ async function exportToExcel (userData, reportData) {
|
|
|
218
288
|
{ label: translations.report.geofenceAlarm, value: 'geofenceAlarm' },
|
|
219
289
|
{ label: translations.report.continuesDrivingAlarm, value: 'continuesDrivingAlarm' },
|
|
220
290
|
{ label: translations.report.reverseAlarm, value: 'reverseAlarm' },
|
|
221
|
-
{ label: translations.report.otherAlarm, value: 'otherAlarm' }
|
|
222
|
-
{ label: translations.report.score, value: 'score' }
|
|
291
|
+
{ label: translations.report.otherAlarm, value: 'otherAlarm' }
|
|
223
292
|
]
|
|
224
293
|
|
|
225
294
|
let data = []
|
|
226
295
|
if (reportDataRows) {
|
|
227
296
|
reportDataRows.forEach(row => {
|
|
228
297
|
data = data.concat({
|
|
298
|
+
rank: row.rank,
|
|
229
299
|
name: row.name,
|
|
230
|
-
|
|
300
|
+
grade: row.grade,
|
|
301
|
+
overallScore: Math.round(row.overallScore),
|
|
302
|
+
safetyScore: Math.round(row.safetyScore),
|
|
303
|
+
efficiencyScore: Math.round(row.efficiencyScore),
|
|
304
|
+
complianceScore: Math.round(row.complianceScore),
|
|
305
|
+
distance: Number((row.distance / 1000).toFixed(1)),
|
|
231
306
|
avgFuelConsumption: Number(row.avgFuelConsumption.toFixed(2)),
|
|
232
307
|
highEngineRPM: convertMS(row.highEngineRPM, false),
|
|
233
308
|
hardBraking: row.hardBraking,
|
|
@@ -237,11 +312,9 @@ async function exportToExcel (userData, reportData) {
|
|
|
237
312
|
geofenceAlarm: row.geofenceAlarm,
|
|
238
313
|
continuesDrivingAlarm: row.continuesDrivingAlarm,
|
|
239
314
|
reverseAlarm: row.reverseAlarm,
|
|
240
|
-
otherAlarm: row.otherAlarm
|
|
241
|
-
score: Number(row.score.toFixed(2))
|
|
315
|
+
otherAlarm: row.otherAlarm
|
|
242
316
|
})
|
|
243
317
|
})
|
|
244
|
-
console.log(data)
|
|
245
318
|
return {
|
|
246
319
|
headers,
|
|
247
320
|
data,
|
|
@@ -250,15 +323,33 @@ async function exportToExcel (userData, reportData) {
|
|
|
250
323
|
}
|
|
251
324
|
}
|
|
252
325
|
|
|
326
|
+
const gradeColors = {
|
|
327
|
+
A: { fill: [200, 236, 204], text: [30, 110, 40] },
|
|
328
|
+
B: { fill: [223, 240, 196], text: [66, 110, 30] },
|
|
329
|
+
C: { fill: [255, 236, 179], text: [153, 101, 0] },
|
|
330
|
+
D: { fill: [255, 205, 178], text: [166, 74, 0] },
|
|
331
|
+
F: { fill: [255, 205, 210], text: [153, 0, 20] }
|
|
332
|
+
}
|
|
333
|
+
|
|
253
334
|
async function exportToPDF (userData, reportData) {
|
|
254
|
-
console.log('
|
|
335
|
+
console.log('exportDriverRankingReportToPDF')
|
|
255
336
|
|
|
256
337
|
const timezone = userData.user.attributes.timezone
|
|
257
338
|
const translations = getTranslations(userData)
|
|
258
339
|
const lang = userData.user.attributes.lang || (isClientSide() && navigator.language)
|
|
259
340
|
const reportDataRows = reportData.drivers
|
|
260
341
|
|
|
261
|
-
const
|
|
342
|
+
const scoreboardHeaders = [
|
|
343
|
+
translations.report.rank,
|
|
344
|
+
translations.report.driver,
|
|
345
|
+
translations.report.grade,
|
|
346
|
+
translations.report.score,
|
|
347
|
+
translations.report.safetyScore,
|
|
348
|
+
translations.report.efficiencyScore,
|
|
349
|
+
translations.report.complianceScore
|
|
350
|
+
]
|
|
351
|
+
|
|
352
|
+
const detailHeaders = [
|
|
262
353
|
translations.report.driver,
|
|
263
354
|
translations.report.distance,
|
|
264
355
|
translations.report.avgFuelConsumption,
|
|
@@ -270,8 +361,7 @@ async function exportToPDF (userData, reportData) {
|
|
|
270
361
|
translations.report.geofenceAlarm,
|
|
271
362
|
translations.report.continuesDrivingAlarm,
|
|
272
363
|
translations.report.reverseAlarm,
|
|
273
|
-
translations.report.otherAlarm
|
|
274
|
-
translations.report.score
|
|
364
|
+
translations.report.otherAlarm
|
|
275
365
|
]
|
|
276
366
|
|
|
277
367
|
if (reportDataRows) {
|
|
@@ -282,30 +372,66 @@ async function exportToPDF (userData, reportData) {
|
|
|
282
372
|
doc.setFontSize(13)
|
|
283
373
|
doc.text(convertToLocaleString(reportData.from, lang, timezone, userData.user) + ' - ' + convertToLocaleString(reportData.to, lang, timezone, userData.user), 20, 25)
|
|
284
374
|
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
row.hardCornering,
|
|
295
|
-
row.overspeed,
|
|
296
|
-
row.geofenceAlarm,
|
|
297
|
-
row.continuesDrivingAlarm,
|
|
298
|
-
row.reverseAlarm,
|
|
299
|
-
row.otherAlarm,
|
|
300
|
-
row.score.toFixed(2)
|
|
301
|
-
]
|
|
302
|
-
data.push(temp)
|
|
303
|
-
})
|
|
304
|
-
const footValues = []
|
|
375
|
+
const scoreboardData = reportDataRows.map(row => [
|
|
376
|
+
row.rank,
|
|
377
|
+
row.name,
|
|
378
|
+
row.grade,
|
|
379
|
+
Math.round(row.overallScore),
|
|
380
|
+
Math.round(row.safetyScore),
|
|
381
|
+
Math.round(row.efficiencyScore),
|
|
382
|
+
Math.round(row.complianceScore)
|
|
383
|
+
])
|
|
305
384
|
|
|
306
385
|
const style = getStyle(getUserPartner(userData.user))
|
|
307
386
|
|
|
308
|
-
addTable(doc,
|
|
387
|
+
addTable(doc, scoreboardHeaders, scoreboardData, [], style, 40, {
|
|
388
|
+
0: { halign: 'center' },
|
|
389
|
+
2: { halign: 'center' },
|
|
390
|
+
3: { halign: 'right' },
|
|
391
|
+
4: { halign: 'right' },
|
|
392
|
+
5: { halign: 'right' },
|
|
393
|
+
6: { halign: 'right' }
|
|
394
|
+
}, (cellData) => {
|
|
395
|
+
if (cellData.section !== 'body') {
|
|
396
|
+
return
|
|
397
|
+
}
|
|
398
|
+
const row = reportDataRows[cellData.row.index]
|
|
399
|
+
if (!row) {
|
|
400
|
+
return
|
|
401
|
+
}
|
|
402
|
+
if (cellData.column.index === 0) {
|
|
403
|
+
cellData.cell.styles.fontStyle = row.rank <= 3 ? 'bold' : 'normal'
|
|
404
|
+
}
|
|
405
|
+
if (cellData.column.index === 2 || cellData.column.index === 3) {
|
|
406
|
+
const colors = gradeColors[row.grade]
|
|
407
|
+
cellData.cell.styles.fillColor = colors.fill
|
|
408
|
+
cellData.cell.styles.textColor = colors.text
|
|
409
|
+
cellData.cell.styles.fontStyle = 'bold'
|
|
410
|
+
}
|
|
411
|
+
})
|
|
412
|
+
|
|
413
|
+
const detailData = reportDataRows.map(row => [
|
|
414
|
+
row.name,
|
|
415
|
+
(row.distance / 1000).toFixed(1),
|
|
416
|
+
row.avgFuelConsumption.toFixed(2),
|
|
417
|
+
convertMS(row.highEngineRPM, false),
|
|
418
|
+
row.hardBraking,
|
|
419
|
+
row.hardAcceleration,
|
|
420
|
+
row.hardCornering,
|
|
421
|
+
row.overspeed,
|
|
422
|
+
row.geofenceAlarm,
|
|
423
|
+
row.continuesDrivingAlarm,
|
|
424
|
+
row.reverseAlarm,
|
|
425
|
+
row.otherAlarm
|
|
426
|
+
])
|
|
427
|
+
|
|
428
|
+
const detailStartY = doc.lastAutoTable.finalY + 12
|
|
429
|
+
doc.setFontSize(10)
|
|
430
|
+
doc.setFont('helvetica', 'bold')
|
|
431
|
+
doc.text(translations.report.detailedMetrics, 15, detailStartY - 4)
|
|
432
|
+
doc.setFont('helvetica', 'normal')
|
|
433
|
+
|
|
434
|
+
addTable(doc, detailHeaders, detailData, [], { ...style, bodyFontSize: 7, headerFontSize: 8 }, detailStartY, {
|
|
309
435
|
1: { halign: 'right' },
|
|
310
436
|
2: { halign: 'right' },
|
|
311
437
|
3: { halign: 'right' },
|
|
@@ -316,13 +442,19 @@ async function exportToPDF (userData, reportData) {
|
|
|
316
442
|
8: { halign: 'right' },
|
|
317
443
|
9: { halign: 'right' },
|
|
318
444
|
10: { halign: 'right' },
|
|
319
|
-
11: { halign: 'right' }
|
|
320
|
-
12: { halign: 'right' }
|
|
445
|
+
11: { halign: 'right' }
|
|
321
446
|
})
|
|
322
447
|
return doc
|
|
323
448
|
}
|
|
324
449
|
}
|
|
325
450
|
|
|
451
|
+
// Entry point for the generic server-side report dispatcher (see executeServerSide / pinme-backend-v2's getReport),
|
|
452
|
+
// which requires a "create" + PascalCase(report name) export returning an array.
|
|
453
|
+
async function createDriverrankingReport (from, to, userData, traccar) {
|
|
454
|
+
return [await create(from, to, userData, traccar)]
|
|
455
|
+
}
|
|
456
|
+
|
|
326
457
|
exports.create = create
|
|
458
|
+
exports.createDriverrankingReport = createDriverrankingReport
|
|
327
459
|
exports.exportToPDF = exportToPDF
|
|
328
460
|
exports.exportToExcel = exportToExcel
|
package/src/util/pdfDocument.js
CHANGED
|
@@ -61,7 +61,7 @@ async function headerFromUser (doc, title, user, orientation = 'l') {
|
|
|
61
61
|
return header(doc, title, partner && partner.host, getStyle(partner), orientation)
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
function addTable (doc, headers, data, footValues, style, startY, columnStyles = {}) {
|
|
64
|
+
function addTable (doc, headers, data, footValues, style, startY, columnStyles = {}, didParseCell) {
|
|
65
65
|
doc.autoTable({
|
|
66
66
|
head: [headers],
|
|
67
67
|
body: data,
|
|
@@ -97,6 +97,7 @@ function addTable (doc, headers, data, footValues, style, startY, columnStyles =
|
|
|
97
97
|
})
|
|
98
98
|
})
|
|
99
99
|
},
|
|
100
|
+
...(didParseCell ? { didParseCell } : {}),
|
|
100
101
|
columnStyles,
|
|
101
102
|
startY
|
|
102
103
|
})
|