Skip to content

Commit c613143

Browse files
committed
- Implement export reports to excel
- Migrate csv export to /api/positions
1 parent 83ca047 commit c613143

13 files changed

+387
-60
lines changed

pom.xml

+10
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,16 @@
117117
<artifactId>mail</artifactId>
118118
<version>1.4.7</version>
119119
</dependency>
120+
<dependency>
121+
<groupId>org.jxls</groupId>
122+
<artifactId>jxls</artifactId>
123+
<version>2.3.0</version>
124+
</dependency>
125+
<dependency>
126+
<groupId>org.jxls</groupId>
127+
<artifactId>jxls-poi</artifactId>
128+
<version>1.0.11</version>
129+
</dependency>
120130

121131
</dependencies>
122132

src/org/traccar/api/resource/PositionResource.java

+22-2
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,31 @@
1818
import org.traccar.Context;
1919
import org.traccar.api.BaseResource;
2020
import org.traccar.model.Position;
21+
import org.traccar.web.CsvBuilder;
2122
import org.traccar.web.JsonConverter;
2223

2324
import javax.ws.rs.Consumes;
2425
import javax.ws.rs.GET;
2526
import javax.ws.rs.Path;
2627
import javax.ws.rs.Produces;
2728
import javax.ws.rs.QueryParam;
29+
import javax.ws.rs.core.HttpHeaders;
2830
import javax.ws.rs.core.MediaType;
31+
import javax.ws.rs.core.Response;
32+
2933
import java.sql.SQLException;
3034
import java.util.Collection;
3135

