fleetmap-reports 2.0.356 → 2.0.358

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.
Files changed (88) hide show
  1. package/.claude/worktrees/eager-stonebraker-c08ac7/.eslintrc.js +15 -0
  2. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/enGB.js +309 -0
  3. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/esCL.js +297 -0
  4. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/frFR.json +324 -0
  5. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/index.js +35 -0
  6. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/ptBR.js +301 -0
  7. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/ptPT.js +310 -0
  8. package/.claude/worktrees/eager-stonebraker-c08ac7/package-lock.json +11944 -0
  9. package/.claude/worktrees/eager-stonebraker-c08ac7/package.json +45 -0
  10. package/.claude/worktrees/eager-stonebraker-c08ac7/src/activity-report.js +822 -0
  11. package/.claude/worktrees/eager-stonebraker-c08ac7/src/automaticReports.js +34 -0
  12. package/.claude/worktrees/eager-stonebraker-c08ac7/src/carbone/index.js +13 -0
  13. package/.claude/worktrees/eager-stonebraker-c08ac7/src/custom/vistawasteActivity-report.js +544 -0
  14. package/.claude/worktrees/eager-stonebraker-c08ac7/src/events-report.js +360 -0
  15. package/.claude/worktrees/eager-stonebraker-c08ac7/src/fuel-consumption-report.js +296 -0
  16. package/.claude/worktrees/eager-stonebraker-c08ac7/src/fuel-drop-report.js +128 -0
  17. package/.claude/worktrees/eager-stonebraker-c08ac7/src/here.js +172 -0
  18. package/.claude/worktrees/eager-stonebraker-c08ac7/src/idle-report.js +403 -0
  19. package/.claude/worktrees/eager-stonebraker-c08ac7/src/index.js +244 -0
  20. package/.claude/worktrees/eager-stonebraker-c08ac7/src/kms-report.js +549 -0
  21. package/.claude/worktrees/eager-stonebraker-c08ac7/src/location-report.js +496 -0
  22. package/.claude/worktrees/eager-stonebraker-c08ac7/src/machines-report.js +278 -0
  23. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/afriquia.js +129 -0
  24. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/dailyuse-report.js +80 -0
  25. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/delayedstart-report.js +223 -0
  26. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/driverranking-report.js +328 -0
  27. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/gpsjump-report.js +61 -0
  28. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/performance-report.js +359 -0
  29. package/.claude/worktrees/eager-stonebraker-c08ac7/src/refueling-report.js +345 -0
  30. package/.claude/worktrees/eager-stonebraker-c08ac7/src/reportStyle.js +34 -0
  31. package/.claude/worktrees/eager-stonebraker-c08ac7/src/sensor-report.js +447 -0
  32. package/.claude/worktrees/eager-stonebraker-c08ac7/src/speeding-report.js +663 -0
  33. package/.claude/worktrees/eager-stonebraker-c08ac7/src/stop-report.js +242 -0
  34. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/ActivityReportTemplate.mjml +51 -0
  35. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/EmptyReportTemplate.mjml +25 -0
  36. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/EventsReportTemplate.mjml +75 -0
  37. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/FuelDropReportTemplate.mjml +60 -0
  38. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/IdleReportTemplate.mjml +54 -0
  39. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/KmsReportGroupByDayTemplate.mjml +43 -0
  40. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/KmsReportTemplate.mjml +44 -0
  41. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/LocationReportTemplate.mjml +37 -0
  42. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/RefuelingReportTemplate.mjml +60 -0
  43. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/SensorReportTemplate.mjml +23 -0
  44. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/SpeedingReportTemplate.mjml +80 -0
  45. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/TripReportTemplate.mjml +97 -0
  46. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/ZoneReportTemplate.mjml +52 -0
  47. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/report.mjml +96 -0
  48. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/activity.test.js +99 -0
  49. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/carbone.test.js +20 -0
  50. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/events.test.js +30 -0
  51. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/fuel.test.js +35 -0
  52. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/idle.test.js +16 -0
  53. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/index.js +24 -0
  54. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/index.test.js +203 -0
  55. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/kms.test.js +74 -0
  56. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/performance.test.js +36 -0
  57. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/positions.test.js +67 -0
  58. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/speeding.test.js +120 -0
  59. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/translations.test.js +21 -0
  60. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/trips.test.js +140 -0
  61. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/zones.test.js +162 -0
  62. package/.claude/worktrees/eager-stonebraker-c08ac7/src/trip-report.js +641 -0
  63. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/device.js +13 -0
  64. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/driver.js +90 -0
  65. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/fuel.js +102 -0
  66. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/geofence.js +52 -0
  67. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/odoo.js +18 -0
  68. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/parallel.js +64 -0
  69. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/pdfDocument.js +140 -0
  70. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/route.js +78 -0
  71. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/timetable.js +30 -0
  72. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/traccar.js +213 -0
  73. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/traccar.worker.js +49 -0
  74. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/trips.js +270 -0
  75. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/utils.js +358 -0
  76. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/worker.js +14 -0
  77. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/xpert.js +58 -0
  78. package/.claude/worktrees/eager-stonebraker-c08ac7/src/word/index.js +95 -0
  79. package/.claude/worktrees/eager-stonebraker-c08ac7/src/zone-report.js +904 -0
  80. package/lang/enGB.js +18 -0
  81. package/lang/esCL.js +18 -0
  82. package/lang/frFR.json +7 -1
  83. package/lang/ptBR.js +18 -0
  84. package/lang/ptPT.js +18 -0
  85. package/package.json +1 -1
  86. package/src/partnerReports/driverranking-report.js +152 -49
  87. package/src/speeding-report.js +11 -1
  88. package/src/util/pdfDocument.js +2 -1
