fleetmap-reports 2.0.357 → 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 (87) 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 +149 -49
  87. package/src/util/pdfDocument.js +2 -1
@@ -0,0 +1,278 @@
1
+ const { devicesToProcess } = require('./util/device')
2
+ const automaticReports = require('./automaticReports')
3
+ const traccarHelper = require('./util/traccar')
4
+ const { getDates, convertMS, getTranslations, convertFromUTC } = require('./util/utils')
5
+ const { getStyle } = require('./reportStyle')
6
+ const { getUserPartner } = require('fleetmap-partners')
7
+ const { addTable, headerFromUser } = require('./util/pdfDocument')
8
+ const jsPDF = require('jspdf')
9
+ require('jspdf-autotable')
10
+
11
+ const fileName = 'MachineReport'
12
+
13
+ async function createMachinesReport (from, to, userData, traccar) {
14
+ console.log('Create MachineReport')
15
+ const reportData = []
16
+
17
+ console.log('ByDevice')
18
+ const report = await createMachinesReportByDevice(from, to, userData, traccar)
19
+ reportData.push(report)
20
+
21
+ return reportData
22
+ }
23
+
24
+ async function createMachinesReportByDevice (from, to, userData, traccar) {
25
+ const devices = devicesToProcess(userData)
26
+
27
+ const allData = {
28
+ devices: [],
29
+ from,
30
+ to,
31
+ xpert: devices.filter(d => d.attributes.xpert).length > 0
32
+ }
33
+
34
+ const sliced = automaticReports.sliceArray(devices, 5)
35
+
36
+ let deviceCount = 0
37
+ for (const slice of sliced) {
38
+ const allInOne = await traccarHelper.getAllInOne(traccar, from, to, slice, true, false, false, false, deviceCount, devices.length)
39
+ const routeData = allInOne.route
40
+
41
+ if (routeData.length > 0) {
42
+ allData.devices.push(...processDevices(from, to, slice, {
43
+ route: routeData
44
+ }, userData))
45
+ }
46
+ deviceCount = deviceCount + slice.length
47
+ }
48
+
49
+ return allData
50
+ }
51
+
52
+ function processDevices (from, to, devices, data, userData) {
53
+ const devicesResult = []
54
+
55
+ devices.forEach(d => {
56
+ const deviceRoute = data.route.filter(t => t.deviceId === d.id)
57
+
58
+ if (deviceRoute.filter(p => p.attributes.ignition && p.valid).length > 0) {
59
+ const summary = []
60
+
61
+ const convertFrom = new Date(convertFromUTC(from, userData.user.attributes.timezone))
62
+ const convertTo = new Date(convertFromUTC(to, userData.user.attributes.timezone))
63
+
64
+ if (userData.withDetails) {
65
+ const ignitions = calculateIgnitions(deviceRoute)
66
+ for (const ignition of ignitions) {
67
+ const dayData = getSummaryData(new Date(ignition.on.fixTime), d, ignition.route, ignition, undefined)
68
+ summary.push(dayData)
69
+ }
70
+ } else {
71
+ const dates = getDates(convertFrom, convertTo)
72
+ for (const date of dates) {
73
+ const fromByDay = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0)
74
+ const toByDay = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59)
75
+
76
+ const routeByDay = deviceRoute.filter(p => (new Date(p.fixTime) > fromByDay && new Date(p.fixTime) < toByDay) && p.valid)
77
+ routeByDay.sort((a, b) => new Date(a.fixTime).getTime() - new Date(b.fixTime).getTime())
78
+
79
+ const ignitions = calculateIgnitions(routeByDay)
80
+ const dayData = getSummaryData(date, d, routeByDay, undefined, ignitions)
81
+ summary.push(dayData)
82
+ }
83
+ }
84
+ if (summary.length) {
85
+ const deviceData = {
86
+ device: d,
87
+ summary
88
+ }
89
+ devicesResult.push(deviceData)
90
+ }
91
+ }
92
+ })
93
+
94
+ return devicesResult
95
+ }
96
+
97
+ function getSummaryData (date, device, route, ignition, ignitions) {
98
+ const startTime = ignition ? ignition.on.fixTime : (ignitions.length ? ignitions[0].on.fixTime : undefined)
99
+ const endTime = ignition ? ignition.off.fixTime : (ignitions.length ? ignitions[ignitions.length - 1].off.fixTime : undefined)
100
+ let engineHours = ignition
101
+ ? ignition.route[ignition.route.length - 1].attributes.hours - ignition.route[0].attributes.hours
102
+ : (ignitions ? ignitions.reduce((a, b) => a + (b.route[b.route.length - 1].attributes.hours - b.route[0].attributes.hours), 0) : undefined)
103
+
104
+ if (!engineHours) {
105
+ engineHours = 0
106
+ if (ignition) {
107
+ engineHours = new Date(endTime).getTime() - new Date(startTime).getTime()
108
+ } else {
109
+ ignitions.forEach(e => {
110
+ engineHours = engineHours + (new Date(e.off.fixTime).getTime() - new Date(e.on.fixTime).getTime())
111
+ })
112
+ }
113
+ }
114
+
115
+ return {
116
+ date,
117
+ deviceId: device.id,
118
+ engineHours,
119
+ totalHours: route.length ? route[route.length - 1].attributes.hours : undefined,
120
+ startTime,
121
+ endTime
122
+ }
123
+ }
124
+
125
+ function calculateIgnitions (routeOfDay) {
126
+ let indexStart
127
+ let last
128
+ const ignitions = []
129
+ routeOfDay.forEach((p, index) => {
130
+ // difference between last position and current position is bigger than 5 minutes
131
+ if ((last && last.attributes.ignition && (new Date(p.fixTime).getTime() - new Date(last.fixTime).getTime()) > 300000) &&
132
+ ignitions.length &&
133
+ !ignitions[ignitions.length - 1].off) {
134
+ ignitions[ignitions.length - 1].off = last
135
+ ignitions[ignitions.length - 1].route = routeOfDay.slice(indexStart, index)
136
+ }
137
+
138
+ if (p.attributes.ignition && (!ignitions.length || ignitions[ignitions.length - 1].off)) {
139
+ ignitions.push({ on: p })
140
+ indexStart = index
141
+ }
142
+
143
+ // ignition is off
144
+ // or is last route position
145
+ if ((!p.attributes.ignition || index + 1 === routeOfDay.length) &&
146
+ ignitions.length &&
147
+ !ignitions[ignitions.length - 1].off) {
148
+ ignitions[ignitions.length - 1].off = p
149
+ ignitions[ignitions.length - 1].route = (index + 1) === routeOfDay.length ? routeOfDay.slice(indexStart) : routeOfDay.slice(indexStart, index + 1)
150
+ }
151
+
152
+ last = p
153
+ })
154
+
155
+ return ignitions
156
+ }
157
+
158
+ async function exportMachinesReportToPDF (userData, reportData) {
159
+ // eslint-disable-next-line new-cap
160
+ const doc = new jsPDF.jsPDF('l')
161
+ const translations = getTranslations(userData)
162
+ await headerFromUser(doc, translations.report.titleMachinesReport, userData.user)
163
+
164
+ console.log('Export to PDF')
165
+ const headers = [translations.report.date,
166
+ translations.report.start,
167
+ translations.report.end,
168
+ translations.report.engineHours,
169
+ translations.report.totalEngineHours]
170
+
171
+ const weekDays = [
172
+ translations.report.sunday,
173
+ translations.report.monday,
174
+ translations.report.tuesday,
175
+ translations.report.wednesday,
176
+ translations.report.thursday,
177
+ translations.report.friday,
178
+ translations.report.saturday
179
+ ]
180
+
181
+ reportData.devices.forEach(function (d, index) {
182
+ const name = userData.byDriver ? d.driver.name : d.device.name
183
+ const group = userData.byDriver ? userData.groups.find(g => g.drivers.includes(d.id)) : userData.groups.find(g => d.device.groupId === g.id)
184
+
185
+ const space = index === 0 ? 8 : 0
186
+ if (index) {
187
+ doc.addPage()
188
+ }
189
+
190
+ doc.setFontSize(13)
191
+ doc.text(name, 20, space + 20)
192
+ doc.setFontSize(11)
193
+ doc.text(group ? translations.report.group + ': ' + group.name : '', 150, space + 20)
194
+ doc.text(new Date(reportData.from).toLocaleString() + ' - ' + new Date(reportData.to).toLocaleString(), 20, space + 25)
195
+
196
+ const data = []
197
+ d.summary.forEach(s => {
198
+ const temp = [
199
+ new Date(s.date).toLocaleDateString() + ' - ' + weekDays[s.date.getDay()],
200
+ s.startTime ? new Date(s.startTime).toLocaleTimeString() : '',
201
+ s.endTime ? new Date(s.endTime).toLocaleTimeString() : '',
202
+ convertMS(s.engineHours),
203
+ s.totalHours ? convertMS(s.totalHours) : ''
204
+ ]
205
+
206
+ data.push(temp)
207
+ })
208
+
209
+ const footValues = ['',
210
+ '', '',
211
+ convertMS(d.summary.reduce((a, b) => a + (b.engineHours || 0), 0))]
212
+
213
+ const style = getStyle(getUserPartner(userData.user))
214
+ addTable(doc, headers, data, footValues, style, 40)
215
+ })
216
+
217
+ return doc
218
+ }
219
+
220
+ async function exportMachinesReportToExcel (userData, reportData) {
221
+ console.log('Export to Excel')
222
+ const translations = getTranslations(userData)
223
+
224
+ const settings = {
225
+ sheetName: translations.report.titleMachinesReport, // The name of the sheet
226
+ fileName // The name of the spreadsheet
227
+ }
228
+
229
+ const weekDays = [
230
+ translations.report.sunday,
231
+ translations.report.monday,
232
+ translations.report.tuesday,
233
+ translations.report.wednesday,
234
+ translations.report.thursday,
235
+ translations.report.friday,
236
+ translations.report.saturday
237
+ ]
238
+
239
+ const headers = [{ label: translations.report.vehicle, value: 'name' },
240
+ { label: translations.report.group, value: 'group' },
241
+ { label: translations.report.date, value: 'date' },
242
+ { label: translations.report.start, value: 'start' },
243
+ { label: translations.report.end, value: 'end' },
244
+ { label: translations.report.engineHours, value: 'engineHours' },
245
+ { label: translations.report.totalEngineHours, value: 'totalEngineHours' }]
246
+
247
+ let data = []
248
+ reportData.devices.forEach(d => {
249
+ const group = userData.groups.find(g => d.device.groupId === g.id)
250
+
251
+ data = data.concat([{}])
252
+ data = data.concat(d.summary.map(s => {
253
+ return {
254
+ name: d.device.name,
255
+ group: group ? group.name : '',
256
+ date: new Date(s.date).toLocaleDateString(),
257
+ weekday: weekDays[s.date.getDay()],
258
+ start: s.startTime ? new Date(s.startTime).toLocaleTimeString() : '',
259
+ end: s.endTime ? new Date(s.endTime).toLocaleTimeString() : '',
260
+ engineHours: convertMS(s.engineHours),
261
+ totalEngineHours: s.totalHours ? convertMS(s.totalHours) : ''
262
+ }
263
+ }))
264
+ })
265
+ console.log(data)
266
+ return {
267
+ headers,
268
+ data,
269
+ settings
270
+ }
271
+ }
272
+
273
+ exports.createMachinesReport = createMachinesReport
274
+ exports.exportMachinesReportToPDF = exportMachinesReportToPDF
275
+ exports.exportMachinesReportToExcel = exportMachinesReportToExcel
276
+ exports.create = createMachinesReport
277
+ exports.exportToPDF = exportMachinesReportToPDF
278
+ exports.exportToExcel = exportMachinesReportToExcel
@@ -0,0 +1,129 @@
1
+ const traccar = require('../util/traccar')
2
+ const { getIdleEvents } = require('../util/route')
3
+ const { getNearestPOIs, checkGeofenceIn } = require('../util/geofence')
4
+ const { convertToFeature, convertPositionToFeature } = require('../util/utils')
5
+
6
+ function calculateLastStopTime (stopTime, to, time) {
7
+ return stopTime + (to.getTime() - new Date(time).getTime())
8
+ }
9
+
10
+ function calculateFirstStopTime (stopTime, from, time) {
11
+ return stopTime + (new Date(time).getTime() - from.getTime())
12
+ }
13
+
14
+ async function createActivityReport (from, to, userData, traccarInstance) {
15
+ const unauthorizedGeofences = []
16
+ const authorizedGeofences = []
17
+
18
+ const notAuthGeofenceIds = (await traccarInstance.geofences.geofencesGet(undefined, undefined, undefined, Number(process.env.AFRIQUIA_NOT_AUTH_GROUP_ID), undefined, undefined).then(r => r.data)).map(g => g.id)
19
+ unauthorizedGeofences.push(...(userData.geofences.filter(g => notAuthGeofenceIds.includes(g.id))))
20
+ const unauthorizedGeofencesFeatures = unauthorizedGeofences.map(g => convertToFeature(g))
21
+ console.log('unauthorizedGeofences', unauthorizedGeofences)
22
+
23
+ const authGeofenceIds = (await traccarInstance.geofences.geofencesGet(undefined, undefined, undefined, Number(process.env.AFRIQUIA_AUTH_GROUP_ID), undefined, undefined).then(r => r.data)).map(g => g.id)
24
+ authorizedGeofences.push(...(userData.geofences.filter(g => authGeofenceIds.includes(g.id))))
25
+ const authorizedGeofencesFeatures = authorizedGeofences.map(g => convertToFeature(g))
26
+ console.log('authorizedGeofences', authorizedGeofences)
27
+
28
+ const allInOne = await traccar.getAllInOne(traccarInstance, from, to, userData.devices, true, true, true, false)
29
+
30
+ const reportData = []
31
+ userData.devices.forEach(d => {
32
+ const trips = allInOne.trips.filter(t => t.deviceId === d.id)
33
+ const route = allInOne.route.filter(p => p.deviceId === d.id)
34
+ const stops = allInOne.stops.filter(s => s.deviceId === d.id)
35
+ const group = userData.groups.find(g => g.id === d.groupId)
36
+
37
+ let unauthorizedStops = 0
38
+ let authorizedStops = 0
39
+ let otherStops = 0
40
+ let authorizedStopsTime = 0
41
+
42
+ stops.forEach(s => {
43
+ const point = convertPositionToFeature({ latitude: s.latitude, longitude: s.longitude })
44
+ const unauthorized = unauthorizedGeofencesFeatures.find(g => {
45
+ const geofenceFeature = unauthorizedGeofencesFeatures.find(gF => gF.properties.geofence.id === g.id)
46
+ return geofenceFeature ? checkGeofenceIn(point, geofenceFeature) : false
47
+ })
48
+
49
+ if (unauthorized) {
50
+ unauthorizedStops = unauthorizedStops + 1
51
+ } else {
52
+ const authorized = authorizedGeofences.find(g => {
53
+ const geofenceFeature = authorizedGeofencesFeatures.find(gF => gF.properties.geofence.id === g.id)
54
+ return geofenceFeature ? checkGeofenceIn(point, geofenceFeature) : false
55
+ })
56
+
57
+ if (authorized) {
58
+ authorizedStops = authorizedStops + 1
59
+ authorizedStopsTime = authorizedStopsTime + (s.duration > s.engineHours ? (s.duration - s.engineHours) : 0)
60
+ } else {
61
+ otherStops = otherStops + 1
62
+ }
63
+ }
64
+ })
65
+
66
+ const idleEvents = getIdleEvents(route)
67
+
68
+ const totalDistance = trips.reduce((a, b) => a + b.distance, 0)
69
+ const drivingTime = trips.reduce((a, b) => a + b.duration, 0)
70
+ let stopTime = stops.reduce((a, b) => a + (b.duration > b.engineHours ? (b.duration - b.engineHours) : 0), 0)
71
+
72
+ const idleTimeArray = idleEvents.map(e => e.idleTime).filter(a => a > 0)
73
+
74
+ const startTrip = trips.length ? trips[0] : undefined
75
+ const endTrip = trips.length ? trips[trips.length - 1] : undefined
76
+
77
+ /* if (stops.length > 0 && startTrip) {
78
+ if (startTrip.startTime > stops[0].startTime) {
79
+
80
+ }
81
+ }
82
+ stopTime = stopTime + (new Date(startTrip).getTime() - from.getTime())
83
+ */
84
+
85
+ if (endTrip && stops.length > 0) {
86
+ if (endTrip.endTime > stops[stops.length - 1].endTime) {
87
+ stopTime = calculateLastStopTime(stopTime, to, endTrip.endTime)
88
+ } else {
89
+ stopTime = calculateLastStopTime(stopTime, to, stops[stops.length - 1].endTime)
90
+ }
91
+ } else if (!endTrip && stops.length > 0) {
92
+ stopTime = calculateLastStopTime(stopTime, to, stops[stops.length - 1].endTime)
93
+ } if (endTrip && stops.length === 0) {
94
+ stopTime = calculateLastStopTime(stopTime, to, endTrip.endTime)
95
+ }
96
+
97
+ const startNearestPOIs = startTrip ? getNearestPOIs(startTrip.startLon, startTrip.startLat, userData.geofences) : []
98
+ const endNearestPOIs = endTrip ? getNearestPOIs(endTrip.endLon, endTrip.endLat, userData.geofences) : []
99
+
100
+ const deviceData = {
101
+ device: d.name,
102
+ group: group ? group.name : '',
103
+ startAddress: startNearestPOIs.length > 0 ? startNearestPOIs[0].p.name : (startTrip ? startTrip.startAddress : ''),
104
+ startTime: startTrip ? startTrip.startTime : '',
105
+ endAddress: endNearestPOIs.length > 0 ? endNearestPOIs[0].p.name : (endTrip ? endTrip.endAddress : ''),
106
+ endTime: endTrip ? endTrip.endTime : '',
107
+ drivingTime,
108
+ stopTime,
109
+ idleTime: idleEvents.reduce((a, b) => a + b.idleTime, 0),
110
+ distance: totalDistance,
111
+ totalIdleEvents: idleEvents.length,
112
+ minIdleEvent: idleTimeArray.length > 0 ? Math.min(...idleTimeArray) : 0,
113
+ maxIdleEvent: idleTimeArray.length > 0 ? Math.max(...idleTimeArray) : 0,
114
+ otherStops,
115
+ authorizedStops,
116
+ unauthorizedStops,
117
+ authorizedStopsTime,
118
+ averageSpeed: totalDistance > 0 ? ((trips.reduce((a, b) => a + (b.averageSpeed * b.distance), 0)) / totalDistance) : 0,
119
+ maxSpeed: trips.reduce((a, b) => {
120
+ return a > b.maxSpeed ? a : b.maxSpeed
121
+ }, 0)
122
+ }
123
+ reportData.push(deviceData)
124
+ })
125
+
126
+ return reportData
127
+ }
128
+
129
+ exports.createActivityReport = createActivityReport
@@ -0,0 +1,80 @@
1
+ const traccarHelper = require('../util/traccar')
2
+ const { isInsideTimetable } = require('../util/trips')
3
+ const { convertFromUTCDate } = require('../util/utils')
4
+ const automaticReports = require('../automaticReports')
5
+
6
+ async function createDailyUseReport (from, to, userData, traccar) {
7
+ const reportData = []
8
+
9
+ const devices = userData.devices
10
+
11
+ const sliced = automaticReports.sliceArray(devices, 5)
12
+ let deviceCount = 0
13
+ for (const slice of sliced) {
14
+ const allInOne = await traccarHelper.getAllInOne(traccar, from, to, slice, false, true, true, true, deviceCount, devices.length)
15
+
16
+ slice.forEach(d => {
17
+ reportData.push(processDeviceData(allInOne, d, userData))
18
+ })
19
+
20
+ deviceCount = deviceCount + slice.length
21
+ }
22
+
23
+ return reportData
24
+ }
25
+
26
+ function processDeviceData (allInOne, d, userData) {
27
+ const now = new Date()
28
+ const group = userData.groups.find(g => g.id === d.groupId)
29
+
30
+ const deviceTrips = allInOne.trips.filter(t => t.deviceId === d.id)
31
+ const deviceStops = allInOne.stops.filter(t => t.deviceId === d.id)
32
+
33
+ userData.weekDays = {
34
+ sunday: true,
35
+ monday: true,
36
+ tuesday: true,
37
+ wednesday: true,
38
+ thursday: true,
39
+ friday: true,
40
+ saturday: true
41
+ }
42
+ userData.dayHours = {
43
+ startTime: '00:00',
44
+ endTime: '12:00'
45
+ }
46
+
47
+ const morningTrips = deviceTrips.filter(t => isInsideTimetable(t, userData))
48
+ const morningStart = morningTrips.length && convertFromUTCDate(new Date(morningTrips[0].startTime), userData.user)
49
+ const morningEnd = morningTrips.length && convertFromUTCDate(new Date(morningTrips[morningTrips.length - 1].endTime), userData.user)
50
+
51
+ userData.dayHours = {
52
+ startTime: '12:00',
53
+ endTime: '23:59'
54
+ }
55
+
56
+ const afternoonTrips = deviceTrips.filter(t => isInsideTimetable(t, userData))
57
+
58
+ const afternoonStart = afternoonTrips.length && convertFromUTCDate(new Date(afternoonTrips[0].startTime), userData.user)
59
+ const afternoonEnd = afternoonTrips.length && convertFromUTCDate(new Date(afternoonTrips[afternoonTrips.length - 1].endTime), userData.user)
60
+
61
+ console.log('device took', new Date() - now, 'ms')
62
+ return {
63
+ device: d.name,
64
+ licensePlate: d.attributes.license_plate,
65
+ group: group ? group.name : '',
66
+ morningStart,
67
+ morningEnd,
68
+ morningTime: morningStart && morningEnd ? morningEnd.getTime() - morningStart.getTime() : 0,
69
+ lunch: morningEnd && afternoonStart ? afternoonStart.getTime() - morningEnd.getTime() : 0,
70
+ afternoonStart,
71
+ afternoonEnd,
72
+ afternoonTime: afternoonStart && afternoonEnd ? afternoonEnd.getTime() - afternoonStart.getTime() : 0,
73
+ totalTime: morningStart && afternoonEnd ? afternoonEnd.getTime() - morningStart.getTime() : 0,
74
+ totalDrivingTime: deviceTrips.reduce((a, b) => a + b.duration, 0),
75
+ totalStops: deviceStops.length,
76
+ totalDistance: deviceTrips.reduce((a, b) => a + b.distance, 0)
77
+ }
78
+ }
79
+
80
+ exports.createDailyUseReport = createDailyUseReport