fleetmap-reports 2.0.366 → 2.0.367
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/package.json
CHANGED
|
@@ -35,65 +35,85 @@ async function createFuelConsumptionReport (from, to, userData, traccar) {
|
|
|
35
35
|
return reportData
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
const daysByDevice = new Map()
|
|
39
|
+
|
|
40
|
+
// Process the period in chunks of whole calendar days (computed once up front, then grouped) so
|
|
41
|
+
// we never hold more than ~a week of raw GPS positions for the whole device set in memory at
|
|
42
|
+
// once - a single busy device can have tens of thousands of positions over a month. Chunking the
|
|
43
|
+
// already-computed dates list, rather than re-deriving dates per raw millisecond sub-range, avoids
|
|
44
|
+
// any risk of a boundary day being counted twice (or dropped) due to getDates' timezone rounding.
|
|
45
|
+
const allDates = getDates(from, to, userData.user.attributes.timezone || getUserPartner(userData.user).timezone)
|
|
46
|
+
const dateGroups = automaticReports.sliceArray(allDates, 7)
|
|
47
|
+
|
|
48
|
+
for (let chunkIndex = 0; chunkIndex < dateGroups.length; chunkIndex++) {
|
|
49
|
+
const dates = dateGroups[chunkIndex]
|
|
50
|
+
const chunkFrom = chunkIndex === 0 ? from : dates[0]
|
|
51
|
+
const chunkTo = chunkIndex === dateGroups.length - 1 ? to : dateGroups[chunkIndex + 1][0]
|
|
52
|
+
|
|
53
|
+
const allInOne = await traccarHelper.getAllInOne(traccar, chunkFrom, chunkTo, devices, true, true, true, false)
|
|
54
|
+
const summary = await getSummaryByDay(dates, userData, traccar, devices, true, chunkFrom, chunkTo)
|
|
55
|
+
|
|
56
|
+
const fuelServicesData = []
|
|
57
|
+
if (devices.some(d => d.attributes.odooId)) {
|
|
58
|
+
fuelServicesData.push(...(await odoo.getOdooFuelServices(traccar, chunkFrom, chunkTo)))
|
|
59
|
+
}
|
|
41
60
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
61
|
+
if (allInOne.trips.length > 0) {
|
|
62
|
+
for (const d of devices) {
|
|
63
|
+
const stops = allInOne.stops.filter(t => t.deviceId === d.id)
|
|
64
|
+
const trips = allInOne.trips.filter(t => t.deviceId === d.id)
|
|
65
|
+
const route = allInOne.route.filter(r => r.deviceId === d.id)
|
|
66
|
+
const deviceFuelServices = fuelServicesData.filter(f => Number.parseInt(d.attributes.odooId) === f.vehicle_id[0])
|
|
67
|
+
|
|
68
|
+
if (trips.length > 0) {
|
|
69
|
+
const refuelingPositions = await refuelingReport.calculateRefuelingPositions(userData, d, { route, trips, fuelServices: deviceFuelServices })
|
|
70
|
+
const days = daysByDevice.get(d.id) || []
|
|
71
|
+
for (const date of dates) {
|
|
72
|
+
const { tripsByDay, stopsByDay, routeByDay, refuelingPositionsByDay } = await getDataByDay(d, date, { trips, stops, route, refuelingPositions }, userData, traccar)
|
|
73
|
+
const summaryByDay = summary.find(a => a.date === date).summary
|
|
74
|
+
const summaryByDevice = summaryByDay.find(a => a.deviceId === d.id)
|
|
75
|
+
|
|
76
|
+
const firstStopIndex = !stopsByDay.length || !tripsByDay.length ? 0 : (new Date(tripsByDay[0].startTime) < new Date(stopsByDay[0].startTime) ? 0 : 1)
|
|
77
|
+
const distance = summaryByDevice.distance
|
|
78
|
+
|
|
79
|
+
const hasFuelSensor = 'fuel_low_threshold' in d.attributes &&
|
|
80
|
+
'fuel_high_threshold' in d.attributes &&
|
|
81
|
+
d.attributes.fuel_tank_capacity
|
|
82
|
+
|
|
83
|
+
const spentFuel = hasFuelSensor ?
|
|
84
|
+
tripsByDay.length > 0 ? calculateTotalFuelFromSensor(tripsByDay, stopsByDay, route, d, firstStopIndex) : 0
|
|
85
|
+
: calculateConsumption(d, { trips: tripsByDay, stops: stopsByDay, route: routeByDay, summary: summaryByDevice })
|
|
86
|
+
|
|
87
|
+
const dataRow = {
|
|
88
|
+
date,
|
|
89
|
+
distance,
|
|
90
|
+
spentFuel,
|
|
91
|
+
avgConsumption: getCanAvgConsumption(distance, spentFuel),
|
|
92
|
+
startOdometer: summaryByDevice.startOdometer,
|
|
93
|
+
endOdometer: summaryByDevice.endOdometer,
|
|
94
|
+
duration: tripsByDay.reduce((a, b) => a + b.duration, 0),
|
|
95
|
+
refueling: (refuelingPositionsByDay && refuelingPositionsByDay.length > 0) ? refuelingPositionsByDay.reduce((a, b) => a + b.diff, 0) : 0
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
days.push(dataRow)
|
|
99
|
+
}
|
|
100
|
+
daysByDevice.set(d.id, days)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
46
104
|
|
|
47
|
-
|
|
48
|
-
|
|
105
|
+
console.log('LOADING_MESSAGE:' + (devices[0] && devices[0].name))
|
|
106
|
+
console.log(`PROGRESS_PERC:${(chunkIndex + 1) / dateGroups.length * 100}`)
|
|
49
107
|
}
|
|
50
108
|
|
|
51
109
|
for (const d of devices) {
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const days = []
|
|
60
|
-
for (const date of dates) {
|
|
61
|
-
const { tripsByDay, stopsByDay, routeByDay, refuelingPositionsByDay } = await getDataByDay(d, date, { trips, stops, route, refuelingPositions }, userData, traccar)
|
|
62
|
-
const summaryByDay = summary.find(a => a.date === date).summary
|
|
63
|
-
const summaryByDevice = summaryByDay.find(a => a.deviceId === d.id)
|
|
64
|
-
|
|
65
|
-
const firstStopIndex = !stopsByDay.length || !tripsByDay.length ? 0 : (new Date(tripsByDay[0].startTime) < new Date(stopsByDay[0].startTime) ? 0 : 1)
|
|
66
|
-
const distance = summaryByDevice.distance
|
|
67
|
-
|
|
68
|
-
const hasFuelSensor = 'fuel_low_threshold' in d.attributes &&
|
|
69
|
-
'fuel_high_threshold' in d.attributes &&
|
|
70
|
-
d.attributes.fuel_tank_capacity
|
|
71
|
-
|
|
72
|
-
const spentFuel = hasFuelSensor ?
|
|
73
|
-
tripsByDay.length > 0 ? calculateTotalFuelFromSensor(tripsByDay, stopsByDay, route, d, firstStopIndex) : 0
|
|
74
|
-
: calculateConsumption(d, { trips: tripsByDay, stops: stopsByDay, route: routeByDay, summary: summaryByDevice })
|
|
75
|
-
|
|
76
|
-
const dataRow = {
|
|
77
|
-
date,
|
|
78
|
-
distance,
|
|
79
|
-
spentFuel,
|
|
80
|
-
avgConsumption: getCanAvgConsumption(distance, spentFuel),
|
|
81
|
-
startOdometer: summaryByDevice.startOdometer,
|
|
82
|
-
endOdometer: summaryByDevice.endOdometer,
|
|
83
|
-
duration: tripsByDay.reduce((a, b) => a + b.duration, 0),
|
|
84
|
-
refueling: (refuelingPositionsByDay && refuelingPositionsByDay.length > 0) ? refuelingPositionsByDay.reduce((a, b) => a + b.diff, 0) : 0
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
days.push(dataRow)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (days.length > 0) {
|
|
91
|
-
allData.devices.push({
|
|
92
|
-
device: d,
|
|
93
|
-
days
|
|
94
|
-
})
|
|
95
|
-
allData.totalDevices = allData.totalDevices + 1
|
|
96
|
-
}
|
|
110
|
+
const days = daysByDevice.get(d.id)
|
|
111
|
+
if (days && days.length > 0) {
|
|
112
|
+
allData.devices.push({
|
|
113
|
+
device: d,
|
|
114
|
+
days
|
|
115
|
+
})
|
|
116
|
+
allData.totalDevices = allData.totalDevices + 1
|
|
97
117
|
}
|
|
98
118
|
}
|
|
99
119
|
|
|
@@ -3,7 +3,7 @@ const traccarHelper = require('../util/traccar')
|
|
|
3
3
|
const { getDriverData, devicesByDriver } = require('../util/driver')
|
|
4
4
|
const { getCanDriverStyleMessages, parseCanDriverStyleMessage } = require('../util/xpert')
|
|
5
5
|
const { calculateRPMSections } = require('./performance-report')
|
|
6
|
-
const { getTranslations, isClientSide, convertToLocaleString, convertMS, getServerHost } = require('../util/utils')
|
|
6
|
+
const { getTranslations, isClientSide, convertToLocaleString, convertMS, getServerHost, splitDateRange } = require('../util/utils')
|
|
7
7
|
const jsPDF = require('jspdf')
|
|
8
8
|
const { headerFromUser, addTable } = require('../util/pdfDocument')
|
|
9
9
|
const { getStyle } = require('../reportStyle')
|
|
@@ -58,20 +58,6 @@ function toGrade (score) {
|
|
|
58
58
|
return grades.find(g => score >= g.min).grade
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
// Splits [from, to] into consecutive sub-ranges of at most chunkDays each
|
|
62
|
-
function splitDateRange (from, to, chunkDays) {
|
|
63
|
-
const chunkMs = chunkDays * 24 * 60 * 60 * 1000
|
|
64
|
-
const end = new Date(to).getTime()
|
|
65
|
-
const chunks = []
|
|
66
|
-
let chunkStart = new Date(from).getTime()
|
|
67
|
-
while (chunkStart < end) {
|
|
68
|
-
const chunkEnd = Math.min(chunkStart + chunkMs, end)
|
|
69
|
-
chunks.push({ from: new Date(chunkStart), to: new Date(chunkEnd) })
|
|
70
|
-
chunkStart = chunkEnd
|
|
71
|
-
}
|
|
72
|
-
return chunks.length ? chunks : [{ from: new Date(from), to: new Date(to) }]
|
|
73
|
-
}
|
|
74
|
-
|
|
75
61
|
async function create (from, to, userData, traccar) {
|
|
76
62
|
const reportData = {
|
|
77
63
|
drivers: []
|
package/src/util/utils.js
CHANGED
|
@@ -110,6 +110,25 @@ function convertToFeature (geofence) {
|
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
// Splits [from, to] into consecutive sub-ranges of at most chunkDays each, so callers can process a
|
|
114
|
+
// long period in bounded-size pieces instead of holding the whole thing (e.g. a month of GPS
|
|
115
|
+
// positions) in memory at once.
|
|
116
|
+
function splitDateRange (from, to, chunkDays) {
|
|
117
|
+
const chunkMs = chunkDays * 24 * 60 * 60 * 1000
|
|
118
|
+
const end = new Date(to).getTime()
|
|
119
|
+
const chunks = []
|
|
120
|
+
let chunkStart = new Date(from).getTime()
|
|
121
|
+
while (chunkStart < end) {
|
|
122
|
+
const chunkEnd = Math.min(chunkStart + chunkMs, end)
|
|
123
|
+
const isLastChunk = chunkEnd >= end
|
|
124
|
+
// Pull the boundary back by 1ms on every chunk but the last, so callers that round to whole
|
|
125
|
+
// calendar days (e.g. getDates) don't count the boundary day in both adjacent chunks.
|
|
126
|
+
chunks.push({ from: new Date(chunkStart), to: new Date(isLastChunk ? chunkEnd : chunkEnd - 1) })
|
|
127
|
+
chunkStart = chunkEnd
|
|
128
|
+
}
|
|
129
|
+
return chunks.length ? chunks : [{ from: new Date(from), to: new Date(to) }]
|
|
130
|
+
}
|
|
131
|
+
|
|
113
132
|
function getDates (startDate, endDate, timezone) {
|
|
114
133
|
const dates = []
|
|
115
134
|
let currentDate = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate(), 12, 0, 0, 0)
|
|
@@ -307,6 +326,7 @@ exports.getImgFromUrl = getImgFromUrl
|
|
|
307
326
|
exports.convertMS = convertMS
|
|
308
327
|
exports.coordsDistance = coordsDistance
|
|
309
328
|
exports.getDates = getDates
|
|
329
|
+
exports.splitDateRange = splitDateRange
|
|
310
330
|
exports.convertToLocaleString = convertToLocaleString
|
|
311
331
|
exports.convertToLocaleDateString = convertToLocaleDateString
|
|
312
332
|
exports.convertToLocaleTimeString = convertToLocaleTimeString
|