@@ -0,0 +1,223 @@
1
+ const automaticReports = require('../automaticReports')
2
+ const traccarHelper = require('../util/traccar')
3
+ const {
4
+ getDates, getTranslations, convertMS, isClientSide, convertToLocaleString, convertToLocaleDateString,
5
+ convertToLocaleTimeString
6
+ } = require('../util/utils')
7
+ const { getUserPartner } = require('fleetmap-partners')
8
+ const { getDataByDay } = require('../util/trips')
9
+ const jsPDF = require('jspdf')
10
+ const { headerFromUser, addTable } = require('../util/pdfDocument')
11
+ const { getStyle } = require('../reportStyle')
12
+
13
+ async function createDelayedStartReport (from, to, userData, traccar) {
14
+ const reportData = {
15
+ devices: [],
16
+ from,
17
+ to
18
+ }
19
+
20
+ const devices = userData.devices
21
+
22
+ const sliced = automaticReports.sliceArray(devices, 5)
23
+ let deviceCount = 0
24
+ for (const slice of sliced) {
25
+ const allInOne = await traccarHelper.getAllInOne(traccar, from, to, slice, true, true, false, false, deviceCount, devices.length)
26
+
27
+ for (const d of slice) {
28
+ const deviceData = await processDeviceData(from, to, allInOne, d, userData, traccar)
29
+ reportData.devices.push(deviceData)
30
+ }
31
+
32
+ deviceCount = deviceCount + slice.length
33
+ }
34
+
35
+ reportData.devices = reportData.devices.flat()
36
+
37
+ return reportData
38
+ }
39
+
40
+ function getDelayDescription (startTime, activityStart, lowDelay, mediumDelay, highDelay) {
41
+ const startTimeDate = new Date(startTime)
42
+ const activityStartDate = new Date(startTime)
43
+ activityStartDate.setHours(activityStart.substring(0, 2), activityStart.substring(3, 5))
44
+
45
+ if (startTimeDate >= activityStartDate) {
46
+ const delay = startTimeDate.getTime() - activityStartDate.getTime()
47
+ if (delay < (lowDelay * 60 * 1000)) {
48
+ return '-'
49
+ } else if (delay < (mediumDelay * 60 * 1000)) {
50
+ return convertMS(delay, false) + ' - Low (+' + lowDelay + ')'
51
+ } else if (delay < (highDelay * 60 * 1000)) {
52
+ return convertMS(delay, false) + ' - Medium (+' + mediumDelay + ')'
53
+ } else {
54
+ return convertMS(delay, false) + ' - High (+' + highDelay + ')'
55
+ }
56
+ }
57
+
58
+ return ''
59
+ }
60
+
61
+ async function processDeviceData (from, to, allInOne, d, userData, traccar) {
62
+ const trips = allInOne.trips.filter(t => t.deviceId === d.id)
63
+ const route = allInOne.route.filter(t => t.deviceId === d.id)
64
+
65
+ const translations = getTranslations(userData)
66
+
67
+ const weekDays = [
68
+ translations.report.sunday,
69
+ translations.report.monday,
70
+ translations.report.tuesday,
71
+ translations.report.wednesday,
72
+ translations.report.thursday,
73
+ translations.report.friday,
74
+ translations.report.saturday
75
+ ]
76
+
77
+ const dates = getDates(from, to, userData.user.attributes.timezone || getUserPartner(userData.user).timezone)
78
+
79
+ const days = []
80
+ for (const date of dates) {
81
+ const { tripsByDay, routeByDay } = await getDataByDay(d, date, { trips, route }, userData, traccar)
82
+
83
+ let startTime
84
+ let endTime
85
+ if (userData.useIgnition) {
86
+ const firstIgnitionOn = routeByDay.find(p => p.attributes.ignition)
87
+ if (firstIgnitionOn) {
88
+ startTime = firstIgnitionOn.fixTime
89
+ }
90
+ } else {
91
+ if (tripsByDay.length) {
92
+ startTime = tripsByDay[0].startTime
93
+ }
94
+ }
95
+
96
+ if (tripsByDay.length) {
97
+ endTime = tripsByDay[tripsByDay.length - 1].endTime
98
+ }
99
+
100
+ if (startTime) {
101
+ const deviceDayData = {
102
+ device: d.name,
103
+ groupId: d.groupId,
104
+ licensePlate: d.attributes.license_plate,
105
+ driver: '',
106
+ weekDay: weekDays[new Date(date).getDay()],
107
+ startTime,
108
+ delayDescription: getDelayDescription(startTime, userData.activityStart, userData.lowDelay, userData.mediumDelay, userData.highDelay),
109
+ endTime
110
+ }
111
+
112
+ days.push(deviceDayData)
113
+ }
114
+ }
115
+
116
+ return days
117
+ }
118
+
119
+ async function exportDelayedStartReportToPDF (userData, reportData) {
120
+ console.log('exportDelayedStartReportToPDF')
121
+
122
+ const timezone = userData.user.attributes.timezone
123
+ const translations = getTranslations(userData)
124
+ const lang = userData.user.attributes.lang || (isClientSide() && navigator.language)
125
+ const reportDataRows = reportData.devices
126
+
127
+ const headers = [
128
+ translations.report.group,
129
+ translations.report.vehicle,
130
+ translations.report.driver,
131
+ translations.report.day,
132
+ translations.report.startDate,
133
+ translations.report.startTime,
134
+ translations.report.delayDescription,
135
+ translations.report.endTime
136
+ ]
137
+
138
+ if (reportDataRows) {
139
+ // eslint-disable-next-line new-cap
140
+ const doc = new jsPDF.jsPDF('l')
141
+ await headerFromUser(doc, translations.report.titleDelayedStartReport, userData.user)
142
+
143
+ doc.setFontSize(13)
144
+ doc.text(convertToLocaleString(reportData.from, lang, timezone, userData.user) + ' - ' + convertToLocaleString(reportData.to, lang, timezone, userData.user), 20, 25)
145
+
146
+ const data = []
147
+ reportDataRows.forEach(function (row) {
148
+ const group = userData.groups.find(g => row.groupId === g.id)
149
+
150
+ const temp = [
151
+ group ? group.name : '',
152
+ row.device,
153
+ row.driver,
154
+ row.weekDay,
155
+ convertToLocaleDateString(row.startTime, lang, timezone, userData.user),
156
+ convertToLocaleTimeString(row.startTime, lang, timezone, userData.user),
157
+ row.delayDescription,
158
+ convertToLocaleTimeString(row.endTime, lang, timezone, userData.user)
159
+ ]
160
+ data.push(temp)
161
+ })
162
+ const footValues = []
163
+
164
+ const style = getStyle(getUserPartner(userData.user))
165
+
166
+ addTable(doc, headers, data, footValues, style, 40)
167
+ return doc
168
+ }
169
+ }
170
+
171
+ const fileName = 'DelayedStartReport'
172
+ async function exportDelayedStartReportToExcel (userData, reportData) {
173
+ console.log('Export to Excel')
174
+
175
+ const translations = getTranslations(userData)
176
+ const timezone = userData.user.attributes.timezone
177
+ const lang = userData.user.attributes.lang
178
+
179
+ const reportDataRows = reportData.devices
180
+
181
+ const settings = {
182
+ sheetName: translations.report.titleTripReport, // The name of the sheet
183
+ fileName // The name of the spreadsheet
184
+ }
185
+
186
+ const headers = [
187
+ { label: translations.report.group, value: 'group' },
188
+ { label: translations.report.vehicle, value: 'name' },
189
+ { label: translations.report.driver, value: 'driver' },
190
+ { label: translations.report.day, value: 'day' },
191
+ { label: translations.report.startDate, value: 'startDate' },
192
+ { label: translations.report.startTime, value: 'startTime' },
193
+ { label: translations.report.delayDescription, value: 'delayDescription' },
194
+ { label: translations.report.endTime, value: 'endTime' }
195
+ ]
196
+
197
+ let data = []
198
+ if (reportDataRows) {
199
+ reportDataRows.forEach(row => {
200
+ const group = userData.groups.find(g => row.groupId === g.id)
201
+ data = data.concat({
202
+ group: group ? group.name : '',
203
+ name: row.device,
204
+ driver: row.driver,
205
+ day: row.weekDay,
206
+ startDate: convertToLocaleDateString(row.startTime, lang, timezone, userData.user),
207
+ startTime: convertToLocaleTimeString(row.startTime, lang, timezone, userData.user),
208
+ delayDescription: row.delayDescription,
209
+ endTime: convertToLocaleTimeString(row.endTime, lang, timezone, userData.user)
210
+ })
211
+ })
212
+ console.log(data)
213
+ return {
214
+ headers,
215
+ data,
216
+ settings
217
+ }
218
+ }
219
+ }
220
+
221
+ exports.create = createDelayedStartReport
222
+ exports.exportToPDF = exportDelayedStartReportToPDF
223
+ exports.exportToExcel = exportDelayedStartReportToExcel
@@ -0,0 +1,328 @@
1
+ const traccarHelper = require('../util/traccar')
2
+ const { getDriverData, devicesByDriver } = require('../util/driver')
3
+ const { getCanDriverStyleMessages, parseCanDriverStyleMessage } = require('../util/xpert')
4
+ const { calculateRPMSections } = require('./performance-report')
5
+ const { getTranslations, isClientSide, convertToLocaleString, convertMS } = require('../util/utils')
6
+ const jsPDF = require('jspdf')
7
+ const { headerFromUser, addTable } = require('../util/pdfDocument')
8
+ const { getStyle } = require('../reportStyle')
9
+ const { getUserPartner } = require('fleetmap-partners')
10
+ const { getOverspeedEvents } = require('../speeding-report')
11
+ const { calculateConsumption } = require('../util/fuel')
12
+ const { getCanAvgConsumption } = require('../fuel-consumption-report')
13
+ const automaticReports = require('../automaticReports')
14
+
15
+ const distanceCoefficient = 0.5
16
+ const consumptionCoefficient = 0.7
17
+ const rpmCoefficient = 0.8
18
+ const hardBrakingCoefficient = 0.10
19
+ const hardAccelerationCoefficient = 0.10
20
+ const hardCorneringCoefficient = 0.10
21
+ const overspeedCoefficient = 0.15
22
+ const geofenceCoefficient = 0.15
23
+ const continuesDrivingCoefficient = 0.1
24
+ const reverseCoefficient = 0.1
25
+ const otherCoefficient = 0.1
26
+
27
+ async function create (from, to, userData, traccar) {
28
+ const reportData = {
29
+ drivers: []
30
+ }
31
+
32
+ userData.geofences = []
33
+ userData.roadSpeedLimits = true
34
+
35
+ const { devices, drivers } = await devicesByDriver(traccar, from, to, userData.drivers, userData.devices)
36
+ userData.drivers = drivers
37
+
38
+ let deviceCount = 0
39
+ const driversData = new Map()
40
+ const overspeedEvents = await getOverspeedEvents(from, to, userData, devices, userData, traccar)
41
+
42
+ const sliced = automaticReports.sliceArray(devices, 5)
43
+
44
+ for (const slice of sliced) {
45
+ const allInOne = await traccarHelper.getAllInOne(traccar, from, to, slice, true, true, false, false, deviceCount, devices.length)
46
+
47
+ const result = []
48
+ const requests = []
49
+ for (const _chunk of automaticReports.sliceArray(slice.map(d => d.id), 1)) {
50
+ requests.push(traccar.reports.reportsEventsGet(from, to, _chunk, null, ['alarm']))
51
+ }
52
+
53
+ try {
54
+ result.push(...(await Promise.all(requests)))
55
+ } catch (e) {
56
+ console.log(e)
57
+ }
58
+ const alarmEvents = result.flatMap(d => d.data)
59
+
60
+ for (const device of slice) {
61
+ const route = allInOne.route.filter(t => t.deviceId === device.id)
62
+ const trips = allInOne.trips.filter(t => t.deviceId === device.id)
63
+
64
+ const deviceOverspeedEvents = overspeedEvents.find(e => e.device.id === device.id)
65
+ const deviceAlarmEvents = alarmEvents.filter(e => e.deviceId === device.id)
66
+
67
+ await processDevice(driversData, device, userData, { route, trips }, deviceAlarmEvents, deviceOverspeedEvents)
68
+ deviceCount++
69
+ }
70
+
71
+ console.log('LOADING_MESSAGE:' + slice[0].name)
72
+ console.log(`PROGRESS_PERC:${deviceCount / devices.length * 100}`)
73
+ }
74
+
75
+ const allDriversData = (Array.from(driversData.values())).filter(d => d.distance > 0)
76
+ 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
+ d.avgFuelConsumption = getCanAvgConsumption(d.distance, d.spentFuel).byKms
90
+ d.score = distanceScore + rpmScore + hardBrakingScore + hardAccelerationScore +
91
+ hardCorneringScore + consumptionScore + overspeedScore + geofenceAlarmScore +
92
+ continuesDrivingAlarmScore + reverseAlarmScore + otherAlarmScore
93
+ })
94
+ allDriversData.sort((a, b) => (a.score > b.score) ? 1 : -1)
95
+
96
+ reportData.drivers.push(...allDriversData)
97
+
98
+ reportData.target = {
99
+ distanceTarget: userData.distanceTarget,
100
+ rpmTarget: userData.rpmTarget,
101
+ hardBrakingTarget: userData.hardBrakingTarget,
102
+ hardAccelerationTarget: userData.hardAccelerationTarget,
103
+ hardCorneringTarget: userData.hardCorneringTarget,
104
+ fuelConsumptionTarget: userData.fuelConsumptionTarget,
105
+ overspeedTarget: userData.overspeedTarget,
106
+ geofenceTarget: userData.geofenceTarget,
107
+ continuesDrivingTarget: userData.continuesDrivingTarget,
108
+ reverseTarget: userData.reverseTarget,
109
+ otherTarget: userData.otherTarget
110
+ }
111
+
112
+ reportData.coefficient = {
113
+ distanceCoefficient,
114
+ rpmCoefficient,
115
+ hardBrakingCoefficient,
116
+ hardAccelerationCoefficient,
117
+ hardCorneringCoefficient,
118
+ consumptionCoefficient,
119
+ overspeedCoefficient,
120
+ geofenceCoefficient,
121
+ continuesDrivingCoefficient,
122
+ reverseCoefficient,
123
+ otherCoefficient
124
+ }
125
+
126
+ return reportData
127
+ }
128
+
129
+ async function processDevice (driversData, device, userData, allInOne, alarmEvents, overspeedEvents) {
130
+ for (const d of userData.drivers) {
131
+ const { trips, route } = await getDriverData(d, allInOne, userData)
132
+ const positionsIds = route.map(p => p.id)
133
+ const events = alarmEvents.filter(e => positionsIds.includes(e.positionId))
134
+ const driverOverSpeedEvents = overspeedEvents ? overspeedEvents.alerts.filter(e => positionsIds.includes(e.position.id)) : []
135
+ const spentFuel = calculateConsumption(device, { trips, route })
136
+
137
+ const geofenceAlarm = events.filter(e => e.attributes.alarm === 'la ceinture').length
138
+ const continuesDrivingAlarm = events.filter(e => e.attributes.alarm === 'Conduite continue').length
139
+ const reverseAlarm = events.filter(e => e.attributes.alarm === 'MARCHE ARIERRE').length
140
+ const otherAlarm = events.length - (geofenceAlarm + continuesDrivingAlarm + reverseAlarm)
141
+
142
+ let driverData = driversData.get(d.id)
143
+ if (!driverData) {
144
+ driverData = {
145
+ name: d.name,
146
+ distance: 0,
147
+ highEngineRPM: 0,
148
+ hardBraking: 0,
149
+ hardAcceleration: 0,
150
+ hardCornering: 0,
151
+ overspeed: 0,
152
+ spentFuel: 0,
153
+ geofenceAlarm: 0,
154
+ continuesDrivingAlarm: 0,
155
+ reverseAlarm: 0,
156
+ otherAlarm: 0
157
+ }
158
+ driversData.set(d.id, driverData)
159
+ }
160
+
161
+ let highEngineRPM = 0
162
+ let hardBraking = 0
163
+ let hardAcceleration = 0
164
+ let hardCornering = 0
165
+ if (route.find(p => p.attributes.rawXpert)) {
166
+ const xpertMessages = route.map(p => p.attributes.rawXpert).filter(a => a)
167
+ const canDriverStyle = getCanDriverStyleMessages(xpertMessages).map(m => parseCanDriverStyleMessage(m))
168
+ highEngineRPM = canDriverStyle.reduce((a, b) => a + b.totalTimeWithHighRpmAndTorque, 0)
169
+ hardBraking = canDriverStyle.reduce((a, b) => a + b.totalNumberOfHarshBrakes, 0)
170
+ hardAcceleration = canDriverStyle.reduce((a, b) => a + b.totalNumberOfHarshAccelerations, 0)
171
+ } else {
172
+ const highEngineRPMSections = calculateRPMSections(route, d.attributes.highRPM || 1500)
173
+ highEngineRPM = highEngineRPMSections.map(a => a.length > 1
174
+ ? a.map((p, index) => index === 0 ? 0 : new Date(p.fixTime).getTime() - new Date(a[index - 1].fixTime).getTime()).reduce((a, b) => a + b, 0)
175
+ : 0).reduce((a, b) => a + b, 0)
176
+
177
+ hardBraking = route.filter(p => p.attributes.alarm === 'hardBraking').length
178
+ hardAcceleration = route.filter(p => p.attributes.alarm === 'hardAcceleration').length
179
+ hardCornering = route.filter(p => p.attributes.alarm === 'hardCornering').length
180
+ }
181
+
182
+ driverData.distance = driverData.distance + trips.filter(t => t.distance > 0).reduce((a, b) => a + b.distance, 0)
183
+ driverData.highEngineRPM = driverData.highEngineRPM + highEngineRPM
184
+ driverData.hardBraking = driverData.hardBraking + hardBraking
185
+ driverData.hardAcceleration = driverData.hardAcceleration + hardAcceleration
186
+ driverData.hardCornering = driverData.hardCornering + hardCornering
187
+ driverData.overspeed = driverData.overspeed + driverOverSpeedEvents.length
188
+ driverData.spentFuel = driverData.spentFuel + spentFuel
189
+ driverData.geofenceAlarm = driverData.geofenceAlarm + geofenceAlarm
190
+ driverData.continuesDrivingAlarm = driverData.continuesDrivingAlarm + continuesDrivingAlarm
191
+ driverData.reverseAlarm = driverData.reverseAlarm + reverseAlarm
192
+ driverData.otherAlarm = driverData.otherAlarm + otherAlarm
193
+ }
194
+ }
195
+
196
+ const fileName = 'DriverRankingReport'
197
+ async function exportToExcel (userData, reportData) {
198
+ console.log('Export to Excel')
199
+
200
+ const translations = getTranslations(userData)
201
+
202
+ const reportDataRows = reportData.drivers
203
+
204
+ const settings = {
205
+ sheetName: translations.report.titleDriverRankingReport, // The name of the sheet
206
+ fileName // The name of the spreadsheet
207
+ }
208
+
209
+ const headers = [
210
+ { label: translations.report.driver, value: 'name' },
211
+ { label: translations.report.distance, value: 'distance' },
212
+ { label: translations.report.avgFuelConsumption, value: 'avgFuelConsumption' },
213
+ { label: translations.report.highEngineRPM, value: 'highEngineRPM' },
214
+ { label: translations.report.hardBraking, value: 'hardBraking' },
215
+ { label: translations.report.hardAcceleration, value: 'hardAcceleration' },
216
+ { label: translations.report.hardCornering, value: 'hardCornering' },
217
+ { label: translations.report.overspeedAlarm, value: 'overspeed' },
218
+ { label: translations.report.geofenceAlarm, value: 'geofenceAlarm' },
219
+ { label: translations.report.continuesDrivingAlarm, value: 'continuesDrivingAlarm' },
220
+ { label: translations.report.reverseAlarm, value: 'reverseAlarm' },
221
+ { label: translations.report.otherAlarm, value: 'otherAlarm' },
222
+ { label: translations.report.score, value: 'score' }
223
+ ]
224
+
225
+ let data = []
226
+ if (reportDataRows) {
227
+ reportDataRows.forEach(row => {
228
+ data = data.concat({
229
+ name: row.name,
230
+ distance: Number(row.distance.toFixed(1)),
231
+ avgFuelConsumption: Number(row.avgFuelConsumption.toFixed(2)),
232
+ highEngineRPM: convertMS(row.highEngineRPM, false),
233
+ hardBraking: row.hardBraking,
234
+ hardAcceleration: row.hardAcceleration,
235
+ hardCornering: row.hardCornering,
236
+ overspeed: row.overspeed,
237
+ geofenceAlarm: row.geofenceAlarm,
238
+ continuesDrivingAlarm: row.continuesDrivingAlarm,
239
+ reverseAlarm: row.reverseAlarm,
240
+ otherAlarm: row.otherAlarm,
241
+ score: Number(row.score.toFixed(2))
242
+ })
243
+ })
244
+ console.log(data)
245
+ return {
246
+ headers,
247
+ data,
248
+ settings
249
+ }
250
+ }
251
+ }
252
+
253
+ async function exportToPDF (userData, reportData) {
254
+ console.log('exportDelayedStartReportToPDF')
255
+
256
+ const timezone = userData.user.attributes.timezone
257
+ const translations = getTranslations(userData)
258
+ const lang = userData.user.attributes.lang || (isClientSide() && navigator.language)
259
+ const reportDataRows = reportData.drivers
260
+
261
+ const headers = [
262
+ translations.report.driver,
263
+ translations.report.distance,
264
+ translations.report.avgFuelConsumption,
265
+ translations.report.highEngineRPM,
266
+ translations.report.hardBraking,
267
+ translations.report.hardAcceleration,
268
+ translations.report.hardCornering,
269
+ translations.report.overspeedAlarm,
270
+ translations.report.geofenceAlarm,
271
+ translations.report.continuesDrivingAlarm,
272
+ translations.report.reverseAlarm,
273
+ translations.report.otherAlarm,
274
+ translations.report.score
275
+ ]
276
+
277
+ if (reportDataRows) {
278
+ // eslint-disable-next-line new-cap
279
+ const doc = new jsPDF.jsPDF('l')
280
+ await headerFromUser(doc, translations.report.titleDriverRankingReport, userData.user)
281
+
282
+ doc.setFontSize(13)
283
+ doc.text(convertToLocaleString(reportData.from, lang, timezone, userData.user) + ' - ' + convertToLocaleString(reportData.to, lang, timezone, userData.user), 20, 25)
284
+
285
+ const data = []
286
+ reportDataRows.forEach(function (row) {
287
+ const temp = [
288
+ row.name,
289
+ row.distance.toFixed(1),
290
+ row.avgFuelConsumption.toFixed(2),
291
+ convertMS(row.highEngineRPM, false),
292
+ row.hardBraking,
293
+ row.hardAcceleration,
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 = []
305
+
306
+ const style = getStyle(getUserPartner(userData.user))
307
+
308
+ addTable(doc, headers, data, footValues, style, 40, {
309
+ 1: { halign: 'right' },
310
+ 2: { halign: 'right' },
311
+ 3: { halign: 'right' },
312
+ 4: { halign: 'right' },
313
+ 5: { halign: 'right' },
314
+ 6: { halign: 'right' },
315
+ 7: { halign: 'right' },
316
+ 8: { halign: 'right' },
317
+ 9: { halign: 'right' },
318
+ 10: { halign: 'right' },
319
+ 11: { halign: 'right' },
320
+ 12: { halign: 'right' }
321
+ })
322
+ return doc
323
+ }
324
+ }
325
+
326
+ exports.create = create
327
+ exports.exportToPDF = exportToPDF
328
+ exports.exportToExcel = exportToExcel
@@ -0,0 +1,61 @@
1
+ const automaticReports = require('../automaticReports')
2
+ const traccarHelper = require('../util/traccar')
3
+ const distance = require('@turf/distance')
4
+
5
+ async function createGPSJumpReport (from, to, userData, traccar, minDistance) {
6
+ let reportData = []
7
+
8
+ const devices = userData.devices
9
+
10
+ const sliced = automaticReports.sliceArray(devices, 10)
11
+ let deviceCount = 0
12
+ for (const slice of sliced) {
13
+ const allInOne = await traccarHelper.getAllInOne(traccar, from, to, slice, true, false, false, false, deviceCount, devices.length)
14
+
15
+ slice.forEach(d => {
16
+ const deviceData = getDeviceData(allInOne, d, userData.user, minDistance || userData.minDistance)
17
+ reportData = reportData.concat(deviceData)
18
+ })
19
+
20
+ deviceCount = deviceCount + slice.length
21
+ }
22
+
23
+ return reportData
24
+ }
25
+
26
+ function getDeviceData (allInOne, d, user, minDistance) {
27
+ const deviceRoute = allInOne.route.filter(t => t.deviceId === d.id)
28
+
29
+ return deviceRoute.map((pA, index) => {
30
+ if (index < deviceRoute.length - 1) {
31
+ const pB = deviceRoute[index + 1]
32
+ const distanceValue = distance.default([pA.longitude, pA.latitude], [pB.longitude, pB.latitude], { units: 'meters' })
33
+ if ((minDistance * 1000) < distanceValue) {
34
+ return {
35
+ device: d.name,
36
+ dateA: pA.fixTime,
37
+ addressA: pA.address,
38
+ dateB: pB.fixTime,
39
+ addressB: pB.address,
40
+ distance: distanceValue
41
+ }
42
+ }
43
+ }
44
+ return undefined
45
+ }).filter(a => a).sort((a, b) => (a.device > b.device) ? 1 : -1)
46
+ }
47
+
48
+ async function exportGPSJumpReportToPDF (userData, reportData) {
49
+
50
+ }
51
+
52
+ async function exportGPSJumpReportToExcel (userData, reportData) {
53
+
54
+ }
55
+
56
+ exports.createGPSJumpReport = createGPSJumpReport
57
+ exports.exportGPSJumpReportToPDF = exportGPSJumpReportToPDF
58
+ exports.exportGPSJumpReportToExcel = exportGPSJumpReportToExcel
59
+ exports.create = createGPSJumpReport
60
+ exports.exportToPDF = exportGPSJumpReportToPDF
61
+ exports.exportToExcel = exportGPSJumpReportToExcel