bar chart included

This commit is contained in:
Greg DiCristofaro 2021-03-19 20:04:51 -04:00
parent 189580df63
commit 27f90e7e1f
3 changed files with 116 additions and 56 deletions

View File

@ -114,6 +114,12 @@ SizeRepresentationUtil_units_megabytes=MB
SizeRepresentationUtil_units_petabytes=PB
SizeRepresentationUtil_units_terabytes=TB
TimelinePanel_earliestLabel_title=Earliest
TimelinePanel_getExports_activityRange=Activity Range
TimelinePanel_getExports_chartName=Last 30 Days
TimelinePanel_getExports_dateColumnHeader=Date
TimelinePanel_getExports_earliest=Earliest:
TimelinePanel_getExports_latest=Latest:
TimelinePanel_getExports_sheetName=Timeline
TimelinePanel_latestLabel_title=Latest
TimlinePanel_last30DaysChart_artifactEvts_title=Result Events
TimlinePanel_last30DaysChart_fileEvts_title=File Events

View File

@ -39,6 +39,7 @@ import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineDataSourceUtils
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.DailyActivityAmount;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.TimelineSummaryData;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel.OrderedKey;
@ -46,7 +47,12 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries.BarChartIt
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.KeyValueItemExportable;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.TitledExportable;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableLabel;
@ -70,7 +76,9 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
private static final Logger logger = Logger.getLogger(TimelinePanel.class.getName());
private static final long serialVersionUID = 1L;
private static final DateFormat EARLIEST_LATEST_FORMAT = getUtcFormat("MMM d, yyyy");
private static final String EARLIEST_LATEST_FORMAT_STR = "MMM d, yyyy";
private static final DateFormat EARLIEST_LATEST_FORMAT = getUtcFormat(EARLIEST_LATEST_FORMAT_STR);
private static final DateFormat CHART_FORMAT = getUtcFormat("MMM d, yyyy");
private static final int MOST_RECENT_DAYS_COUNT = 30;
@ -94,6 +102,8 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
// all loadable components on this tab
private final List<LoadableComponent<?>> loadableComponents = Arrays.asList(earliestLabel, latestLabel, last30DaysChart);
private final DataFetcher<DataSource, TimelineSummaryData> dataFetcher;
// actions to load data for this tab
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
@ -107,12 +117,11 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
public TimelinePanel(TimelineSummary timelineData) {
super(timelineData);
dataFetcher = (dataSource) -> timelineData.getData(dataSource, MOST_RECENT_DAYS_COUNT);
// set up data acquisition methods
dataFetchComponents = Arrays.asList(
new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> timelineData.getData(dataSource, MOST_RECENT_DAYS_COUNT),
(result) -> handleResult(result))
);
new DataFetchWorker.DataFetchComponents<>(dataFetcher, (result) -> handleResult(result)));
initComponents();
}
@ -282,9 +291,34 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
super.close();
}
private static DefaultCellModel<?> getEarliestLatestCell(Date date) {
return new DefaultCellModel<>(date, (dt) -> dt == null ? "" : EARLIEST_LATEST_FORMAT.format(dt), EARLIEST_LATEST_FORMAT_STR);
}
@Messages({
"TimelinePanel_getExports_sheetName=Timeline",
"TimelinePanel_getExports_activityRange=Activity Range",
"TimelinePanel_getExports_earliest=Earliest:",
"TimelinePanel_getExports_latest=Latest:",
"TimelinePanel_getExports_dateColumnHeader=Date",
"TimelinePanel_getExports_chartName=Last 30 Days",})
@Override
List<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
return Collections.emptyList();
TimelineSummaryData summaryData = getFetchResult(dataFetcher, "Timeline", dataSource);
if (summaryData == null) {
return Collections.emptyList();
}
return Arrays.asList(
new ExcelSpecialFormatExport(Bundle.TimelinePanel_getExports_sheetName(),
Arrays.asList(
new TitledExportable(Bundle.TimelinePanel_getExports_activityRange(), Collections.emptyList()),
new KeyValueItemExportable(Bundle.TimelinePanel_getExports_earliest(), getEarliestLatestCell(summaryData.getMinDate())),
new KeyValueItemExportable(Bundle.TimelinePanel_getExports_latest(), getEarliestLatestCell(summaryData.getMaxDate())),
new BarChartExport(Bundle.TimelinePanel_getExports_dateColumnHeader(),
"#,###",
Bundle.TimelinePanel_getExports_chartName(),
parseChartData(summaryData.getMostRecentDaysActivity())))));
}
/**

View File

@ -5,13 +5,13 @@
*/
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
import com.google.cloud.Tuple;
import java.awt.Color;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.poi.ss.usermodel.Sheet;
@ -31,8 +31,6 @@ import org.apache.poi.xddf.usermodel.chart.XDDFChartData;
import org.apache.poi.xddf.usermodel.chart.XDDFChartLegend;
import org.apache.poi.xddf.usermodel.chart.XDDFDataSource;
import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory;
import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource;
import org.apache.poi.xddf.usermodel.chart.XDDFPieChartData;
import org.apache.poi.xddf.usermodel.chart.XDDFValueAxis;
import org.apache.poi.xssf.usermodel.XSSFChart;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
@ -48,7 +46,8 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.
* @author gregd
*/
public class BarChartExport implements ExcelItemExportable, ExcelSheetExport {
private final ExcelTableExport<BarChartSeries, ? extends ExcelCellModel> tableExport;
private final ExcelTableExport<Pair<Object, List<Double>>, ? extends ExcelCellModel> tableExport;
private final int colOffset;
private final int rowPadding;
private final int colSize;
@ -72,33 +71,53 @@ public class BarChartExport implements ExcelItemExportable, ExcelSheetExport {
this.keyColumnHeader = keyColumnHeader;
List<BarChartSeries> categoryKeys = categories.stream()
.filter(cat -> cat != null && cat.getKey() != null)
.sorted((c1, c2) -> c1.getKey().compareTo(c2.getKey()))
.collect(Collectors.toList());
List<Comparable<?>> rowKeys = categories.stream()
List<? extends Object> rowKeys = categories.stream()
.filter(cat -> cat != null && cat.getItems() != null)
.flatMap(cat -> cat.getItems().stream().map(item -> item.getKey()))
.filter(i -> i != null)
.distinct()
.sorted()
.map(cat -> cat.getItems())
.max((items1, items2) -> Integer.compare(items1.size(), items2.size()))
.orElse(Collections.emptyList())
.stream()
.map((barChartItem) -> barChartItem.getKey())
.collect(Collectors.toList());
Map<Tuple<Comparable<?>, Comparable<?>>, Double> valueMap = categories.stream()
.flatMap((cat) -> cat.getItems().stream().map((item) -> Tuple.of(Tuple.of(cat.getKey(), item.getKey()), item.getValue())))
// map of (category, item) -> value
Map<Pair<Integer, Integer>, Double> valueMap = IntStream.range(0, categories.size())
.mapToObj(idx -> Pair.of(idx, categories.get(idx)))
.filter(pair -> pair.getValue() != null && pair.getValue().getItems() != null)
.flatMap(categoryPair -> {
return IntStream.range(0, categoryPair.getValue().getItems().size())
.mapToObj(idx -> Pair.of(idx, categoryPair.getValue().getItems().get(idx)))
.map(itemPair -> Pair.of(
Pair.of(categoryPair.getKey(), itemPair.getKey()),
itemPair.getValue() == null ? null : itemPair.getValue().getValue()));
})
.collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue(), (v1, v2) -> v1));
List<Pair<Comparable<?>, List<Double>>> values = rowKeys.stream()
.map((rowValue))
List<Pair<Object, List<Double>>> values = IntStream.range(0, rowKeys.size())
.mapToObj(idx -> Pair.of(idx, rowKeys.get(idx)))
.map((rowPair) -> {
List<Double> items = IntStream.range(0, categories.size())
.mapToObj(idx -> valueMap.get(Pair.of(idx, rowPair.getKey())))
.collect(Collectors.toList());
return Pair.of(rowPair.getValue(), items);
})
.collect(Collectors.toList());
ColumnModel<Pair<Object, List<Double>>, DefaultCellModel> categoryColumn = new ColumnModel<>(keyColumnHeader, (row) -> new DefaultCellModel<>(row.getKey()));
Stream<ColumnModel<Pair<Object, List<Double>>, DefaultCellModel>> dataColumns = IntStream.range(0, categories.size())
.mapToObj(idx -> new ColumnModel<>(
categories.get(idx).getKey().toString(),
(row) -> new DefaultCellModel<>(row.getValue().get(idx))));
this.tableExport = new ExcelTableExport<Pair<Object, List<Double>>, DefaultCellModel>(
chartTitle,
Stream.concat(Stream.of(categoryColumn), dataColumns)
.collect(Collectors.toList()),
values
);
this.tableExport = new ExcelTableExport<>(chartTitle,
Stream.concat(Stream.of(new ColumnModel<>(keyColumnHeader, (category) -> new DefaultCellModel<>(category.getLabel()))), )
Arrays.asList(
,
new ColumnModel<>(valueColumnHeader, (category) -> new DefaultCellModel<>(category.getValue(), null, valueFormatString))
),
categories);
this.colOffset = colOffset;
this.rowPadding = rowPadding;
this.colSize = colSize;
@ -108,8 +127,6 @@ public class BarChartExport implements ExcelItemExportable, ExcelSheetExport {
this.categories = categories;
}
@Override
public String getSheetName() {
return sheetName;
@ -133,7 +150,7 @@ public class BarChartExport implements ExcelItemExportable, ExcelSheetExport {
XSSFDrawing drawing = xssfSheet.createDrawingPatriarch();
int chartColStart = colStart + 2 + colOffset;
int chartColStart = colStart + categories.size() + 1 + colOffset;
//createAnchor(int dx1, int dy1, int dx2, int dy2, int col1, int row1, int col2, int row2);
XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, chartColStart, rowStart + rowPadding, chartColStart + colSize, rowStart + rowSize);
@ -144,7 +161,6 @@ public class BarChartExport implements ExcelItemExportable, ExcelSheetExport {
XDDFChartLegend legend = chart.getOrAddLegend();
legend.setPosition(LegendPosition.BOTTOM);
// Use a category axis for the bottom axis.
XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
bottomAxis.setTitle(keyColumnHeader);
@ -156,13 +172,19 @@ public class BarChartExport implements ExcelItemExportable, ExcelSheetExport {
XDDFBarChartData data = (XDDFBarChartData) chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
data.setBarGrouping(BarGrouping.STACKED);
XDDFDataSource<String> headerSource = XDDFDataSourcesFactory.fromStringCellRange(sheet, new CellRangeAddress(1, keyVals.size(), 0, 0));
XDDFDataSource<String> headerSource = XDDFDataSourcesFactory.fromStringCellRange(xssfSheet,
new CellRangeAddress(tableDimensions.getRowStart() + 1, tableDimensions.getRowEnd(),
tableDimensions.getColStart(), tableDimensions.getColStart()));
data.setBarDirection(BarDirection.COL);
for (int i = 0; i < categories.size(); i++) {
XDDFChartData.Series series = data.addSeries(headerSource,
XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(1, keyVals.size(), i + 1, i + 1)));
series.setTitle(categories.size() > i && categories.get(i).getIdentifier() != null ? categories.get(i).getIdentifier() : "", null);
XDDFDataSourcesFactory.fromNumericCellRange(xssfSheet,
new CellRangeAddress(tableDimensions.getRowStart() + 1, tableDimensions.getRowEnd(),
tableDimensions.getColStart() + 1 + i, tableDimensions.getColStart() + 1 + i)));
series.setTitle(categories.size() > i && categories.get(i).getKey() != null ? categories.get(i).getKey().toString() : "", null);
if (categories.get(i).getColor() != null) {
Color color = categories.get(i).getColor();
byte[] colorArrARGB = ByteBuffer.allocate(4).putInt(color.getRGB()).array();
@ -182,6 +204,4 @@ public class BarChartExport implements ExcelItemExportable, ExcelSheetExport {
return new ItemDimensions(rowStart, colStart, Math.max(tableDimensions.getRowEnd(), rowStart + rowSize) + rowPadding, chartColStart + colSize);
}
}