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.
- package/.claude/worktrees/eager-stonebraker-c08ac7/.eslintrc.js +15 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/lang/enGB.js +309 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/lang/esCL.js +297 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/lang/frFR.json +324 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/lang/index.js +35 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/lang/ptBR.js +301 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/lang/ptPT.js +310 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/package-lock.json +11944 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/package.json +45 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/activity-report.js +822 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/automaticReports.js +34 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/carbone/index.js +13 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/custom/vistawasteActivity-report.js +544 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/events-report.js +360 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/fuel-consumption-report.js +296 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/fuel-drop-report.js +128 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/here.js +172 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/idle-report.js +403 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/index.js +244 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/kms-report.js +549 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/location-report.js +496 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/machines-report.js +278 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/afriquia.js +129 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/dailyuse-report.js +80 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/delayedstart-report.js +223 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/driverranking-report.js +328 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/gpsjump-report.js +61 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/performance-report.js +359 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/refueling-report.js +345 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/reportStyle.js +34 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/sensor-report.js +447 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/speeding-report.js +663 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/stop-report.js +242 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/ActivityReportTemplate.mjml +51 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/EmptyReportTemplate.mjml +25 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/EventsReportTemplate.mjml +75 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/FuelDropReportTemplate.mjml +60 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/IdleReportTemplate.mjml +54 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/KmsReportGroupByDayTemplate.mjml +43 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/KmsReportTemplate.mjml +44 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/LocationReportTemplate.mjml +37 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/RefuelingReportTemplate.mjml +60 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/SensorReportTemplate.mjml +23 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/SpeedingReportTemplate.mjml +80 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/TripReportTemplate.mjml +97 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/ZoneReportTemplate.mjml +52 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/report.mjml +96 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/activity.test.js +99 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/carbone.test.js +20 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/events.test.js +30 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/fuel.test.js +35 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/idle.test.js +16 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/index.js +24 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/index.test.js +203 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/kms.test.js +74 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/performance.test.js +36 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/positions.test.js +67 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/speeding.test.js +120 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/translations.test.js +21 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/trips.test.js +140 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/zones.test.js +162 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/trip-report.js +641 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/device.js +13 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/driver.js +90 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/fuel.js +102 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/geofence.js +52 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/odoo.js +18 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/parallel.js +64 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/pdfDocument.js +140 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/route.js +78 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/timetable.js +30 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/traccar.js +213 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/traccar.worker.js +49 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/trips.js +270 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/utils.js +358 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/worker.js +14 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/xpert.js +58 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/word/index.js +95 -0
- package/.claude/worktrees/eager-stonebraker-c08ac7/src/zone-report.js +904 -0
- 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 +152 -49
- package/src/speeding-report.js +11 -1
- 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
|