/*
* Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
* Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
Ext.define('Traccar.view.ReportController', {
extend: 'Ext.app.ViewController',
alias: 'controller.report',
requires: [
'Traccar.AttributeFormatter',
'Traccar.model.Position',
'Traccar.model.ReportTrip',
'Traccar.view.dialog.ReportConfig',
'Traccar.store.ReportEventTypes'
],
config: {
listen: {
controller: {
'*': {
selectdevice: 'selectDevice',
showsingleevent: 'showSingleEvent',
deselectfeature: 'deselectFeature'
},
'map': {
selectreport: 'selectReport'
}
},
global: {
routegeocode: 'onGeocode'
},
store: {
'#ReportEvents': {
add: 'loadRelatedPositions',
load: 'loadRelatedPositions'
},
'#ReportRoute': {
load: 'loadRoute'
},
'#ReportStops': {
load: 'loadRelatedPositions'
}
}
}
},
hideReports: function () {
Traccar.app.showReports(false);
},
getGrid: function () {
return this.getView().getComponent('grid');
},
getChart: function () {
return this.getView().getComponent('chart');
},
init: function () {
var i, data, attribute;
data = Ext.getStore('PositionAttributes').getData().items;
for (i = 0; i < data.length; i++) {
attribute = data[i];
this.routeColumns.push({
text: attribute.get('name'),
dataIndex: 'attribute.' + attribute.get('key'),
renderer: Traccar.AttributeFormatter.getAttributeFormatter(attribute.get('key')),
hidden: true
});
}
if (Traccar.app.getVehicleFeaturesDisabled()) {
for (i = 0; i < this.summaryColumns.length; i++) {
if (this.summaryColumns[i].dataIndex.match('engineHours|spentFuel')) {
this.summaryColumns[i].hidden = true;
}
}
for (i = 0; i < this.tripsColumns.length; i++) {
if (this.tripsColumns[i].dataIndex.match('spentFuel|driverUniqueId')) {
this.tripsColumns[i].hidden = true;
}
}
for (i = 0; i < this.stopsColumns.length; i++) {
if (this.stopsColumns[i].dataIndex.match('engineHours|spentFuel')) {
this.stopsColumns[i].hidden = true;
}
}
}
},
onConfigureClick: function () {
var dialog = Ext.create('Traccar.view.dialog.ReportConfig');
dialog.lookupReference('eventTypeField').setHidden(this.lookupReference('reportTypeField').getValue() !== 'events');
dialog.lookupReference('chartTypeField').setHidden(this.lookupReference('reportTypeField').getValue() !== 'chart');
dialog.callingPanel = this;
dialog.lookupReference('deviceField').setValue(this.deviceId);
dialog.lookupReference('groupField').setValue(this.groupId);
if (this.eventType !== undefined) {
dialog.lookupReference('eventTypeField').setValue(this.eventType);
} else {
dialog.lookupReference('eventTypeField').setValue([Traccar.store.ReportEventTypes.allEvents]);
}
if (this.chartType !== undefined) {
dialog.lookupReference('chartTypeField').setValue(this.chartType);
}
if (this.showMarkers !== undefined) {
dialog.lookupReference('showMarkersField').setValue(this.showMarkers);
}
if (this.fromDate !== undefined) {
dialog.lookupReference('fromDateField').setValue(this.fromDate);
}
if (this.fromTime !== undefined) {
dialog.lookupReference('fromTimeField').setValue(this.fromTime);
}
if (this.toDate !== undefined) {
dialog.lookupReference('toDateField').setValue(this.toDate);
}
if (this.toTime !== undefined) {
dialog.lookupReference('toTimeField').setValue(this.toTime);
}
if (this.period !== undefined) {
dialog.lookupReference('periodField').setValue(this.period);
}
dialog.show();
},
updateButtons: function () {
var reportType, disabled, devices, time;
reportType = this.lookupReference('reportTypeField').getValue();
devices = this.deviceId && this.deviceId.length !== 0 || this.groupId && this.groupId.length !== 0;
time = this.fromDate && this.fromTime && this.toDate && this.toTime;
disabled = !reportType || !devices || !time || this.reportProgress;
this.lookupReference('showButton').setDisabled(disabled);
this.lookupReference('exportButton').setDisabled(reportType === 'chart' || disabled);
this.lookupReference('emailButton').setDisabled(reportType === 'chart' || disabled);
},
onReportClick: function (button) {
var reportType, from, to, store, url;
this.getGrid().getSelectionModel().deselectAll();
reportType = this.lookupReference('reportTypeField').getValue();
if (reportType && (this.deviceId || this.groupId)) {
from = new Date(
this.fromDate.getFullYear(), this.fromDate.getMonth(), this.fromDate.getDate(),
this.fromTime.getHours(), this.fromTime.getMinutes(), this.fromTime.getSeconds(), this.fromTime.getMilliseconds());
to = new Date(
this.toDate.getFullYear(), this.toDate.getMonth(), this.toDate.getDate(),
this.toTime.getHours(), this.toTime.getMinutes(), this.toTime.getSeconds(), this.toTime.getMilliseconds());
this.reportProgress = true;
this.updateButtons();
if (button.reference === 'showButton') {
if (reportType === 'chart') {
store = this.getChart().getStore();
this.getChart().setSeries([]);
} else {
store = this.getGrid().getStore();
}
store.showMarkers = this.showMarkers;
store.load({
scope: this,
callback: function () {
this.reportProgress = false;
this.updateButtons();
},
params: {
deviceId: this.deviceId,
groupId: this.groupId,
type: this.eventType,
from: from.toISOString(),
to: to.toISOString()
}
});
} else {
url = this.getGrid().getStore().getProxy().url;
this.excelReport(url, {
deviceId: this.deviceId,
groupId: this.groupId,
type: this.eventType,
from: Ext.Date.format(from, 'c'),
to: Ext.Date.format(to, 'c'),
mail: button.reference === 'emailButton'
});
}
}
},
onClearClick: function () {
var reportType = this.lookupReference('reportTypeField').getValue();
this.clearReport(reportType);
},
clearReport: function (reportType) {
this.getGrid().getStore().removeAll();
if (reportType === 'trips' || reportType === 'events' || reportType === 'stops') {
Ext.getStore('ReportRoute').removeAll();
}
if (reportType === 'chart') {
this.getChart().getStore().removeAll();
}
},
onSelectionChange: function (selection, selected) {
var report;
if (selected.length > 0) {
report = selected[0];
this.fireEvent('selectreport', report, true);
if (report instanceof Traccar.model.ReportTrip) {
this.selectTrip(report);
}
if (report instanceof Traccar.model.Event || report instanceof Traccar.model.ReportStop) {
this.selectPositionRelated(report);
}
}
},
selectDevice: function (device) {
if (device) {
this.getGrid().getSelectionModel().deselectAll();
}
},
selectReport: function (object) {
var positionRelated, reportType = this.lookupReference('reportTypeField').getValue();
if (object instanceof Traccar.model.Position) {
if (reportType === 'route') {
this.getGrid().getSelectionModel().select([object], false, true);
this.getGrid().getView().focusRow(object);
} else if (reportType === 'events' || reportType === 'stops') {
positionRelated = this.getGrid().getStore().findRecord('positionId', object.get('id'), 0, false, true, true);
this.getGrid().getSelectionModel().select([positionRelated], false, true);
this.getGrid().getView().focusRow(positionRelated);
}
}
},
deselectFeature: function () {
if (this.lookupReference('reportTypeField').getValue() !== 'trips') {
this.getGrid().getSelectionModel().deselectAll();
}
},
selectTrip: function (trip) {
var from, to;
from = new Date(trip.get('startTime'));
to = new Date(trip.get('endTime'));
Ext.getStore('ReportRoute').removeAll();
Ext.getStore('ReportRoute').showMarkers = this.showMarkers;
Ext.getStore('ReportRoute').load({
params: {
deviceId: trip.get('deviceId'),
from: from.toISOString(),
to: to.toISOString()
}
});
},
selectPositionRelated: function (report) {
var position;
if (report.get('positionId')) {
position = Ext.getStore('ReportRoute').getById(report.get('positionId'));
if (position) {
this.fireEvent('selectreport', position, true);
}
}
},
loadRelatedPositions: function (store, data) {
var i, reportObject, positionIds = [];
Ext.getStore('ReportRoute').removeAll();
for (i = 0; i < data.length; i++) {
reportObject = data[i];
if (reportObject.get('positionId')) {
positionIds.push(reportObject.get('positionId'));
}
}
if (positionIds.length > 0) {
Ext.getStore('Positions').load({
params: {
id: positionIds
},
scope: this,
callback: function (records, operation, success) {
if (success) {
Ext.getStore('ReportRoute').showMarkers = this.showMarkers;
Ext.getStore('ReportRoute').add(records);
if (records.length === 1) {
this.fireEvent('selectreport', records[0], false);
}
}
}
});
}
},
loadRoute: function (store) {
var i, deviceIds, chartSeries, deviceStore;
if (this.lookupReference('reportTypeField').getValue() === 'chart') {
this.getChart().getAxes()[0].setTitle(
Ext.getStore('ReportChartTypes').findRecord('key', this.chartType).get('name'));
chartSeries = [];
deviceIds = store.collect('deviceId');
for (i = 0; i < deviceIds.length; i++) {
deviceStore = Ext.create('Ext.data.ChainedStore', {
source: 'ReportRoute',
filters: [{
property: 'deviceId',
value: deviceIds[i]
}]
});
chartSeries.push({
type: 'line',
store: deviceStore,
yField: this.chartType,
xField: 'fixTime',
highlightCfg: {
scaling: Traccar.Style.chartMarkerHighlightScaling
},
colors: [Traccar.app.getReportColor(deviceIds[i])],
marker: {
type: 'circle',
radius: Traccar.Style.chartMarkerRadius,
fill: Traccar.app.getReportColor(deviceIds[i])
}
});
}
this.getChart().setSeries(chartSeries);
}
},
onChartMarkerClick: function (chart, item) {
this.fireEvent('selectreport', item.record, true);
},
showSingleEvent: function (eventId) {
this.lookupReference('reportTypeField').setValue('events');
Ext.getStore('Events').load({
id: eventId,
scope: this,
callback: function (records, operation, success) {
if (success) {
Ext.getStore('ReportEvents').add(records);
if (records.length > 0) {
if (!records[0].get('positionId')) {
if (Traccar.app.isMobile()) {
Traccar.app.showReports(true);
} else {
this.getView().expand();
}
}
this.getGrid().getSelectionModel().select([records[0]], false, true);
this.getGrid().getView().focusRow(records[0]);
}
}
}
});
},
excelReport: function (requestUrl, requestParams) {
Ext.Ajax.request({
url: requestUrl,
method: 'GET',
timeout: Traccar.Style.reportTimeout,
params: requestParams,
headers: {
Accept: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
},
binary: true,
scope: this,
callback: function (options, success, response) {
var disposition, filename, type, blob, url, downloadUrl;
if (success && !requestParams.mail) {
disposition = response.getResponseHeader('Content-Disposition');
filename = disposition.slice(disposition.indexOf('=') + 1, disposition.length);
type = response.getResponseHeader('Content-Type');
blob = new Blob([response.responseBytes], {type: type});
if (typeof window.navigator.msSaveBlob !== 'undefined') {
// IE workaround
window.navigator.msSaveBlob(blob, filename);
} else {
url = window.URL || window.webkitURL;
downloadUrl = url.createObjectURL(blob);
if (filename) {
Ext.dom.Helper.append(Ext.getBody(), {
tag: 'a',
href: downloadUrl,
download: filename
}).click();
}
setTimeout(function () {
url.revokeObjectURL(downloadUrl);
}, 100);
}
}
this.reportProgress = false;
this.updateButtons();
}
});
},
onTypeChange: function (combobox, newValue, oldValue) {
if (oldValue !== null) {
this.clearReport(oldValue);
}
if (newValue === 'route') {
this.getGrid().reconfigure('ReportRoute', this.routeColumns);
this.getView().getLayout().setActiveItem('grid');
} else if (newValue === 'events') {
this.getGrid().reconfigure('ReportEvents', this.eventsColumns);
this.getView().getLayout().setActiveItem('grid');
} else if (newValue === 'summary') {
this.getGrid().reconfigure('ReportSummary', this.summaryColumns);
this.getView().getLayout().setActiveItem('grid');
} else if (newValue === 'trips') {
this.getGrid().reconfigure('ReportTrips', this.tripsColumns);
this.getView().getLayout().setActiveItem('grid');
} else if (newValue === 'stops') {
this.getGrid().reconfigure('ReportStops', this.stopsColumns);
this.getView().getLayout().setActiveItem('grid');
} else if (newValue === 'chart') {
this.getView().getLayout().setActiveItem('chart');
}
this.updateButtons();
},
onGeocode: function (positionId) {
var position = Ext.getStore('ReportRoute').getById(positionId);
if (position && !position.get('address')) {
Ext.Ajax.request({
scope: this,
method: 'GET',
url: 'api/server/geocode',
params: {
latitude: position.get('latitude'),
longitude: position.get('longitude')
},
success: function (response) {
position.set('address', response.responseText);
position.commit();
this.fireEvent('selectReport', position);
},
failure: function (response) {
Traccar.app.showError(response);
}
});
}
},
routeColumns: [{
text: Strings.reportDeviceName,
dataIndex: 'deviceId',
renderer: Traccar.AttributeFormatter.getFormatter('deviceId')
}, {
text: Strings.positionValid,
dataIndex: 'valid',
renderer: Traccar.AttributeFormatter.getFormatter('valid')
}, {
text: Strings.positionFixTime,
dataIndex: 'fixTime',
xtype: 'datecolumn',
renderer: Traccar.AttributeFormatter.getFormatter('fixTime')
}, {
text: Strings.positionLatitude,
dataIndex: 'latitude',
renderer: Traccar.AttributeFormatter.getFormatter('latitude')
}, {
text: Strings.positionLongitude,
dataIndex: 'longitude',
renderer: Traccar.AttributeFormatter.getFormatter('longitude')
}, {
text: Strings.positionAltitude,
dataIndex: 'altitude',
renderer: Traccar.AttributeFormatter.getFormatter('altitude')
}, {
text: Strings.positionSpeed,
dataIndex: 'speed',
renderer: Traccar.AttributeFormatter.getFormatter('speed')
}, {
text: Strings.positionAddress,
dataIndex: 'address',
renderer: function (value, metaData, record) {
if (!value) {
return '' +
Strings.sharedShowAddress + '';
}
return Traccar.AttributeFormatter.getFormatter('address')(value);
}
}],
eventsColumns: [{
text: Strings.positionFixTime,
dataIndex: 'serverTime',
xtype: 'datecolumn',
renderer: Traccar.AttributeFormatter.getFormatter('serverTime')
}, {
text: Strings.reportDeviceName,
dataIndex: 'deviceId',
renderer: Traccar.AttributeFormatter.getFormatter('deviceId')
}, {
text: Strings.sharedType,
dataIndex: 'type',
renderer: function (value) {
return Traccar.app.getEventString(value);
}
}, {
text: Strings.sharedGeofence,
dataIndex: 'geofenceId',
renderer: Traccar.AttributeFormatter.getFormatter('geofenceId')
}, {
text: Strings.sharedMaintenance,
dataIndex: 'maintenanceId',
renderer: Traccar.AttributeFormatter.getFormatter('maintenanceId')
}],
summaryColumns: [{
text: Strings.reportDeviceName,
dataIndex: 'deviceId',
renderer: Traccar.AttributeFormatter.getFormatter('deviceId')
}, {
text: Strings.sharedDistance,
dataIndex: 'distance',
renderer: Traccar.AttributeFormatter.getFormatter('distance')
}, {
text: Strings.reportStartOdometer,
dataIndex: 'startOdometer',
renderer: Traccar.AttributeFormatter.getFormatter('distance')
}, {
text: Strings.reportEndOdometer,
dataIndex: 'endOdometer',
renderer: Traccar.AttributeFormatter.getFormatter('distance')
}, {
text: Strings.reportAverageSpeed,
dataIndex: 'averageSpeed',
renderer: Traccar.AttributeFormatter.getFormatter('speed')
}, {
text: Strings.reportMaximumSpeed,
dataIndex: 'maxSpeed',
renderer: Traccar.AttributeFormatter.getFormatter('speed')
}, {
text: Strings.reportEngineHours,
dataIndex: 'engineHours',
renderer: Traccar.AttributeFormatter.getFormatter('duration')
}, {
text: Strings.reportSpentFuel,
dataIndex: 'spentFuel',
renderer: Traccar.AttributeFormatter.getFormatter('spentFuel')
}],
tripsColumns: [{
text: Strings.reportDeviceName,
dataIndex: 'deviceId',
renderer: Traccar.AttributeFormatter.getFormatter('deviceId')
}, {
text: Strings.reportStartTime,
dataIndex: 'startTime',
xtype: 'datecolumn',
renderer: Traccar.AttributeFormatter.getFormatter('startTime')
}, {
text: Strings.reportStartOdometer,
dataIndex: 'startOdometer',
renderer: Traccar.AttributeFormatter.getFormatter('distance')
}, {
text: Strings.reportStartAddress,
dataIndex: 'startAddress',
renderer: Traccar.AttributeFormatter.getFormatter('address')
}, {
text: Strings.reportEndTime,
dataIndex: 'endTime',
xtype: 'datecolumn',
renderer: Traccar.AttributeFormatter.getFormatter('endTime')
}, {
text: Strings.reportEndOdometer,
dataIndex: 'endOdometer',
renderer: Traccar.AttributeFormatter.getFormatter('distance')
}, {
text: Strings.reportEndAddress,
dataIndex: 'endAddress',
renderer: Traccar.AttributeFormatter.getFormatter('address')
}, {
text: Strings.sharedDistance,
dataIndex: 'distance',
renderer: Traccar.AttributeFormatter.getFormatter('distance')
}, {
text: Strings.reportAverageSpeed,
dataIndex: 'averageSpeed',
renderer: Traccar.AttributeFormatter.getFormatter('speed')
}, {
text: Strings.reportMaximumSpeed,
dataIndex: 'maxSpeed',
renderer: Traccar.AttributeFormatter.getFormatter('speed')
}, {
text: Strings.reportDuration,
dataIndex: 'duration',
renderer: Traccar.AttributeFormatter.getFormatter('duration')
}, {
text: Strings.reportSpentFuel,
dataIndex: 'spentFuel',
renderer: Traccar.AttributeFormatter.getFormatter('spentFuel')
}, {
text: Strings.sharedDriver,
dataIndex: 'driverUniqueId',
renderer: Traccar.AttributeFormatter.getFormatter('driverUniqueId')
}],
stopsColumns: [{
text: Strings.reportDeviceName,
dataIndex: 'deviceId',
renderer: Traccar.AttributeFormatter.getFormatter('deviceId')
}, {
text: Strings.reportStartTime,
dataIndex: 'startTime',
xtype: 'datecolumn',
renderer: Traccar.AttributeFormatter.getFormatter('startTime')
}, {
text: Strings.positionOdometer,
dataIndex: 'startOdometer',
renderer: Traccar.AttributeFormatter.getFormatter('distance')
}, {
text: Strings.positionAddress,
dataIndex: 'address',
renderer: Traccar.AttributeFormatter.getFormatter('address')
}, {
text: Strings.reportEndTime,
dataIndex: 'endTime',
xtype: 'datecolumn',
renderer: Traccar.AttributeFormatter.getFormatter('endTime')
}, {
text: Strings.reportDuration,
dataIndex: 'duration',
renderer: Traccar.AttributeFormatter.getFormatter('duration')
}, {
text: Strings.reportEngineHours,
dataIndex: 'engineHours',
renderer: Traccar.AttributeFormatter.getFormatter('duration')
}, {
text: Strings.reportSpentFuel,
dataIndex: 'spentFuel',
renderer: Traccar.AttributeFormatter.getFormatter('spentFuel')
}]
});