fleetmap-reports 1.0.409 → 1.0.412
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/.eslintrc.js +15 -0
- package/package.json +2 -2
- package/src/idle-report.js +302 -232
- package/src/index.test.js +15 -0
- package/src/util/traccar.js +9 -3
- package/src/zone-report.js +3 -4
package/.eslintrc.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fleetmap-reports",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.412",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"eslint": "^8.15.0",
|
|
33
33
|
"eslint-config-standard": "^17.0.0",
|
|
34
34
|
"eslint-plugin-import": "^2.26.0",
|
|
35
|
-
"eslint-plugin-
|
|
35
|
+
"eslint-plugin-node": "^11.1.0",
|
|
36
36
|
"eslint-plugin-promise": "^6.0.0",
|
|
37
37
|
"jest": "^27.5.0",
|
|
38
38
|
"mocha": "^9.0.3",
|
package/src/idle-report.js
CHANGED
|
@@ -1,280 +1,350 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
3
|
-
const {
|
|
4
|
-
const {
|
|
5
|
-
const {
|
|
6
|
-
const {
|
|
7
|
-
const
|
|
1
|
+
const jsPDF = require('jspdf')
|
|
2
|
+
const { headerFromUser, AmiriRegular } = require('./util/pdfDocument')
|
|
3
|
+
const { convertToLocaleString, convertMS, getTranslations } = require('./util/utils')
|
|
4
|
+
const { getStyle } = require('./reportStyle')
|
|
5
|
+
const { getUserPartner } = require('fleetmap-partners')
|
|
6
|
+
const { devicesToProcess } = require('./util/device')
|
|
7
|
+
const automaticReports = require('./automaticReports')
|
|
8
|
+
const traccarHelper = require('./util/traccar')
|
|
8
9
|
|
|
9
10
|
const fileName = 'IdleReport'
|
|
10
11
|
|
|
11
|
-
async function createIdleReport(from, to, userData, traccarInstance) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
12
|
+
async function createIdleReport (from, to, userData, traccarInstance) {
|
|
13
|
+
console.log('Create IdleReport')
|
|
14
|
+
const reportData = []
|
|
15
|
+
|
|
16
|
+
if (userData.byDriver) {
|
|
17
|
+
console.log('ByDriver')
|
|
18
|
+
const report = await createIdleReportByDriver(from, to, userData, traccarInstance)
|
|
19
|
+
reportData.push(report)
|
|
20
|
+
} else if (userData.byGroup) {
|
|
21
|
+
console.log('ByGroup')
|
|
22
|
+
const allData = await createIdleReportByGroup(from, to, userData, traccarInstance)
|
|
23
|
+
reportData.push(...allData)
|
|
24
|
+
} else {
|
|
25
|
+
console.log('ByDevice')
|
|
26
|
+
const report = await createIdleReportByDevice(from, to, userData, traccarInstance)
|
|
27
|
+
reportData.push(report)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return reportData
|
|
26
31
|
}
|
|
27
32
|
|
|
28
|
-
async function
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const devices = userData.devices.filter(d => d.groupId === g.id)
|
|
32
|
-
console.log(g.name + ' devices:' + devices.length)
|
|
33
|
-
if(devices.length > 0) {
|
|
34
|
-
const groupData = {
|
|
35
|
-
devices: [],
|
|
36
|
-
group: g,
|
|
37
|
-
from: from,
|
|
38
|
-
to: to
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const response = await traccarInstance.reports.reportsRouteGet(from, to, null, [g.id])
|
|
42
|
-
const routes = response.data
|
|
43
|
-
|
|
44
|
-
devices.sort((a, b) => (a.name > b.name) ? 1 : -1)
|
|
45
|
-
|
|
46
|
-
if (routes.length > 0) {
|
|
47
|
-
console.log('Routes:' + routes.length)
|
|
48
|
-
groupData.devices = processDevices(from, to, devices, routes, userData)
|
|
49
|
-
|
|
50
|
-
reportData.push(groupData)
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
33
|
+
async function createIdleReportByDriver (from, to, userData, traccarInstance) {
|
|
34
|
+
const devices = await traccarInstance.devices.devicesGet().then(d => d.data)
|
|
35
|
+
console.log(devices.length)
|
|
54
36
|
|
|
55
|
-
|
|
56
|
-
|
|
37
|
+
if (!devices.length) {
|
|
38
|
+
// Empty report
|
|
39
|
+
return { drivers: [] }
|
|
40
|
+
}
|
|
57
41
|
|
|
58
|
-
|
|
59
|
-
reportData.push(allData)
|
|
42
|
+
const { route } = await traccarHelper.getAllInOne(traccarInstance, from, to, devices, true, false, false, false)
|
|
60
43
|
|
|
61
|
-
|
|
44
|
+
return { drivers: processDrivers(from, to, userData, route) }
|
|
62
45
|
}
|
|
63
46
|
|
|
64
|
-
async function
|
|
65
|
-
|
|
47
|
+
async function createIdleReportByGroup (from, to, userData, traccarInstance) {
|
|
48
|
+
const reportData = []
|
|
49
|
+
for (const g of userData.groups) {
|
|
50
|
+
const devices = userData.devices.filter(d => d.groupId === g.id)
|
|
51
|
+
console.log(g.name + ' devices:' + devices.length)
|
|
52
|
+
if (devices.length > 0) {
|
|
53
|
+
const groupData = {
|
|
66
54
|
devices: [],
|
|
67
|
-
|
|
68
|
-
|
|
55
|
+
group: g,
|
|
56
|
+
from,
|
|
57
|
+
to
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const response = await traccarInstance.reports.reportsRouteGet(from, to, null, [g.id])
|
|
61
|
+
const routes = response.data
|
|
62
|
+
|
|
63
|
+
devices.sort((a, b) => (a.name > b.name) ? 1 : -1)
|
|
64
|
+
|
|
65
|
+
if (routes.length > 0) {
|
|
66
|
+
console.log('Routes:' + routes.length)
|
|
67
|
+
groupData.devices = processDevices(from, to, devices, routes, userData)
|
|
68
|
+
|
|
69
|
+
reportData.push(groupData)
|
|
70
|
+
}
|
|
69
71
|
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const groupIds = userData.groups.map(g => g.id)
|
|
75
|
+
const withoutGroupDevices = userData.devices.filter(d => !groupIds.includes(d.groupId))
|
|
70
76
|
|
|
71
|
-
|
|
77
|
+
const allData = await createIdleReportByDevice(from, to, withoutGroupDevices, userData, traccarInstance)
|
|
78
|
+
reportData.push(allData)
|
|
72
79
|
|
|
73
|
-
|
|
80
|
+
return reportData
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function createIdleReportByDevice (from, to, userData, traccarInstance) {
|
|
84
|
+
const allData = {
|
|
85
|
+
devices: [],
|
|
86
|
+
from,
|
|
87
|
+
to
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const devices = devicesToProcess(userData)
|
|
91
|
+
const sliced = automaticReports.sliceArray(devices, 5)
|
|
74
92
|
|
|
75
|
-
|
|
93
|
+
let deviceCount = 0
|
|
94
|
+
for (const slice of sliced) {
|
|
95
|
+
const { route } = await traccarHelper.getAllInOne(traccarInstance, from, to, slice, true, false, false, false, deviceCount, devices.length)
|
|
76
96
|
|
|
77
97
|
if (route.length > 0) {
|
|
78
|
-
|
|
98
|
+
allData.devices.push(...processDevices(from, to, slice, route, userData))
|
|
79
99
|
}
|
|
80
100
|
|
|
81
|
-
|
|
101
|
+
deviceCount = deviceCount + slice.length
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return allData
|
|
82
105
|
}
|
|
83
106
|
|
|
84
|
-
function
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const route = routes.filter(p => p.deviceId === d.id)
|
|
89
|
-
|
|
90
|
-
const idleEvents = []
|
|
91
|
-
let inIdle = false
|
|
92
|
-
route.forEach(p => {
|
|
93
|
-
if(p.attributes.ignition && p.speed === 0){
|
|
94
|
-
if(!inIdle) {
|
|
95
|
-
const idleEvent = {
|
|
96
|
-
position: p,
|
|
97
|
-
idleTime: 0
|
|
98
|
-
}
|
|
99
|
-
idleEvents.push(idleEvent)
|
|
100
|
-
inIdle = true
|
|
101
|
-
}
|
|
102
|
-
} else if(inIdle) {
|
|
103
|
-
const currentIdleEvent = idleEvents[idleEvents.length-1]
|
|
104
|
-
currentIdleEvent.idleTime = new Date(p.fixTime) - new Date(currentIdleEvent.position.fixTime)
|
|
105
|
-
inIdle = false
|
|
106
|
-
}
|
|
107
|
-
})
|
|
107
|
+
function processDrivers (from, to, userData, routes) {
|
|
108
|
+
const driversResult = []
|
|
109
|
+
userData.drivers.forEach(d => {
|
|
110
|
+
const route = routes.filter(p => p.attributes.driverUniqueId === d.uniqueId || !p.attributes.ignition)
|
|
108
111
|
|
|
109
|
-
|
|
112
|
+
const idleEvents = getIdleEvents(route)
|
|
113
|
+
console.log(idleEvents)
|
|
114
|
+
if (route.length > 0) {
|
|
115
|
+
const filteredEvents = idleEvents.filter(e => userData.minimumIdleMinutes ? e.idleTime > userData.minimumIdleMinutes * 60 * 1000 : true)
|
|
110
116
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
devicesResult.push(deviceData)
|
|
117
|
+
if (filteredEvents.length) {
|
|
118
|
+
const driverData = {
|
|
119
|
+
driver: d,
|
|
120
|
+
idleEvents: filteredEvents
|
|
117
121
|
}
|
|
118
|
-
|
|
122
|
+
driversResult.push(driverData)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
})
|
|
119
126
|
|
|
120
|
-
|
|
127
|
+
return driversResult
|
|
121
128
|
}
|
|
122
129
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const timezone = userData.user.attributes.timezone
|
|
127
|
-
const lang = userData.user.attributes.lang || (navigator && navigator.language)
|
|
128
|
-
const translations = getTranslations(userData)
|
|
130
|
+
function processDevices (from, to, devices, routes, userData) {
|
|
131
|
+
const devicesResult = []
|
|
129
132
|
|
|
130
|
-
|
|
133
|
+
devices.forEach(d => {
|
|
134
|
+
const route = routes.filter(p => p.deviceId === d.id)
|
|
131
135
|
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
translations.report.duration
|
|
136
|
-
]
|
|
136
|
+
const idleEvents = getIdleEvents(route)
|
|
137
|
+
console.log(idleEvents)
|
|
138
|
+
const filteredEvents = idleEvents.filter(e => userData.minimumIdleMinutes ? e.idleTime > userData.minimumIdleMinutes * 60 * 1000 : true)
|
|
137
139
|
|
|
138
|
-
if (
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
if (filteredEvents.length) {
|
|
141
|
+
const deviceData = {
|
|
142
|
+
device: d,
|
|
143
|
+
idleEvents: filteredEvents
|
|
144
|
+
}
|
|
145
|
+
devicesResult.push(deviceData)
|
|
142
146
|
}
|
|
147
|
+
})
|
|
143
148
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const doc = new jsPDF.jsPDF('l');
|
|
147
|
-
await headerFromUser(doc, translations.report.titleIdleReport, userData.user)
|
|
148
|
-
|
|
149
|
-
idleData.forEach(d => {
|
|
150
|
-
try {
|
|
151
|
-
let data = []
|
|
152
|
-
|
|
153
|
-
const name = userData.byDriver ? d.driver.name : deviceName(d.device)
|
|
154
|
-
const group = userData.byDriver ? userData.groups.find(g => g.drivers.includes(d.id)) : userData.groups.find(g => d.device.groupId === g.id)
|
|
155
|
-
|
|
156
|
-
let space = 0
|
|
157
|
-
if(!first) {
|
|
158
|
-
doc.addPage()
|
|
159
|
-
} else {
|
|
160
|
-
first = false
|
|
161
|
-
space = 10
|
|
162
|
-
}
|
|
163
|
-
doc.setFontSize(13)
|
|
164
|
-
doc.text(name, 20, space+20 )
|
|
165
|
-
doc.setFontSize(11)
|
|
166
|
-
doc.text(group ? translations.report.group + ': ' + group.name : '', 150, space+20 )
|
|
167
|
-
doc.text(convertToLocaleString(reportData.from, lang, timezone) + ' - ' + convertToLocaleString(reportData.to, lang, timezone), 20, space+25 )
|
|
168
|
-
|
|
169
|
-
d.idleEvents.map(a => {
|
|
170
|
-
const temp = [
|
|
171
|
-
getIdleEventDate(a, userData.user),
|
|
172
|
-
a.position.address,
|
|
173
|
-
convertMS(a.idleTime, true),
|
|
174
|
-
userData.byDriver ? a.deviceName : a.driver
|
|
175
|
-
]
|
|
176
|
-
|
|
177
|
-
data.push(temp)
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
const footValues = [
|
|
181
|
-
'Total:' + d.idleEvents.length,
|
|
182
|
-
'',
|
|
183
|
-
convertMS(d.idleEvents.reduce((a, b) => a + b.idleTime, 0), true)
|
|
184
|
-
]
|
|
185
|
-
|
|
186
|
-
if(userData.roadSpeedLimits){
|
|
187
|
-
footValues.splice(2, 0, '')
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if(timezone === 'Asia/Qatar') {
|
|
191
|
-
doc.addFileToVFS("Amiri-Regular.ttf", AmiriRegular());
|
|
192
|
-
doc.addFont("Amiri-Regular.ttf", "Amiri", "normal");
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const style = getStyle(getUserPartner(userData.user))
|
|
196
|
-
doc.autoTable({
|
|
197
|
-
head: [headers],
|
|
198
|
-
body: data,
|
|
199
|
-
foot: [footValues],
|
|
200
|
-
showFoot: 'lastPage',
|
|
201
|
-
headStyles: {
|
|
202
|
-
fillColor: style.pdfHeaderColor,
|
|
203
|
-
textColor: style.pdfHeaderTextColor,
|
|
204
|
-
fontSize: 10
|
|
205
|
-
},
|
|
206
|
-
bodyStyles: {
|
|
207
|
-
fillColor: style.pdfBodyColor,
|
|
208
|
-
textColor: style.pdfBodyTextColor,
|
|
209
|
-
fontSize: 8,
|
|
210
|
-
font: timezone === 'Asia/Qatar' ? "Amiri" : ''
|
|
211
|
-
|
|
212
|
-
},
|
|
213
|
-
footStyles: {
|
|
214
|
-
fillColor: style.pdfFooterColor,
|
|
215
|
-
textColor: style.pdfFooterTextColor,
|
|
216
|
-
fontSize: 9
|
|
217
|
-
},
|
|
218
|
-
startY: space+35 });
|
|
219
|
-
} catch (e) {
|
|
220
|
-
console.error(e, 'moving on...')
|
|
221
|
-
}
|
|
222
|
-
})
|
|
149
|
+
return devicesResult
|
|
150
|
+
}
|
|
223
151
|
|
|
224
|
-
|
|
225
|
-
|
|
152
|
+
function getIdleEvents (route) {
|
|
153
|
+
const idleEvents = []
|
|
154
|
+
|
|
155
|
+
const routeByDevice = route.reduce(function (a, x) {
|
|
156
|
+
(a[x.deviceId] = a[x.deviceId] || []).push(x)
|
|
157
|
+
return a
|
|
158
|
+
}, {})
|
|
159
|
+
|
|
160
|
+
Object.keys(routeByDevice).forEach(function (key) {
|
|
161
|
+
let inIdle = false
|
|
162
|
+
routeByDevice[key].forEach(p => {
|
|
163
|
+
if (p.attributes.ignition && p.speed === 0) {
|
|
164
|
+
if (!inIdle) {
|
|
165
|
+
const idleEvent = {
|
|
166
|
+
position: p,
|
|
167
|
+
idleTime: 0
|
|
168
|
+
}
|
|
169
|
+
idleEvents.push(idleEvent)
|
|
170
|
+
inIdle = true
|
|
171
|
+
} else {
|
|
172
|
+
if (!idleEvents[idleEvents.length - 1].position.attributes.driverUniqueId) {
|
|
173
|
+
idleEvents[idleEvents.length - 1].position.attributes.driverUniqueId = p.attributes.driverUniqueId
|
|
174
|
+
}
|
|
175
|
+
if (p.attributes.idleTime) {
|
|
176
|
+
idleEvents[idleEvents.length - 1].idleTime = p.attributes.idleTime
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
} else if (inIdle) {
|
|
180
|
+
const currentIdleEvent = idleEvents[idleEvents.length - 1]
|
|
181
|
+
if (p.attributes.idleTime === undefined) {
|
|
182
|
+
currentIdleEvent.idleTime = new Date(p.fixTime) - new Date(currentIdleEvent.position.fixTime)
|
|
183
|
+
}
|
|
184
|
+
inIdle = false
|
|
185
|
+
}
|
|
186
|
+
})
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
return idleEvents
|
|
226
190
|
}
|
|
227
191
|
|
|
228
|
-
function
|
|
229
|
-
|
|
192
|
+
async function exportIdleReportToPDF (userData, reportData) {
|
|
193
|
+
console.log('Export to PDF')
|
|
194
|
+
|
|
195
|
+
const timezone = userData.user.attributes.timezone
|
|
196
|
+
const lang = userData.user.attributes.lang || (navigator && navigator.language)
|
|
197
|
+
const translations = getTranslations(userData)
|
|
198
|
+
|
|
199
|
+
const idleData = userData.byDriver ? reportData.drivers : reportData.devices
|
|
200
|
+
|
|
201
|
+
const headers = [
|
|
202
|
+
translations.report.date,
|
|
203
|
+
translations.report.address,
|
|
204
|
+
translations.report.duration
|
|
205
|
+
]
|
|
206
|
+
|
|
207
|
+
if (userData.byDriver) {
|
|
208
|
+
headers.push(translations.report.vehicle)
|
|
209
|
+
} else {
|
|
210
|
+
headers.push(translations.report.driver)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (idleData) {
|
|
214
|
+
let first = true
|
|
215
|
+
// eslint-disable-next-line new-cap
|
|
216
|
+
const doc = new jsPDF.jsPDF('l')
|
|
217
|
+
await headerFromUser(doc, translations.report.titleIdleReport, userData.user)
|
|
218
|
+
|
|
219
|
+
idleData.forEach(d => {
|
|
220
|
+
try {
|
|
221
|
+
const data = []
|
|
222
|
+
|
|
223
|
+
const name = userData.byDriver ? d.driver.name : deviceName(d.device)
|
|
224
|
+
const group = userData.byDriver ? userData.groups.find(g => g.drivers.includes(d.id)) : userData.groups.find(g => d.device.groupId === g.id)
|
|
225
|
+
|
|
226
|
+
let space = 0
|
|
227
|
+
if (!first) {
|
|
228
|
+
doc.addPage()
|
|
229
|
+
} else {
|
|
230
|
+
first = false
|
|
231
|
+
space = 10
|
|
232
|
+
}
|
|
233
|
+
doc.setFontSize(13)
|
|
234
|
+
doc.text(name, 20, space + 20)
|
|
235
|
+
doc.setFontSize(11)
|
|
236
|
+
doc.text(group ? translations.report.group + ': ' + group.name : '', 150, space + 20)
|
|
237
|
+
doc.text(convertToLocaleString(reportData.from, lang, timezone) + ' - ' + convertToLocaleString(reportData.to, lang, timezone), 20, space + 25)
|
|
238
|
+
|
|
239
|
+
d.idleEvents.forEach(a => {
|
|
240
|
+
const temp = [
|
|
241
|
+
getIdleEventDate(a, userData.user),
|
|
242
|
+
a.position.address,
|
|
243
|
+
convertMS(a.idleTime, true),
|
|
244
|
+
userData.byDriver ? a.deviceName : a.driver
|
|
245
|
+
]
|
|
246
|
+
data.push(temp)
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
const footValues = [
|
|
250
|
+
'Total:' + d.idleEvents.length,
|
|
251
|
+
'',
|
|
252
|
+
convertMS(d.idleEvents.reduce((a, b) => a + b.idleTime, 0), true)
|
|
253
|
+
]
|
|
230
254
|
|
|
231
|
-
|
|
255
|
+
if (userData.roadSpeedLimits) {
|
|
256
|
+
footValues.splice(2, 0, '')
|
|
257
|
+
}
|
|
232
258
|
|
|
233
|
-
|
|
259
|
+
if (timezone === 'Asia/Qatar') {
|
|
260
|
+
doc.addFileToVFS('Amiri-Regular.ttf', AmiriRegular())
|
|
261
|
+
doc.addFont('Amiri-Regular.ttf', 'Amiri', 'normal')
|
|
262
|
+
}
|
|
234
263
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
}))
|
|
264
|
+
const style = getStyle(getUserPartner(userData.user))
|
|
265
|
+
doc.autoTable({
|
|
266
|
+
head: [headers],
|
|
267
|
+
body: data,
|
|
268
|
+
foot: [footValues],
|
|
269
|
+
showFoot: 'lastPage',
|
|
270
|
+
headStyles: {
|
|
271
|
+
fillColor: style.pdfHeaderColor,
|
|
272
|
+
textColor: style.pdfHeaderTextColor,
|
|
273
|
+
fontSize: 10
|
|
274
|
+
},
|
|
275
|
+
bodyStyles: {
|
|
276
|
+
fillColor: style.pdfBodyColor,
|
|
277
|
+
textColor: style.pdfBodyTextColor,
|
|
278
|
+
fontSize: 8,
|
|
279
|
+
font: timezone === 'Asia/Qatar' ? 'Amiri' : ''
|
|
280
|
+
|
|
281
|
+
},
|
|
282
|
+
footStyles: {
|
|
283
|
+
fillColor: style.pdfFooterColor,
|
|
284
|
+
textColor: style.pdfFooterTextColor,
|
|
285
|
+
fontSize: 9
|
|
286
|
+
},
|
|
287
|
+
startY: space + 35
|
|
260
288
|
})
|
|
261
|
-
|
|
289
|
+
} catch (e) {
|
|
290
|
+
console.error(e, 'moving on...')
|
|
291
|
+
}
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
return doc
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function exportIdleReportToExcel (userData, reportData) {
|
|
299
|
+
console.log('Export to Excel')
|
|
300
|
+
|
|
301
|
+
const translations = getTranslations(userData)
|
|
302
|
+
|
|
303
|
+
const idleData = userData.byDriver ? reportData.drivers : reportData.devices
|
|
304
|
+
|
|
305
|
+
const settings = {
|
|
306
|
+
sheetName: translations.report.titleIdleReport.substring(0, 31), // The name of the sheet
|
|
307
|
+
fileName // The name of the spreadsheet
|
|
308
|
+
}
|
|
309
|
+
const headers = [
|
|
310
|
+
userData.byDriver ? { label: translations.report.driver, value: 'driver' } : { label: translations.report.vehicle, value: 'name' },
|
|
311
|
+
{ label: translations.report.date, value: 'fixTime' },
|
|
312
|
+
{ label: translations.report.address, value: 'address' },
|
|
313
|
+
{ label: translations.report.duration, value: 'duration' },
|
|
314
|
+
userData.byDriver ? { label: translations.report.vehicle, value: 'name' } : { label: translations.report.driver, value: 'driver' }
|
|
315
|
+
]
|
|
316
|
+
|
|
317
|
+
let data = []
|
|
318
|
+
if (idleData) {
|
|
319
|
+
idleData.forEach(d => {
|
|
320
|
+
data = data.concat([{}])
|
|
321
|
+
data = data.concat(d.idleEvents.map(a => {
|
|
262
322
|
return {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
323
|
+
driver: userData.byDriver ? d.driver.name : a.driver,
|
|
324
|
+
duration: convertMS(a.idleTime, true),
|
|
325
|
+
fixTime: getIdleEventDate(a, userData.user),
|
|
326
|
+
name: userData.byDriver ? a.deviceName : d.device.name,
|
|
327
|
+
address: a.position.address + (a.geofenceName ? ' - ' + a.geofenceName : '')
|
|
266
328
|
}
|
|
329
|
+
}))
|
|
330
|
+
})
|
|
331
|
+
console.log(data)
|
|
332
|
+
return {
|
|
333
|
+
headers,
|
|
334
|
+
data,
|
|
335
|
+
settings
|
|
267
336
|
}
|
|
337
|
+
}
|
|
268
338
|
}
|
|
269
339
|
|
|
270
|
-
function deviceName(device){
|
|
271
|
-
|
|
340
|
+
function deviceName (device) {
|
|
341
|
+
return device.name + (device.attributes.license_plate ? ', ' + device.attributes.license_plate : '') + (device.model ? ', ' + device.model : '')
|
|
272
342
|
}
|
|
273
343
|
|
|
274
|
-
function getIdleEventDate(row, user) {
|
|
275
|
-
|
|
344
|
+
function getIdleEventDate (row, user) {
|
|
345
|
+
return convertToLocaleString(row.position.fixTime, user.attributes.lang, user.attributes.timezone)
|
|
276
346
|
}
|
|
277
347
|
|
|
278
|
-
exports.createIdleReport = createIdleReport
|
|
279
|
-
exports.exportIdleReportToPDF = exportIdleReportToPDF
|
|
280
|
-
exports.exportIdleReportToExcel = exportIdleReportToExcel
|
|
348
|
+
exports.createIdleReport = createIdleReport
|
|
349
|
+
exports.exportIdleReportToPDF = exportIdleReportToPDF
|
|
350
|
+
exports.exportIdleReportToExcel = exportIdleReportToExcel
|
package/src/index.test.js
CHANGED
|
@@ -197,6 +197,21 @@ describe('Test_Reports', function () {
|
|
|
197
197
|
assert.equal(totalIdleTime, 1294000) // Total Duration
|
|
198
198
|
}, 20000)
|
|
199
199
|
// eslint-disable-next-line no-undef
|
|
200
|
+
it('Idle by driver', async () => {
|
|
201
|
+
const report = await getReports()
|
|
202
|
+
const userData = await report.getUserData()
|
|
203
|
+
userData.minimumIdleMinutes = 2
|
|
204
|
+
userData.byDriver = true
|
|
205
|
+
const data = await report.idleReport(new Date(2022, 0, 3, 0, 0, 0, 0),
|
|
206
|
+
new Date(2022, 0, 7, 23, 59, 59, 0),
|
|
207
|
+
userData)
|
|
208
|
+
assert.equal(data.length, 1)
|
|
209
|
+
const driver = data[0].drivers.find(d => d.driver.id === 14020)
|
|
210
|
+
const totalIdleTime = driver.idleEvents.reduce((a, b) => a + b.idleTime, 0)
|
|
211
|
+
assert.equal(driver.idleEvents.length, 12) // Total Alerts
|
|
212
|
+
assert.equal(totalIdleTime, 30785000) // Total Duration
|
|
213
|
+
}, 20000)
|
|
214
|
+
// eslint-disable-next-line no-undef
|
|
200
215
|
it('Activity by device', async () => {
|
|
201
216
|
const report = await getReports()
|
|
202
217
|
const userData = await report.getUserData()
|
package/src/util/traccar.js
CHANGED
|
@@ -77,7 +77,8 @@ async function getAllInOne (
|
|
|
77
77
|
currentDeviceCount = 0,
|
|
78
78
|
totalDevices = devices.length,
|
|
79
79
|
sliceSize = 5,
|
|
80
|
-
devicesPerRequest = 1
|
|
80
|
+
devicesPerRequest = 1,
|
|
81
|
+
counter = undefined) {
|
|
81
82
|
let url = `/reports/allinone?from=${from.toISOString()}&to=${to.toISOString()}`
|
|
82
83
|
if (getRoutes) url += '&type=route'
|
|
83
84
|
if (getTrips) url += '&type=trips'
|
|
@@ -96,8 +97,13 @@ async function getAllInOne (
|
|
|
96
97
|
withCredentials: true
|
|
97
98
|
}).then(r => r.data).then(x => {
|
|
98
99
|
console.log('LOADING_MESSAGE:' + _chunk[0].name)
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
if (counter) {
|
|
101
|
+
counter.count += devicesPerRequest
|
|
102
|
+
console.log(`PROGRESS_PERC:${counter.count / totalDevices * 100}`)
|
|
103
|
+
} else {
|
|
104
|
+
currentDeviceCount += devicesPerRequest
|
|
105
|
+
console.log(`PROGRESS_PERC:${currentDeviceCount / totalDevices * 100}`)
|
|
106
|
+
}
|
|
101
107
|
return x
|
|
102
108
|
}))
|
|
103
109
|
}
|
package/src/zone-report.js
CHANGED
|
@@ -53,14 +53,14 @@ async function createZoneReport (from, to, userData, traccar) {
|
|
|
53
53
|
xpert: devices.filter(d => d.attributes.xpert).length > 0
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
const sliced = automaticReports.sliceArray(devices,
|
|
56
|
+
const sliced = automaticReports.sliceArray(devices, 30)
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
const deviceCount = { count: 0 }
|
|
59
59
|
const promises = []
|
|
60
60
|
for (const slice of sliced) {
|
|
61
61
|
promises.push(traccarHelper.getAllInOne(
|
|
62
62
|
traccar, from, to, slice, true, false, false, false,
|
|
63
|
-
deviceCount, devices.length,
|
|
63
|
+
deviceCount, devices.length, 20, 5, deviceCount).then(async allInOne => {
|
|
64
64
|
const routeData = allInOne.route
|
|
65
65
|
|
|
66
66
|
const data = getInAndOutEvents(slice, routeData, userData)
|
|
@@ -70,7 +70,6 @@ async function createZoneReport (from, to, userData, traccar) {
|
|
|
70
70
|
if (data.length) {
|
|
71
71
|
allData.devices.push(...await processDevices(from, to, devices, userData.drivers, userData.geofences, data, traccar))
|
|
72
72
|
}
|
|
73
|
-
deviceCount = deviceCount + slice.length
|
|
74
73
|
}))
|
|
75
74
|
}
|
|
76
75
|
await Promise.all(promises)
|