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,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
|