3236
@Path("positions")
33-
@Produces(MediaType.APPLICATION_JSON)
3437
@Consumes(MediaType.APPLICATION_JSON)
3538
public class PositionResource extends BaseResource {
3639

40+
public static final String TEXT_CSV = "text/csv";
41+
public static final String CONTENT_DISPOSITION_VALUE_CSV = "attachment; filename=positions.csv";
42+
3743
@GET
38-
public Collection<Position> get(
44+
@Produces(MediaType.APPLICATION_JSON)
45+
public Collection<Position> getJson(
3946
@QueryParam("deviceId") long deviceId, @QueryParam("from") String from, @QueryParam("to") String to)
4047
throws SQLException {
4148
if (deviceId == 0) {
@@ -47,4 +54,17 @@ public Collection<Position> get(
4754
}
4855
}
4956

57+
@GET
58+
@Produces(TEXT_CSV)
59+
public Response getCsv(
60+
@QueryParam("deviceId") long deviceId, @QueryParam("from") String from, @QueryParam("to") String to)
61+
throws SQLException {
62+
Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
63+
CsvBuilder csv = new CsvBuilder();
64+
csv.addHeaderLine(new Position());
65+
csv.addArray(Context.getDataManager().getPositions(
66+
deviceId, JsonConverter.parseDate(from), JsonConverter.parseDate(to)));
67+
return Response.ok(csv.build()).header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_CSV).build();
68+
}
69+
5070
}

src/org/traccar/api/resource/ReportResource.java

+40-30
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.traccar.api.resource;
22

3+
import java.io.ByteArrayOutputStream;
4+
import java.io.IOException;
35
import java.sql.SQLException;
46
import java.util.List;
57

@@ -23,8 +25,8 @@
2325
@Consumes(MediaType.APPLICATION_JSON)
2426
public class ReportResource extends BaseResource {
2527

26-
public static final String TEXT_CSV = "text/csv";
27-
public static final String CONTENT_DISPOSITION_VALUE = "attachment; filename=report.csv";
28+
private static final String XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
29+
private static final String CONTENT_DISPOSITION_VALUE_XLSX = "attachment; filename=report.xlsx";
2830

2931
@Path("route")
3032
@GET
@@ -38,14 +40,16 @@ public Response getRouteJson(
3840

3941
@Path("route")
4042
@GET
41-
@Produces(TEXT_CSV)
42-
public Response getRouteCsv(
43+
@Produces(XLSX)
44+
public Response getRouteExcel(
4345
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
44-
@QueryParam("from") String from, @QueryParam("to") String to) throws SQLException {
45-
return Response.ok(Route.getCsv(getUserId(), deviceIds, groupIds,
46-
JsonConverter.parseDate(from), JsonConverter.parseDate(to)))
47-
.header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE)
48-
.build();
46+
@QueryParam("from") String from, @QueryParam("to") String to) throws SQLException, IOException {
47+
ByteArrayOutputStream stream = new ByteArrayOutputStream();
48+
Route.getExcel(stream, getUserId(), deviceIds, groupIds,
49+
JsonConverter.parseDate(from), JsonConverter.parseDate(to));
50+
51+
return Response.ok(stream.toByteArray())
52+
.header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_XLSX).build();
4953
}
5054

5155
@Path("events")
@@ -61,15 +65,17 @@ public Response getEventsJson(
6165

6266
@Path("events")
6367
@GET
64-
@Produces(TEXT_CSV)
65-
public Response getEventsCsv(
68+
@Produces(XLSX)
69+
public Response getEventsExcel(
6670
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
6771
@QueryParam("type") final List<String> types,
68-
@QueryParam("from") String from, @QueryParam("to") String to) throws SQLException {
69-
return Response.ok(Events.getCsv(getUserId(), deviceIds, groupIds,
70-
types, JsonConverter.parseDate(from), JsonConverter.parseDate(to)))
71-
.header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE)
72-
.build();
72+
@QueryParam("from") String from, @QueryParam("to") String to) throws SQLException, IOException {
73+
ByteArrayOutputStream stream = new ByteArrayOutputStream();
74+
Events.getExcel(stream, getUserId(), deviceIds, groupIds, types,
75+
JsonConverter.parseDate(from), JsonConverter.parseDate(to));
76+
77+
return Response.ok(stream.toByteArray())
78+
.header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_XLSX).build();
7379
}
7480

7581
@Path("summary")
@@ -84,14 +90,16 @@ public Response getSummaryJson(
8490

8591
@Path("summary")
8692
@GET
87-
@Produces(TEXT_CSV)
88-
public Response getSummaryCsv(
93+
@Produces(XLSX)
94+
public Response getSummaryExcel(
8995
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
90-
@QueryParam("from") String from, @QueryParam("to") String to) throws SQLException {
91-
return Response.ok(Summary.getCsv(getUserId(), deviceIds, groupIds,
92-
JsonConverter.parseDate(from), JsonConverter.parseDate(to)))
93-
.header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE)
94-
.build();
96+
@QueryParam("from") String from, @QueryParam("to") String to) throws SQLException, IOException {
97+
ByteArrayOutputStream stream = new ByteArrayOutputStream();
98+
Summary.getExcel(stream, getUserId(), deviceIds, groupIds,
99+
JsonConverter.parseDate(from), JsonConverter.parseDate(to));
100+
101+
return Response.ok(stream.toByteArray())
102+
.header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_XLSX).build();
95103
}
96104

97105
@Path("trips")
@@ -106,14 +114,16 @@ public Response getTripsJson(
106114

107115
@Path("trips")
108116
@GET
109-
@Produces(TEXT_CSV)
110-
public Response getTripsCsv(
117+
@Produces(XLSX)
118+
public Response getTripsExcel(
111119
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
112-
@QueryParam("from") String from, @QueryParam("to") String to) throws SQLException {
113-
return Response.ok(Trips.getCsv(getUserId(), deviceIds, groupIds,
114-
JsonConverter.parseDate(from), JsonConverter.parseDate(to)))
115-
.header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE)
116-
.build();
120+
@QueryParam("from") String from, @QueryParam("to") String to) throws SQLException, IOException {
121+
ByteArrayOutputStream stream = new ByteArrayOutputStream();
122+
Trips.getExcel(stream, getUserId(), deviceIds, groupIds,
123+
JsonConverter.parseDate(from), JsonConverter.parseDate(to));
124+
125+
return Response.ok(stream.toByteArray())
126+
.header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_XLSX).build();
117127
}
118128

119129
}

src/org/traccar/reports/Events.java

+80-7
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,36 @@
1616
*/
1717
package org.traccar.reports;
1818

19+
import java.io.FileInputStream;
20+
import java.io.IOException;
21+
import java.io.InputStream;
22+
import java.io.OutputStream;
1923
import java.sql.SQLException;
24+
import java.util.ArrayList;
2025
import java.util.Collection;
26+
import java.util.Comparator;
2127
import java.util.Date;
28+
import java.util.HashMap;
29+
import java.util.List;
30+
import java.util.SortedSet;
31+
import java.util.TreeSet;
2232

2333
import javax.json.Json;
2434
import javax.json.JsonArrayBuilder;
2535

36+
import org.jxls.area.Area;
37+
import org.jxls.builder.xls.XlsCommentAreaBuilder;
38+
import org.jxls.common.CellRef;
39+
import org.jxls.formula.StandardFormulaProcessor;
40+
import org.jxls.transform.Transformer;
41+
import org.jxls.transform.poi.PoiTransformer;
42+
import org.jxls.util.TransformerFactory;
2643
import org.traccar.Context;
44+
import org.traccar.model.Device;
2745
import org.traccar.model.Event;
28-
import org.traccar.web.CsvBuilder;
46+
import org.traccar.model.Geofence;
47+
import org.traccar.model.Group;
48+
import org.traccar.reports.model.DeviceReport;
2949
import org.traccar.web.JsonConverter;
3050

3151
public final class Events {
@@ -47,16 +67,69 @@ public static String getJson(long userId, Collection<Long> deviceIds, Collection
4767
return json.build().toString();
4868
}
4969

50-
public static String getCsv(long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
51-
Collection<String> types, Date from, Date to) throws SQLException {
52-
CsvBuilder csv = new CsvBuilder();
53-
csv.addHeaderLine(new Event());
70+
public static void getExcel(OutputStream outputStream,
71+
long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
72+
Collection<String> types, Date from, Date to) throws SQLException, IOException {
73+
ArrayList<DeviceReport> devicesEvents = new ArrayList<>();
74+
ArrayList<String> sheetNames = new ArrayList<>();
75+
HashMap<Long, String> geofenceNames = new HashMap<>();
5476
for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
5577
Context.getPermissionsManager().checkDevice(userId, deviceId);
78+
SortedSet<Event> eventReports = new TreeSet<Event>(new Comparator<Event>() {
79+
@Override
80+
public int compare(Event e1, Event e2) {
81+
return e1.getServerTime().compareTo(e2.getServerTime());
82+
}
83+
});
5684
for (String type : types) {
57-
csv.addArray(Context.getDataManager().getEvents(deviceId, type, from, to));
85+
Collection<Event> events = Context.getDataManager().getEvents(deviceId, type, from, to);
86+
for (Event event : events) {
87+
long geofenceId = event.getGeofenceId();
88+
if (geofenceId != 0) {
89+
Geofence geofence = Context.getGeofenceManager().getGeofence(geofenceId);
90+
if (geofence != null) {
91+
geofenceNames.put(geofenceId, geofence.getName());
92+
} else {
93+
geofenceNames.put(geofenceId, Long.toString(geofenceId));
94+
}
95+
}
96+
eventReports.add(event);
97+
}
98+
}
99+
if (!eventReports.isEmpty()) {
100+
DeviceReport deviceEvents = new DeviceReport();
101+
Device device = Context.getIdentityManager().getDeviceById(deviceId);
102+
deviceEvents.setDeviceName(device.getName());
103+
sheetNames.add(deviceEvents.getDeviceName());
104+
if (device.getGroupId() != 0) {
105+
Group group = Context.getDeviceManager().getGroupById(device.getGroupId());
106+
if (group != null) {
107+
deviceEvents.setGroupName(group.getName());
108+
}
109+
}
110+
deviceEvents.setObjects(eventReports);
111+
devicesEvents.add(deviceEvents);
112+
}
113+
}
114+
String templatePath = Context.getConfig().getString("report.events.template.excel",
115+
"templates/export/events.xlsx");
116+
try (InputStream inputStream = new FileInputStream(templatePath)) {
117+
org.jxls.common.Context jxlsContext = PoiTransformer.createInitialContext();
118+
jxlsContext.putVar("devices", devicesEvents);
119+
jxlsContext.putVar("sheetNames", sheetNames);
120+
jxlsContext.putVar("geofenceNames", geofenceNames);
121+
jxlsContext.putVar("from", from);
122+
jxlsContext.putVar("to", to);
123+
jxlsContext.putVar("bracketsRegex", "[\\{\\}\"]");
124+
Transformer transformer = TransformerFactory.createTransformer(inputStream, outputStream);
125+
List<Area> xlsAreas = new XlsCommentAreaBuilder(transformer).build();
126+
for (Area xlsArea : xlsAreas) {
127+
xlsArea.applyAt(new CellRef(xlsArea.getStartCellRef().getCellName()), jxlsContext);
128+
xlsArea.setFormulaProcessor(new StandardFormulaProcessor());
129+
xlsArea.processFormulas();
58130
}
131+
transformer.deleteSheet(xlsAreas.get(0).getStartCellRef().getSheetName());
132+
transformer.write();
59133
}
60-
return csv.build();
61134
}
62135
}

src/org/traccar/reports/Route.java

+55-7
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,31 @@
1616
*/
1717
package org.traccar.reports;
1818

19+
import java.io.FileInputStream;
20+
import java.io.IOException;
21+
import java.io.InputStream;
22+
import java.io.OutputStream;
1923
import java.sql.SQLException;
24+
import java.util.ArrayList;
2025
import java.util.Collection;
2126
import java.util.Date;
27+
import java.util.List;
2228

2329
import javax.json.Json;
2430
import javax.json.JsonArrayBuilder;
2531

32+
import org.jxls.area.Area;
33+
import org.jxls.builder.xls.XlsCommentAreaBuilder;
34+
import org.jxls.common.CellRef;
35+
import org.jxls.formula.StandardFormulaProcessor;
36+
import org.jxls.transform.Transformer;
37+
import org.jxls.transform.poi.PoiTransformer;
38+
import org.jxls.util.TransformerFactory;
2639
import org.traccar.Context;
40+
import org.traccar.model.Device;
41+
import org.traccar.model.Group;
2742
import org.traccar.model.Position;
28-
import org.traccar.web.CsvBuilder;
43+
import org.traccar.reports.model.DeviceReport;
2944
import org.traccar.web.JsonConverter;
3045

3146
public final class Route {
@@ -45,14 +60,47 @@ public static String getJson(long userId, Collection<Long> deviceIds, Collection
4560
return json.build().toString();
4661
}
4762

48-
public static String getCsv(long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
49-
Date from, Date to) throws SQLException {
50-
CsvBuilder csv = new CsvBuilder();
51-
csv.addHeaderLine(new Position());
63+
public static void getExcel(OutputStream outputStream,
64+
long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
65+
Date from, Date to) throws SQLException, IOException {
66+
ArrayList<DeviceReport> devicesRoutes = new ArrayList<>();
67+
ArrayList<String> sheetNames = new ArrayList<>();
5268
for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
5369
Context.getPermissionsManager().checkDevice(userId, deviceId);
54-
csv.addArray(Context.getDataManager().getPositions(deviceId, from, to));
70+
Collection<Position> positions = Context.getDataManager().getPositions(deviceId, from, to);
71+
if (positions != null && !positions.isEmpty()) {
72+
DeviceReport deviceRoutes = new DeviceReport();
73+
Device device = Context.getIdentityManager().getDeviceById(deviceId);
74+
deviceRoutes.setDeviceName(device.getName());
75+
sheetNames.add(deviceRoutes.getDeviceName());
76+
if (device.getGroupId() != 0) {
77+
Group group = Context.getDeviceManager().getGroupById(device.getGroupId());
78+
if (group != null) {
79+
deviceRoutes.setGroupName(group.getName());
80+
}
81+
}
82+
deviceRoutes.setObjects(positions);
83+
devicesRoutes.add(deviceRoutes);
84+
}
85+
}
86+
String templatePath = Context.getConfig().getString("report.route.template.excel",
87+
"templates/export/route.xlsx");
88+
try (InputStream inputStream = new FileInputStream(templatePath)) {
89+
org.jxls.common.Context jxlsContext = PoiTransformer.createInitialContext();
90+
jxlsContext.putVar("devices", devicesRoutes);
91+
jxlsContext.putVar("sheetNames", sheetNames);
92+
jxlsContext.putVar("from", from);
93+
jxlsContext.putVar("to", to);
94+
jxlsContext.putVar("bracketsRegex", "[\\{\\}\"]");
95+
Transformer transformer = TransformerFactory.createTransformer(inputStream, outputStream);
96+
List<Area> xlsAreas = new XlsCommentAreaBuilder(transformer).build();
97+
for (Area xlsArea : xlsAreas) {
98+
xlsArea.applyAt(new CellRef(xlsArea.getStartCellRef().getCellName()), jxlsContext);
99+
xlsArea.setFormulaProcessor(new StandardFormulaProcessor());
100+
xlsArea.processFormulas();
101+
}
102+
transformer.deleteSheet(xlsAreas.get(0).getStartCellRef().getSheetName());
103+
transformer.write();
55104
}
56-
return csv.build();
57105
}
58106
}

0 commit comments

Comments
 (0)