19 package org.sleuthkit.autopsy.healthmonitor;
 
   21 import java.awt.Color;
 
   22 import java.awt.FontMetrics;
 
   23 import java.awt.Graphics;
 
   24 import java.awt.Graphics2D;
 
   25 import java.awt.RenderingHints;
 
   26 import java.util.Collections;
 
   27 import java.util.Comparator;
 
   28 import java.util.ArrayList;
 
   29 import java.util.List;
 
   32 import java.util.HashMap;
 
   33 import java.util.HashSet;
 
   34 import java.util.TreeSet;
 
   35 import java.util.Calendar;
 
   36 import java.util.GregorianCalendar;
 
   37 import javax.swing.JPanel;
 
   38 import java.util.TimeZone;
 
   39 import java.util.concurrent.TimeUnit;
 
   40 import org.openide.util.NbBundle;
 
   46 class UserMetricGraphPanel 
extends JPanel {
 
   48     private static final int padding = 25;
 
   49     private static final int labelPadding = 25;
 
   50     private final Color examinerColor = 
new Color(0x12, 0x20, 0xdb, 255);
 
   51     private final Color autoIngestColor = 
new Color(0x12, 0x80, 0x20, 255);
 
   52     private final Color gridColor = 
new Color(200, 200, 200, 200);
 
   53     private static final int pointWidth = 4;
 
   54     private static final int numberYDivisions = 10;
 
   55     private final List<UserCount> dataToPlot;
 
   56     private final String graphLabel;
 
   57     private final long dataInterval;
 
   58     private final long MILLISECONDS_PER_HOUR = 1000 * 60 * 60;
 
   59     private final long MILLISECONDS_PER_DAY = MILLISECONDS_PER_HOUR * 24;
 
   60     private final long maxTimestamp;
 
   61     private final long minTimestamp;
 
   63     private static final int minCount = 0; 
 
   65     @NbBundle.Messages({
"UserMetricGraphPanel.constructor.casesOpen=Cases open",
 
   66                     "UserMetricGraphPanel.constructor.loggedIn=Users logged in - examiner nodes in blue, auto ingest nodes in green" 
   68     UserMetricGraphPanel(List<UserData> userResults, 
long timestampThreshold, 
boolean plotCases) {
 
   70         maxTimestamp = System.currentTimeMillis();
 
   71         minTimestamp = timestampThreshold;
 
   75             graphLabel = Bundle.UserMetricGraphPanel_constructor_casesOpen();
 
   77             graphLabel = Bundle.UserMetricGraphPanel_constructor_loggedIn();
 
   81         Comparator<UserData> sortOnTimestamp = 
new Comparator<UserData>() {
 
   83             public int compare(UserData o1, UserData o2) {
 
   84                 return Long.compare(o1.getTimestamp(), o2.getTimestamp());
 
   90         Map<String, TreeSet<UserData>> userDataMap = 
new HashMap<>();
 
   91         for(UserData result:userResults) {
 
   92             if(userDataMap.containsKey(result.getHostname())) {
 
   93                 userDataMap.get(result.getHostname()).add(result);
 
   95                 TreeSet<UserData> resultTreeSet = 
new TreeSet<>(sortOnTimestamp);
 
   96                 resultTreeSet.add(result);
 
   97                 userDataMap.put(result.getHostname(), resultTreeSet);
 
  107         dataToPlot = 
new ArrayList<>();
 
  108         dataInterval = MILLISECONDS_PER_HOUR;
 
  109         maxCount = Integer.MIN_VALUE;
 
  110         for (
long timestamp = maxTimestamp;timestamp > minTimestamp;timestamp -= dataInterval) {
 
  114             UserCount openCaseCount = 
new UserCount(timestamp);
 
  115             UserCount loggedInUserCount = 
new UserCount(timestamp);
 
  117             Set<String> openCaseNames = 
new HashSet<>();
 
  118             UserData timestampUserData = UserData.createDummyUserData(timestamp);
 
  120             for (String hostname:userDataMap.keySet()) {
 
  122                 UserData lastRecord = userDataMap.get(hostname).floor(timestampUserData);
 
  124                 if (lastRecord != null) {
 
  127                     if (lastRecord.getEventType().caseIsOpen()) {
 
  130                         if ( ! openCaseNames.contains(lastRecord.getCaseName())) {
 
  135                             openCaseCount.addExaminer();
 
  136                             openCaseNames.add(lastRecord.getCaseName());
 
  141                     if (lastRecord.getEventType().userIsLoggedIn()) {
 
  142                         if(lastRecord.isExaminerNode()) {
 
  143                             loggedInUserCount.addExaminer();
 
  145                             loggedInUserCount.addAutoIngestNode();
 
  154             maxCount = Integer.max(maxCount, openCaseCount.getTotalNodeCount());
 
  155             maxCount = Integer.max(maxCount, loggedInUserCount.getTotalNodeCount());
 
  159                 dataToPlot.add(openCaseCount);
 
  161                 dataToPlot.add(loggedInUserCount);
 
  178     protected void paintComponent(Graphics g) {
 
  179         super.paintComponent(g);
 
  180         Graphics2D g2 = (Graphics2D) g;
 
  181         g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
 
  185         double maxValueOnXAxis = maxTimestamp + TimeUnit.HOURS.toMillis(2); 
 
  186         double minValueOnXAxis = minTimestamp - TimeUnit.HOURS.toMillis(1); 
 
  190         if((maxCount % 5) != 0) {
 
  191             maxCount += (5 - (maxCount % 5));
 
  193         int maxValueOnYAxis = Integer.max(maxCount, 5);
 
  194         int minValueOnYAxis = minCount;
 
  201         int leftGraphPadding = padding + labelPadding;
 
  202         int rightGraphPadding = padding;
 
  203         int topGraphPadding = padding + g2.getFontMetrics().getHeight();
 
  204         int bottomGraphPadding = labelPadding;
 
  214         int graphWidth = getWidth() - leftGraphPadding - rightGraphPadding;
 
  215         int graphHeight = getHeight() - topGraphPadding - bottomGraphPadding;
 
  216         double xScale = ((double) graphWidth) / (maxValueOnXAxis - minValueOnXAxis);
 
  217         double yScale = ((double) graphHeight) / (maxValueOnYAxis - minValueOnYAxis);  
 
  220         g2.setColor(Color.WHITE);
 
  221         g2.fillRect(leftGraphPadding, topGraphPadding, graphWidth, graphHeight); 
 
  225         int positionForMetricNameLabel = 0;
 
  226         Map<Integer, Integer> countToGraphPosition = 
new HashMap<>();
 
  227         for (
int i = 0; i < numberYDivisions + 1; i++) {
 
  228             int x0 = leftGraphPadding;
 
  229             int x1 = pointWidth + leftGraphPadding;
 
  230             int y0 = getHeight() - ((i * graphHeight) / numberYDivisions + bottomGraphPadding);
 
  233             if ( ! dataToPlot.isEmpty()) {
 
  235                 g2.setColor(gridColor);
 
  236                 g2.drawLine(leftGraphPadding + 1 + pointWidth, y0, getWidth() - rightGraphPadding, y1);
 
  239                 g2.setColor(Color.BLACK);
 
  240                 double yValue = minValueOnYAxis + ((maxValueOnYAxis - minValueOnYAxis) * ((i * 1.0) / numberYDivisions));
 
  241                 int intermediateLabelVal = (int) (yValue * 100);
 
  242                 if ((i == numberYDivisions) || ((intermediateLabelVal % 100) == 0)) {
 
  243                     countToGraphPosition.put(intermediateLabelVal / 100, y0);
 
  244                     String yLabel = Integer.toString(intermediateLabelVal / 100);
 
  245                     FontMetrics fontMetrics = g2.getFontMetrics();
 
  246                     labelWidth = fontMetrics.stringWidth(yLabel);
 
  247                     g2.drawString(yLabel, x0 - labelWidth - 5, y0 + (fontMetrics.getHeight() / 2) - 3);
 
  251                     if (i == numberYDivisions) {
 
  252                         positionForMetricNameLabel = x0 - labelWidth - 5;
 
  258             g2.setColor(Color.BLACK);
 
  259             g2.drawLine(x0, y0, x1, y1);
 
  263         Calendar maxDate = 
new GregorianCalendar();
 
  264         maxDate.setTimeInMillis(maxTimestamp);
 
  265         maxDate.set(Calendar.HOUR_OF_DAY, 0);
 
  266         maxDate.set(Calendar.MINUTE, 0);
 
  267         maxDate.set(Calendar.SECOND, 0);
 
  268         maxDate.set(Calendar.MILLISECOND, 0);
 
  269         long maxMidnightInMillis = maxDate.getTimeInMillis();
 
  273         long totalDays = (maxMidnightInMillis - (long)minValueOnXAxis) / MILLISECONDS_PER_DAY;
 
  274         long daysPerDivision;
 
  275         if(totalDays <= 20) {
 
  278             daysPerDivision = (totalDays / 20);
 
  279             if((totalDays % 20) != 0) {
 
  287         for (
long currentDivision = maxMidnightInMillis; currentDivision >= minValueOnXAxis; currentDivision -= MILLISECONDS_PER_DAY * daysPerDivision) {
 
  289             int x0 = (int) ((currentDivision - minValueOnXAxis) * xScale + leftGraphPadding);
 
  291             int y0 = getHeight() - bottomGraphPadding;
 
  292             int y1 = y0 - pointWidth;
 
  295             g2.setColor(gridColor);
 
  296             g2.drawLine(x0, getHeight() - bottomGraphPadding - 1 - pointWidth, x1, topGraphPadding);
 
  299             g2.setColor(Color.BLACK);
 
  300             g2.drawLine(x0, y0, x1, y1);
 
  303             Calendar thisDate = 
new GregorianCalendar();
 
  304             thisDate.setTimeZone(TimeZone.getTimeZone(
"GMT")); 
 
  305             thisDate.setTimeInMillis(currentDivision);
 
  306             int month = thisDate.get(Calendar.MONTH) + 1;
 
  307             int day = thisDate.get(Calendar.DAY_OF_MONTH);
 
  309             String xLabel = month + 
"/" + day;
 
  310             FontMetrics metrics = g2.getFontMetrics();
 
  311             labelWidth = metrics.stringWidth(xLabel);
 
  312             g2.drawString(xLabel, x0 - labelWidth / 2, y0 + metrics.getHeight() + 3);           
 
  316         g2.setColor(Color.BLACK);
 
  317         g2.drawLine(leftGraphPadding, getHeight() - bottomGraphPadding, leftGraphPadding, topGraphPadding);
 
  318         g2.drawLine(leftGraphPadding, getHeight() - bottomGraphPadding, getWidth() - rightGraphPadding, getHeight() - bottomGraphPadding);
 
  321         Collections.sort(dataToPlot, 
new Comparator<UserCount>(){
 
  323             public int compare(UserCount o1, UserCount o2){
 
  324                 return Long.compare(o1.getTimestamp(), o2.getTimestamp());
 
  329         for(
int i = 0;i < dataToPlot.size();i++) {
 
  330             UserCount userCount = dataToPlot.get(i);
 
  331             int x = (int) ((userCount.getTimestamp() - minValueOnXAxis) * xScale + leftGraphPadding);
 
  332             int yTopOfExaminerBox;
 
  333             if(countToGraphPosition.containsKey(userCount.getTotalNodeCount())) {
 
  336                 yTopOfExaminerBox = countToGraphPosition.get(userCount.getTotalNodeCount());
 
  338                 yTopOfExaminerBox = (int) ((maxValueOnYAxis - userCount.getTotalNodeCount()) * yScale + topGraphPadding);
 
  344             if(i < dataToPlot.size() - 1) {
 
  345                 width = Integer.max((
int)((dataToPlot.get(i + 1).getTimestamp() - minValueOnXAxis) * xScale + leftGraphPadding) - x - 1,
 
  348                 width = Integer.max((
int)(dataInterval * xScale), 1);
 
  354             int heightExaminerBox = (getHeight() - bottomGraphPadding) - yTopOfExaminerBox;
 
  357             g2.setColor(examinerColor);
 
  358             g2.fillRect(x, yTopOfExaminerBox, width, heightExaminerBox);
 
  362             if (userCount.getAutoIngestNodeCount() > 0) {
 
  363                 int yTopOfAutoIngestBox;
 
  364                 if(countToGraphPosition.containsKey(userCount.getAutoIngestNodeCount())) {
 
  367                     yTopOfAutoIngestBox =countToGraphPosition.get(userCount.getAutoIngestNodeCount());
 
  369                     yTopOfAutoIngestBox = yTopOfExaminerBox + heightExaminerBox;
 
  371                 int heightAutoIngestBox = (getHeight() - bottomGraphPadding) - yTopOfAutoIngestBox;
 
  374                 g2.setColor(autoIngestColor);
 
  375                 g2.fillRect(x, yTopOfAutoIngestBox, width, heightAutoIngestBox);
 
  381         g2.setColor(this.getBackground());
 
  382         g2.fillRect(leftGraphPadding, 0, graphWidth, topGraphPadding); 
 
  385         g2.setColor(Color.BLACK);
 
  386         String titleStr = graphLabel;
 
  387         g2.drawString(titleStr, positionForMetricNameLabel, padding);
 
  405             this.examinerCount = 0;
 
  406             this.autoIngestCount = 0;
 
  419         void addAutoIngestNode() {
 
  427         int getExaminerNodeCount() {
 
  435         int getAutoIngestNodeCount() {
 
  443         int getTotalNodeCount() {
 
  451         long getTimestamp() {