Fix webserver being unable to respond due to open socket -> endless inputstream.

(Introduced in 7e03c1dcc0)

Fix #179

Attempts to use scatter graph for inspect page, code still needs refining.
This commit is contained in:
Rsl1122 2017-07-25 11:27:08 +03:00
parent 7a4edcac03
commit 3769c303fb
9 changed files with 894 additions and 892 deletions

View File

@ -1,47 +1,29 @@
package main.java.com.djrapitops.plan.ui.html.graphs;
import main.java.com.djrapitops.plan.Plan;
import main.java.com.djrapitops.plan.Settings;
import main.java.com.djrapitops.plan.data.SessionData;
import main.java.com.djrapitops.plan.data.TPS;
import main.java.com.djrapitops.plan.utilities.FormatUtils;
import main.java.com.djrapitops.plan.utilities.MiscUtils;
import main.java.com.djrapitops.plan.utilities.analysis.MathUtils;
import main.java.com.djrapitops.plan.utilities.analysis.Point;
import java.io.Serializable;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
*
* @author Rsl1122
*/
public class PlayerActivityGraphCreator {
@Deprecated
public static String[] generateArray(List<TPS> tpsData, long scale) {
long now = MiscUtils.getTime();
List<TPS> filtered = tpsData.stream().filter(tps -> tps.getDate() >= now - scale).collect(Collectors.toList());
String players = filtered.stream().map(TPS::getPlayers).collect(Collectors.toList()).toString();
String dates = filtered.stream().map(TPS::getDate).collect(Collectors.toList()).toString();
return new String[]{players, dates};
}
public static String buildScatterDataString(List<TPS> tpsData, long scale) {
long now = MiscUtils.getTime();
List<Point> points = tpsData.stream().filter(tps -> tps.getDate() >= now - scale).map(tps -> new Point(tps.getDate(), tps.getPlayers())).collect(Collectors.toList());
List<Point> points = tpsData.stream()
.filter(tps -> tps.getDate() >= now - scale)
.map(tps -> new Point(tps.getDate(), tps.getPlayers()))
.collect(Collectors.toList());
return ScatterGraphCreator.scatterGraph(points, true);
}
/**
*
* @param sessionData
* @param scale
* @return
*/
public static String[] generateDataArray(List<SessionData> sessionData, long scale) {
public static String buildScatterDataStringSessions(List<SessionData> sessionData, long scale) {
long now = MiscUtils.getTime();
long nowMinusScale = now - scale;
List<List<Long>> s = filterAndTransformSessions(sessionData, nowMinusScale);
@ -53,52 +35,26 @@ public class PlayerActivityGraphCreator {
sessionStarts.add(nowMinusScale);
}
Map<Long, Integer> change = transformIntoChangeMap(sessionStarts, sessionEnds);
long lastPValue = 0;
long lastSavedPValue = -1;
long lastSaveIndex = 0;
List<Long> playersOnline = new ArrayList<>();
List<String> labels = new ArrayList<>();
for (long i = nowMinusScale / 1000; i <= now / 1000; i += 1) {
long index = i * 1000;
boolean contains = change.containsKey(index);
boolean isBelowMinimumScaleThreshold = index - lastSaveIndex > (scale / (long) 75);
if (!(contains || isBelowMinimumScaleThreshold)) {
continue;
}
if (contains) {
lastPValue += change.get(index);
}
if (isBelowMinimumScaleThreshold || lastSavedPValue != lastPValue) {
lastSaveIndex = index;
labels.add("\"" + FormatUtils.formatTimeStamp(index) + "\"");
lastSavedPValue = lastPValue;
playersOnline.add(lastPValue);
}
}
if (Settings.ANALYSIS_REMOVE_OUTLIERS.isTrue()) {
long average = MathUtils.averageLong(playersOnline.stream());
double standardDeviation = getStandardDeviation(playersOnline, average);
if (standardDeviation > 3.5) {
for (int i = 0; i < playersOnline.size(); i++) {
long value = playersOnline.get(i);
if (value - average > 3 * standardDeviation) {
playersOnline.set(i, (long) Plan.getInstance().getVariable().getMaxPlayers() + 10);
}
}
}
}
return new String[]{playersOnline.toString(), labels.toString()};
Map<Long, Integer> changeMap = transformIntoChangeMap(sessionStarts, sessionEnds);
List<Point> points = getPointsFromChangeMap(changeMap);
return ScatterGraphCreator.scatterGraph(points, false);
}
private static double getStandardDeviation(List<Long> players, long avg) {
List<Double> valueMinusAvg = players.stream()
.map(p -> Math.pow(Math.abs(p - avg), 2))
.collect(Collectors.toList());
int size = valueMinusAvg.size();
double sum = MathUtils.sumDouble(valueMinusAvg.stream().map(p -> (Serializable) p));
return Math.sqrt(sum / size);
private static List<Point> getPointsFromChangeMap(Map<Long, Integer> changeMap) {
List<Point> points = new ArrayList<>();
int lastIndex = -1;
for (Long key : changeMap.keySet()) {
long date = key;
int change = changeMap.get(key);
if (change != 0) {
int previousValue = 0;
if (lastIndex >= 0) {
previousValue = (int) points.get(lastIndex).getY();
}
points.add(new Point(date, previousValue+change));
}
}
return points;
}
private static Map<Long, Integer> transformIntoChangeMap(List<Long> sessionStarts, List<Long> sessionEnds) {
@ -121,20 +77,15 @@ public class PlayerActivityGraphCreator {
}
/**
*
* @param values
* @param lookFor
* @return
*/
public static long getCount(List<Long> values, long lookFor) {
return Collections.frequency(values, lookFor);
// values.stream()
// .filter((start) -> (start == lookFor))
// .count();
}
/**
*
* @param sessionData
* @param nowMinusScale
* @return
@ -162,7 +113,6 @@ public class PlayerActivityGraphCreator {
}
/**
*
* @param ms
* @return
*/

View File

@ -1,14 +1,13 @@
package main.java.com.djrapitops.plan.ui.html.graphs;
import main.java.com.djrapitops.plan.data.TPS;
import main.java.com.djrapitops.plan.utilities.FormatUtils;
import main.java.com.djrapitops.plan.utilities.MiscUtils;
import main.java.com.djrapitops.plan.utilities.analysis.Point;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import main.java.com.djrapitops.plan.Log;
import main.java.com.djrapitops.plan.data.TPS;
import main.java.com.djrapitops.plan.utilities.Benchmark;
import main.java.com.djrapitops.plan.utilities.MiscUtils;
import main.java.com.djrapitops.plan.utilities.analysis.Point;
import main.java.com.djrapitops.plan.utilities.comparators.TPSComparator;
/**
*
@ -17,22 +16,12 @@ import main.java.com.djrapitops.plan.utilities.comparators.TPSComparator;
*/
public class TPSGraphCreator {
public static String[] generateDataArray(List<TPS> tpsData, long scale) {
Benchmark.start("TPSGraph: generate array");
long now = MiscUtils.getTime();
List<TPS> filtered = filterTPS(tpsData, now - scale);
Log.debug("TPSGraph, filtered: " + filtered.size());
filtered.sort(new TPSComparator());
List<Long> dates = filtered.stream().map(TPS::getDate).collect(Collectors.toList());
List<Double> tps = filtered.stream().map(TPS::getTps).collect(Collectors.toList());
List<Integer> players = filtered.stream().map(TPS::getPlayers).collect(Collectors.toList());
Benchmark.stop("TPSGraph: generate array");
return new String[]{dates.toString(), tps.toString(), players.toString()};
}
public static String buildScatterDataStringTPS(List<TPS> tpsData, long scale) {
long now = MiscUtils.getTime();
List<Point> points = tpsData.stream().filter(tps -> tps.getDate() >= now - scale).map(tps -> new Point(tps.getDate(), tps.getTps())).collect(Collectors.toList());
List<Point> points = tpsData.stream()
.filter(tps -> tps.getDate() >= now - scale)
.map(tps -> new Point(tps.getDate(), Double.parseDouble(FormatUtils.cutDecimals(tps.getTps()).replace(",", "."))))
.collect(Collectors.toList());
return ScatterGraphCreator.scatterGraph(points, true);
}

View File

@ -8,12 +8,12 @@ import java.util.Optional;
/**
* Represents a HTTP Request.
*
* <p>
* Request is read from the given InputStream.
*
* <p>
* Closing the Request closes the InputStream. (Closing Socket InputStream
* closes the socket.)
*
* <p>
* Request Strings should not be logged because they may contain base64 encoded
* user:password Authorization combinations.
*
@ -40,11 +40,11 @@ public class Request implements Closeable {
/**
* Reads the information in the Request and parses required information.
*
* <p>
* Parses Request (GET, POST etc.)
*
* <p>
* Parses Target (/home/etc)
*
* <p>
* Parses Authorization (Authorization header).
*
* @throws java.io.IOException if InputStream can not be read.
@ -53,8 +53,11 @@ public class Request implements Closeable {
StringBuilder headerB = new StringBuilder();
BufferedReader in = new BufferedReader(new InputStreamReader(input));
close = in;
String line;
while ((line = in.readLine()) != null) {
while (true) {
String line = in.readLine();
if (line == null || line.isEmpty()) {
break;
}
headerB.append(line);
headerB.append(":::");
}
@ -126,7 +129,7 @@ public class Request implements Closeable {
/**
* Closes the Request.
*
* <p>
* Closes the InputStream.
*
* @throws IOException if the stream can not be closed.

View File

@ -92,8 +92,8 @@ public class WebSocketServer {
response.sendStaticResource();
} catch (IOException | IllegalArgumentException e) {
} finally {
Benchmark.stop("Webserver Response");
MiscUtils.close(input, request, output, socket);
Benchmark.stop("Webserver Response");
}
}
this.cancel();

View File

@ -37,6 +37,7 @@ public abstract class Response {
+ content;
// Log.debug("Response: " + response); // Responses should not be logged, html content large.
output.write(response.getBytes());
output.flush();
}
public void setHeader(String header) {

View File

@ -1,5 +1,6 @@
package main.java.com.djrapitops.plan.utilities;
import com.djrapitops.plugin.api.TimeAmount;
import main.java.com.djrapitops.plan.Plan;
import main.java.com.djrapitops.plan.Settings;
import main.java.com.djrapitops.plan.data.AnalysisData;
@ -106,10 +107,9 @@ public class PlaceholderUtils {
Plan plugin = Plan.getInstance();
replaceMap.put("%version%", plugin.getDescription().getVersion());
replaceMap.put("%planlite%", "");
String[] playersDataArray = PlayerActivityGraphCreator.generateDataArray(data.getSessions(), (long) 604800 * 1000);
replaceMap.put("%graphmaxplayers%", 2 + "");
replaceMap.put("%dataweek%", playersDataArray[0]);
replaceMap.put("%labelsweek%", playersDataArray[1]);
String scatterGraphData = PlayerActivityGraphCreator.buildScatterDataStringSessions(data.getSessions(), TimeAmount.WEEK.ms());
replaceMap.put("%dataweek%", scatterGraphData);
replaceMap.put("%playersgraphcolor%", Settings.HCOLOR_ACT_ONL + "");
replaceMap.put("%playersgraphfill%", Settings.HCOLOR_ACT_ONL_FILL + "");
replaceMap.put("%datapunchcard%", PunchCardGraphCreator.generateDataArray(data.getSessions()));

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>Plan | Inspect %name%</title>
<meta name="description" content="Player Analysis window">
<meta name="author" content="Rsl1122">
<link rel="icon" href="https://puu.sh/tK0KL/6aa2ba141b.ico" type="image/x-icon" />
<script src="https://use.fontawesome.com/df48eb908b.js"></script>
<style>
<title>Plan | Inspect %name%</title>
<meta name="description" content="Player Analysis window">
<meta name="author" content="Rsl1122">
<link rel="icon" href="https://puu.sh/tK0KL/6aa2ba141b.ico" type="image/x-icon"/>
<script src="https://use.fontawesome.com/df48eb908b.js"></script>
<style>
header {
position: fixed;
top: 0;
@ -314,240 +314,252 @@
width: 100%;
}
}
</style>
</style>
</head>
<body>
<header>
<div class="header-content">
<img style="float: right; padding: 5px" src="https://puu.sh/tJZUb/c2e0ab220f.png" alt="Player Analytics | Analysis">
<p style="float: right; text-align: right;">Player Analytics v.%version%</p>
<h1><span id="navbutton" onclick="openNav()">&#9776; </span>%servername% | Inspect Player %name%%op%</h1>
</div>
<div class="header-content">
<img style="float: right; padding: 5px" src="https://puu.sh/tJZUb/c2e0ab220f.png"
alt="Player Analytics | Analysis">
<p style="float: right; text-align: right;">Player Analytics v.%version%</p>
<h1><span id="navbutton" onclick="openNav()">&#9776; </span>%servername% | Inspect Player %name%%op%</h1>
</div>
</header>
<div id="content" class="content">
<div id="sidenav" class="sidenav">
<p>Last Refresh: <br><span id="divTime">%refresh%</span> ago</p>
<a href="javascript:void(0)" class="nav-button">
<i class="fa fa-info-circle" aria-hidden="true"></i> Information
</a>
<a href="javascript:void(0)" class="nav-button">
<i class="fa fa-calendar" aria-hidden="true"></i> Sessions
</a>
<a href="javascript:void(0)" class="nav-button">
<i class="fa fa-cubes" aria-hidden="true"></i> Plugins
</a>
</div>
<div id="limiter" class="main-limiter">
<div id="main" class="main-wrapper">
<div class="tab">
<div class="row">
<div class="about box column">
<div class="headerbox">
<div class="header-icon">
<div class="header-label"><i class="fa fa-info-circle" aria-hidden="true"></i><span class="header-text"> Information</span></div>
</div>
<div class="infobox" style="float: right;">
<div class="info-icon">
<i class="fa fa-user-plus" aria-hidden="true"></i>
</div>
<div class="info-text" style="width: 70%;">
<div class="info-number">
%registered%
</div>
<div class="info-label">
Registered
</div>
</div>
</div>
</div>
<img style="float: right; padding: 5px" alt="%name%" src="https://cravatar.eu/head/%name%/128.png">
<p><i class="fa fa-user" aria-hidden="true"></i> %active% %isonline%%banned%<br/>
<i class="fa fa-address-card-o" aria-hidden="true"></i> Nicknames: %nicknames%<br/>
<i class="fa fa-clock-o" aria-hidden="true"></i> Playtime: %playtime%<br/>
<i class="fa fa-calendar-plus-o" aria-hidden="true"></i> Login times: %logintimes%<br/>
<i class="fa fa-gavel" aria-hidden="true"></i> Times kicked: %timeskicked%<br/>
<i class="fa fa-crosshairs" aria-hidden="true"></i> Player kills: %playerkills% | <i class="fa fa-crosshairs" aria-hidden="true"></i> Mob kills: %mobkills% | <i class="fa fa-meh-o" aria-hidden="true"></i> Deaths: %deaths%<br/>
<br/>
<i class="fa fa-globe" aria-hidden="true"></i> Geolocation: %geoloc%<br/>
<i class="fa fa-tag" aria-hidden="true"></i> UUID: %uuid%<br/>
<i class="fa fa-globe" aria-hidden="true"></i> Has Connected from ips: %ips%</p>
</div>
<div class="about box column">
<div class="headerbox">
<div class="header-icon" style="width: 50%">
<div class="header-label"><i class="fa fa-pie-chart" aria-hidden="true"></i><span class="header-text"> Gamemode Usage</span></div>
</div>
<div class="infobox" >
<div class="info-icon">
<i class="fa fa-clock-o" aria-hidden="true"></i>
</div>
<div class="info-text" style="width: 70%;">
<div class="info-number">
%gmtotal%
</div>
<div class="info-label">
Total
</div>
</div>
</div>
</div>
<div class="box-area">
<div class="infobox" style="float: left; background-color: #%gm0col%; width: 45%;">
<div class="info-icon">
<i class="fa fa-fire" aria-hidden="true"></i>
</div>
<div class="info-text">
<div class="info-number">
%gm0%
</div>
<div class="info-label">
Survival
</div>
</div>
</div>
<div class="infobox" style="float: left; background-color: #%gm1col%; width: 45%;">
<div class="info-icon">
<i class="fa fa-cube" aria-hidden="true"></i>
</div>
<div class="info-text">
<div class="info-number">
%gm1%
</div>
<div class="info-label">
Creative
</div>
</div>
</div>
<div class="infobox" style="float: left; background-color: #%gm2col%; width: 45%;">
<div class="info-icon">
<i class="fa fa-chevron-right" aria-hidden="true"></i>
</div>
<div class="info-text">
<div class="info-number">
%gm2%
</div>
<div class="info-label">
Adventure
</div>
</div>
</div>
<div class="infobox" style="float: left; background-color: #%gm3col%; width: 45%;">
<div class="info-icon">
<i class="fa fa-binoculars" aria-hidden="true"></i>
</div>
<div class="info-text">
<div class="info-number">
%gm3%
</div>
<div class="info-label">
Spectator
</div>
</div>
</div>
</div>
<canvas id="gmPie" width="1000" height="600" style="width: 95%;"></canvas>
</div>
</div>
<div class="row">
<div class="about box column">
<div class="headerbox">
<div class="header-icon">
<div class="header-label"><i class="fa fa-bar-chart" aria-hidden="true"></i><span class="header-text"> Player Activity: 7d</span></div>
</div>
<div class="infobox" style="float: right;">
<div class="info-icon">
<i class="fa fa-calendar-check-o" aria-hidden="true"></i>
</div>
<div class="info-text" style="width: 70%;">
<div class="info-number">
%lastseen%
</div>
<div class="info-label">
Last seen
</div>
</div>
</div>
</div>
<canvas id="playerChartWeek" width="1000" height="350" style="width: 95%;"></canvas>
</div>
<div class="about box column">
<div class="headerbox">
<div class="header-icon">
<div class="header-label"><i class="fa fa-crosshairs" aria-hidden="true"></i><span class="header-text"> Last 10 Kills</span></div>
</div>
<div class="infobox" style="float: right;">
<div class="info-icon">
<i class="fa fa-crosshairs" aria-hidden="true"></i>
</div>
<div class="info-text" style="width: 70%;">
<div class="info-number">
%playerkills%
</div>
<div class="info-label">
Player Kills
</div>
</div>
</div>
</div>
%killstable%
</div>
</div>
</div>
<div class="tab">
<div class="row">
<div class="about box column">
<div class="headerbox">
<div class="header-icon" style="width: 50%">
<div class="header-label"><i class="fa fa-braille" aria-hidden="true"></i><span class="header-text"> PunchCard</span></div>
</div>
<div class="infobox" >
<div class="info-icon">
<i class="fa fa-calendar-plus-o" aria-hidden="true"></i>
</div>
<div class="info-text" style="width: 70%;">
<div class="info-number">
%logintimes%
</div>
<div class="info-label">
Login Times
</div>
</div>
</div>
</div>
<canvas id="punchcard" width="1000" height="600" style="width: 95%;"></canvas>
</div>
</div>
<div class="row">
<div class="about box column">
<div class="headerbox">
<div class="header-icon">
<div class="header-label"><i class="fa fa-bar-chart" aria-hidden="true"></i><span class="header-text"> Length Distribution</span></div>
</div>
<div class="infobox">
<div class="info-icon">
<i class="fa fa-clock-o" aria-hidden="true"></i>
</div>
<div class="info-text" style="width: 70%;">
<div class="info-number">
%sessionaverage%
</div>
<div class="info-label">
Average Length
</div>
</div>
</div>
</div>
<canvas id="sessiondistribution" width="1000" height="600" style="width: 95%;"></canvas>
<p>If the graph contains more sessions than login times, likely cause is use of /reload (Restarts sessions).</p>
</div>
</div>
</div>
<div class="tab" style="display: block;">
%plugins%
</div>
</div>
</div>
<script>
<div id="sidenav" class="sidenav">
<p>Last Refresh: <br><span id="divTime">%refresh%</span> ago</p>
<a href="javascript:void(0)" class="nav-button">
<i class="fa fa-info-circle" aria-hidden="true"></i> Information
</a>
<a href="javascript:void(0)" class="nav-button">
<i class="fa fa-calendar" aria-hidden="true"></i> Sessions
</a>
<a href="javascript:void(0)" class="nav-button">
<i class="fa fa-cubes" aria-hidden="true"></i> Plugins
</a>
</div>
<div id="limiter" class="main-limiter">
<div id="main" class="main-wrapper">
<div class="tab">
<div class="row">
<div class="about box column">
<div class="headerbox">
<div class="header-icon">
<div class="header-label"><i class="fa fa-info-circle" aria-hidden="true"></i><span
class="header-text"> Information</span></div>
</div>
<div class="infobox" style="float: right;">
<div class="info-icon">
<i class="fa fa-user-plus" aria-hidden="true"></i>
</div>
<div class="info-text" style="width: 70%;">
<div class="info-number">
%registered%
</div>
<div class="info-label">
Registered
</div>
</div>
</div>
</div>
<img style="float: right; padding: 5px" alt="%name%"
src="https://cravatar.eu/head/%name%/128.png">
<p><i class="fa fa-user" aria-hidden="true"></i> %active% %isonline%%banned%<br/>
<i class="fa fa-address-card-o" aria-hidden="true"></i> Nicknames: %nicknames%<br/>
<i class="fa fa-clock-o" aria-hidden="true"></i> Playtime: %playtime%<br/>
<i class="fa fa-calendar-plus-o" aria-hidden="true"></i> Login times: %logintimes%<br/>
<i class="fa fa-gavel" aria-hidden="true"></i> Times kicked: %timeskicked%<br/>
<i class="fa fa-crosshairs" aria-hidden="true"></i> Player kills: %playerkills% | <i
class="fa fa-crosshairs" aria-hidden="true"></i> Mob kills: %mobkills% | <i
class="fa fa-meh-o" aria-hidden="true"></i> Deaths: %deaths%<br/>
<br/>
<i class="fa fa-globe" aria-hidden="true"></i> Geolocation: %geoloc%<br/>
<i class="fa fa-tag" aria-hidden="true"></i> UUID: %uuid%<br/>
<i class="fa fa-globe" aria-hidden="true"></i> Has Connected from ips: %ips%</p>
</div>
<div class="about box column">
<div class="headerbox">
<div class="header-icon" style="width: 50%">
<div class="header-label"><i class="fa fa-pie-chart" aria-hidden="true"></i><span
class="header-text"> Gamemode Usage</span></div>
</div>
<div class="infobox">
<div class="info-icon">
<i class="fa fa-clock-o" aria-hidden="true"></i>
</div>
<div class="info-text" style="width: 70%;">
<div class="info-number">
%gmtotal%
</div>
<div class="info-label">
Total
</div>
</div>
</div>
</div>
<div class="box-area">
<div class="infobox" style="float: left; background-color: #%gm0col%; width: 45%;">
<div class="info-icon">
<i class="fa fa-fire" aria-hidden="true"></i>
</div>
<div class="info-text">
<div class="info-number">
%gm0%
</div>
<div class="info-label">
Survival
</div>
</div>
</div>
<div class="infobox" style="float: left; background-color: #%gm1col%; width: 45%;">
<div class="info-icon">
<i class="fa fa-cube" aria-hidden="true"></i>
</div>
<div class="info-text">
<div class="info-number">
%gm1%
</div>
<div class="info-label">
Creative
</div>
</div>
</div>
<div class="infobox" style="float: left; background-color: #%gm2col%; width: 45%;">
<div class="info-icon">
<i class="fa fa-chevron-right" aria-hidden="true"></i>
</div>
<div class="info-text">
<div class="info-number">
%gm2%
</div>
<div class="info-label">
Adventure
</div>
</div>
</div>
<div class="infobox" style="float: left; background-color: #%gm3col%; width: 45%;">
<div class="info-icon">
<i class="fa fa-binoculars" aria-hidden="true"></i>
</div>
<div class="info-text">
<div class="info-number">
%gm3%
</div>
<div class="info-label">
Spectator
</div>
</div>
</div>
</div>
<canvas id="gmPie" width="1000" height="600" style="width: 95%;"></canvas>
</div>
</div>
<div class="row">
<div class="about box column">
<div class="headerbox">
<div class="header-icon">
<div class="header-label"><i class="fa fa-bar-chart" aria-hidden="true"></i><span
class="header-text"> Player Activity: 7d</span></div>
</div>
<div class="infobox" style="float: right;">
<div class="info-icon">
<i class="fa fa-calendar-check-o" aria-hidden="true"></i>
</div>
<div class="info-text" style="width: 70%;">
<div class="info-number">
%lastseen%
</div>
<div class="info-label">
Last seen
</div>
</div>
</div>
</div>
<canvas id="playerChartWeek" width="1000" height="350" style="width: 95%;"></canvas>
</div>
<div class="about box column">
<div class="headerbox">
<div class="header-icon">
<div class="header-label"><i class="fa fa-crosshairs" aria-hidden="true"></i><span
class="header-text"> Last 10 Kills</span></div>
</div>
<div class="infobox" style="float: right;">
<div class="info-icon">
<i class="fa fa-crosshairs" aria-hidden="true"></i>
</div>
<div class="info-text" style="width: 70%;">
<div class="info-number">
%playerkills%
</div>
<div class="info-label">
Player Kills
</div>
</div>
</div>
</div>
%killstable%
</div>
</div>
</div>
<div class="tab">
<div class="row">
<div class="about box column">
<div class="headerbox">
<div class="header-icon" style="width: 50%">
<div class="header-label"><i class="fa fa-braille" aria-hidden="true"></i><span
class="header-text"> PunchCard</span></div>
</div>
<div class="infobox">
<div class="info-icon">
<i class="fa fa-calendar-plus-o" aria-hidden="true"></i>
</div>
<div class="info-text" style="width: 70%;">
<div class="info-number">
%logintimes%
</div>
<div class="info-label">
Login Times
</div>
</div>
</div>
</div>
<canvas id="punchcard" width="1000" height="600" style="width: 95%;"></canvas>
</div>
</div>
<div class="row">
<div class="about box column">
<div class="headerbox">
<div class="header-icon">
<div class="header-label"><i class="fa fa-bar-chart" aria-hidden="true"></i><span
class="header-text"> Length Distribution</span></div>
</div>
<div class="infobox">
<div class="info-icon">
<i class="fa fa-clock-o" aria-hidden="true"></i>
</div>
<div class="info-text" style="width: 70%;">
<div class="info-number">
%sessionaverage%
</div>
<div class="info-label">
Average Length
</div>
</div>
</div>
</div>
<canvas id="sessiondistribution" width="1000" height="600" style="width: 95%;"></canvas>
<p>If the graph contains more sessions than login times, likely cause is use of /reload
(Restarts sessions).</p>
</div>
</div>
</div>
<div class="tab" style="display: block;">
%plugins%
</div>
</div>
</div>
<script>
var serverTime = new Date(%currenttime%);
var now = new Date();
var timediff = serverTime.getTime()-now.getTime();
@ -639,11 +651,12 @@
document.getElementById('divTime').innerHTML = out;
setTimeout('countUpTimer()', 1000);
}
</script>
<script src="https://www.kryogenix.org/code/browser/sorttable/sorttable.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.bundle.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.js"></script>
<script>
</script>
<script src="https://www.kryogenix.org/code/browser/sorttable/sorttable.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.bundle.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.js"></script>
<script>
function hour(i) {
switch (i) {
case 25:
@ -702,7 +715,6 @@
});
var ctxweek = document.getElementById("playerChartWeek");
var dataweek = {
labels: %labelsweek% ,
datasets: [{
label: "Online",
fill: true,
@ -726,14 +738,24 @@
data: %dataweek% ,
}]};
var playersChartWeek = new Chart(ctxweek, {
type: 'line',
data: dataweek,
options: {
scales: {
yAxes: [{
display: true,
ticks: {
callback: function(value, index, values) {
type: 'scatter',
data: dataweek,
options: {
tooltips: {
callbacks: {
label: function(tooltipItems, data) {
var newDate = new Date();
newDate.setTime(tooltipItems.xLabel);
dateString = newDate.toUTCString();
return dateString +': '+ tooltipItems.yLabel+ ' Players';
}
}
},
scales: {
yAxes: [{
display: true,
ticks: {
callback: function(value, index, values) {
switch (value) {
case 0:
return '0';
@ -743,16 +765,16 @@
return '';
};
},
suggestedMax: 2,
suggestedMin: 0
suggestedMax: %graphmaxplayers%
}
}],
xAxes: [{
type: 'linear',
display: false
}]
}
}],
xAxes: [{
display: false
}]
}
}
});
}
});
var ctxpunch = document.getElementById("punchcard");
var datapunch = {
datasets: [
@ -877,6 +899,8 @@
}
}
});
</script>
</script>
</div>
</body>
</html>

View File

@ -1,15 +1,17 @@
package test.java.main.java.com.djrapitops.plan.ui.graphs;
import main.java.com.djrapitops.plan.data.SessionData;
import main.java.com.djrapitops.plan.ui.html.graphs.PlayerActivityGraphCreator;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import main.java.com.djrapitops.plan.data.SessionData;
import main.java.com.djrapitops.plan.ui.html.graphs.PlayerActivityGraphCreator;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
*
@ -30,18 +32,6 @@ public class PlayerActivityGraphCreatorTest {
public PlayerActivityGraphCreatorTest() {
}
/**
*
*/
@Test
@Ignore("Useless test")
public void testGenerateDataArray() {
List<SessionData> sessionData = createRandomSessionDataList();
long scale = 2592000L * 1000L;
String result = PlayerActivityGraphCreator.generateDataArray(sessionData, scale)[1];
assertTrue("0", 0 < result.length());
}
/**
*
* @return