mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2024-12-23 09:37:54 +01:00
Unique Players, Outlier removal, Gender pie removed
This commit is contained in:
parent
a2af65349a
commit
0b061f0a5e
@ -16,6 +16,7 @@ public enum Settings {
|
||||
ANALYSIS_REFRESH_ON_ENABLE("Settings.Cache.AnalysisCache.RefreshAnalysisCacheOnEnable"),
|
||||
ANALYSIS_LOG_TO_CONSOLE("Settings.Analysis.LogProgressOnConsole"),
|
||||
ANALYSIS_LOG_FINISHED("Settings.Analysis.NotifyWhenFinished"),
|
||||
ANALYSIS_REMOVE_OUTLIERS("Settings.Analysis.RemoveOutliersFromVisualization"),
|
||||
ANALYSIS_EXPORT("Settings.Analysis.Export.Enabled"),
|
||||
SHOW_ALTERNATIVE_IP("Settings.WebServer.ShowAlternativeServerIP"),
|
||||
USE_ALTERNATIVE_UI("Settings.UseTextUI"),
|
||||
@ -78,9 +79,6 @@ public enum Settings {
|
||||
HCOLOR_GMP_1("Customization.Colors.HTML.GamemodePie.Creative"),
|
||||
HCOLOR_GMP_2("Customization.Colors.HTML.GamemodePie.Adventure"),
|
||||
HCOLOR_GMP_3("Customization.Colors.HTML.GamemodePie.Spectator"),
|
||||
HCOLOR_GENP_M("Customization.Colors.HTML.GenderPie.Male"),
|
||||
HCOLOR_GENP_F("Customization.Colors.HTML.GenderPie.Female"),
|
||||
HCOLOR_GENP_U("Customization.Colors.HTML.GenderPie.Unknown"),
|
||||
// StringList
|
||||
HIDE_FACTIONS("Customization.Plugins.Factions.HideFactions"),
|
||||
HIDE_TOWNS("Customization.Plugins.Towny.HideTowns");
|
||||
|
@ -70,7 +70,14 @@ public class AnalysisData {
|
||||
private String geomapZ;
|
||||
private String geomapCodes;
|
||||
|
||||
private int[] genderData;
|
||||
private int avgUniqJoins;
|
||||
private int avgUniqJoinsDay;
|
||||
private int avgUniqJoinsWeek;
|
||||
private int avgUniqJoinsMonth;
|
||||
|
||||
private int uniqueJoinsDay;
|
||||
private int uniqueJoinsWeek;
|
||||
private int uniqueJoinsMonth;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
@ -78,6 +85,13 @@ public class AnalysisData {
|
||||
* All data has to be set with setters to avoid NPEs.
|
||||
*/
|
||||
public AnalysisData() {
|
||||
avgUniqJoins = 0;
|
||||
avgUniqJoinsDay = 0;
|
||||
avgUniqJoinsWeek = 0;
|
||||
avgUniqJoinsMonth = 0;
|
||||
uniqueJoinsDay = 0;
|
||||
uniqueJoinsWeek = 0;
|
||||
uniqueJoinsMonth = 0;
|
||||
sortablePlayersTable = Html.ERROR_NOT_SET + "";
|
||||
commandUseTableHtml = Html.ERROR_NOT_SET + "";
|
||||
recentPlayers = Html.ERROR_NOT_SET + "";
|
||||
@ -88,7 +102,6 @@ public class AnalysisData {
|
||||
sessionDistributionData = new String[]{"[]", "[]"};
|
||||
playtimeDistributionData = new String[]{"[]", "[]"};
|
||||
playersDataArray = new String[]{"[0]", "[\"No data\"]", "[0]", "[\"No data\"]", "[0]", "[\"No data\"]"};
|
||||
genderData = new int[]{0, 0, 0};
|
||||
additionalDataReplaceMap = new HashMap<>();
|
||||
}
|
||||
|
||||
@ -194,9 +207,6 @@ public class AnalysisData {
|
||||
if (!Arrays.deepEquals(this.playersDataArray, other.playersDataArray)) {
|
||||
return false;
|
||||
}
|
||||
if (!Arrays.equals(this.genderData, other.genderData)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -862,29 +872,6 @@ public class AnalysisData {
|
||||
this.sessionAverage = sessionAverage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the integer array containing 3 numbers.
|
||||
*
|
||||
* 0 Male, 1 Female, 2 Unknown.
|
||||
*
|
||||
* @return for example [0, 4, 5] when 0 male, 4 female and 5 unknown.
|
||||
*/
|
||||
public int[] getGenderData() {
|
||||
return genderData;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Set the integer array containing 3 numbers.
|
||||
*
|
||||
* 0 Male, 1 Female, 2 Unknown.
|
||||
*
|
||||
* @param genderData for example [0, 4, 5]
|
||||
*/
|
||||
public void setGenderData(int[] genderData) {
|
||||
this.genderData = genderData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data for the Session Punchcard.
|
||||
*
|
||||
@ -940,4 +927,60 @@ public class AnalysisData {
|
||||
public void setPlaytimeDistributionData(String[] playtimeDistributionData) {
|
||||
this.playtimeDistributionData = playtimeDistributionData;
|
||||
}
|
||||
|
||||
public int getAvgUniqJoins() {
|
||||
return avgUniqJoins;
|
||||
}
|
||||
|
||||
public int getAvgUniqJoinsDay() {
|
||||
return avgUniqJoinsDay;
|
||||
}
|
||||
|
||||
public int getAvgUniqJoinsWeek() {
|
||||
return avgUniqJoinsWeek;
|
||||
}
|
||||
|
||||
public int getAvgUniqJoinsMonth() {
|
||||
return avgUniqJoinsMonth;
|
||||
}
|
||||
|
||||
public void setAvgUniqJoins(int avgUniqJoins) {
|
||||
this.avgUniqJoins = avgUniqJoins;
|
||||
}
|
||||
|
||||
public void setAvgUniqJoinsDay(int avgUniqJoinsDay) {
|
||||
this.avgUniqJoinsDay = avgUniqJoinsDay;
|
||||
}
|
||||
|
||||
public void setAvgUniqJoinsWeek(int avgUniqJoinsWeek) {
|
||||
this.avgUniqJoinsWeek = avgUniqJoinsWeek;
|
||||
}
|
||||
|
||||
public void setAvgUniqJoinsMonth(int avgUniqJoinsMonth) {
|
||||
this.avgUniqJoinsMonth = avgUniqJoinsMonth;
|
||||
}
|
||||
|
||||
public int getUniqueJoinsDay() {
|
||||
return uniqueJoinsDay;
|
||||
}
|
||||
|
||||
public void setUniqueJoinsDay(int uniqueJoinsDay) {
|
||||
this.uniqueJoinsDay = uniqueJoinsDay;
|
||||
}
|
||||
|
||||
public int getUniqueJoinsWeek() {
|
||||
return uniqueJoinsWeek;
|
||||
}
|
||||
|
||||
public void setUniqueJoinsWeek(int uniqueJoinsWeek) {
|
||||
this.uniqueJoinsWeek = uniqueJoinsWeek;
|
||||
}
|
||||
|
||||
public int getUniqueJoinsMonth() {
|
||||
return uniqueJoinsMonth;
|
||||
}
|
||||
|
||||
public void setUniqueJoinsMonth(int uniqueJoinsMonth) {
|
||||
this.uniqueJoinsMonth = uniqueJoinsMonth;
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import main.java.com.djrapitops.plan.utilities.analysis.Analysis;
|
||||
|
||||
/**
|
||||
@ -34,11 +35,11 @@ public class RawAnalysisData {
|
||||
private Map<String, Long> latestLogins;
|
||||
private Map<String, Long> playtimes;
|
||||
private List<SessionData> sessiondata;
|
||||
private Map<UUID, List<SessionData>> sortedSessionData;
|
||||
private Map<String, Integer> commandUse;
|
||||
private Map<String, Integer> geolocations;
|
||||
private Map<String, String> geocodes;
|
||||
private List<Long> registered;
|
||||
private int[] genders;
|
||||
|
||||
/**
|
||||
* Constructor for a new empty dataset.
|
||||
@ -56,16 +57,17 @@ public class RawAnalysisData {
|
||||
inactive = 0;
|
||||
totalKills = 0;
|
||||
totalMobKills = 0;
|
||||
totalDeaths = 0;
|
||||
ops = 0;
|
||||
ages = new ArrayList<>();
|
||||
latestLogins = new HashMap<>();
|
||||
playtimes = new HashMap<>();
|
||||
sessiondata = new ArrayList<>();
|
||||
sortedSessionData = new HashMap<>();
|
||||
commandUse = new HashMap<>();
|
||||
geolocations = new HashMap<>();
|
||||
geocodes = new HashMap<>();
|
||||
registered = new ArrayList<>();
|
||||
genders = new int[]{0, 0, 0};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -368,6 +370,15 @@ public class RawAnalysisData {
|
||||
return sessiondata;
|
||||
}
|
||||
|
||||
public Map<UUID, List<SessionData>> getSortedSessionData() {
|
||||
return sortedSessionData;
|
||||
}
|
||||
|
||||
public void addSessions(UUID uuid, List<SessionData> sessions) {
|
||||
sessiondata.addAll(sessions);
|
||||
sortedSessionData.put(uuid, sessions);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param commandUse
|
||||
@ -391,29 +402,4 @@ public class RawAnalysisData {
|
||||
public List<Long> getRegistered() {
|
||||
return registered;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int[] getGenders() {
|
||||
return genders;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param gender
|
||||
*/
|
||||
public void setGenders(int[] gender) {
|
||||
this.genders = gender;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param i
|
||||
* @param amount
|
||||
*/
|
||||
public void addToGender(int i, int amount) {
|
||||
this.genders[i] = this.genders[i] + amount;
|
||||
}
|
||||
}
|
||||
|
@ -31,8 +31,6 @@ import org.bukkit.scheduler.BukkitRunnable;
|
||||
*/
|
||||
public abstract class SQLDB extends Database {
|
||||
|
||||
final Plan plugin;
|
||||
|
||||
private final boolean supportsModification;
|
||||
|
||||
private Connection connection;
|
||||
@ -44,7 +42,6 @@ public abstract class SQLDB extends Database {
|
||||
*/
|
||||
public SQLDB(Plan plugin, boolean supportsModification) {
|
||||
super(plugin);
|
||||
this.plugin = plugin;
|
||||
this.supportsModification = supportsModification;
|
||||
boolean usingMySQL = getName().equals("MySQL");
|
||||
|
||||
@ -158,6 +155,7 @@ public abstract class SQLDB extends Database {
|
||||
Set<UUID> uuids = usersTable.getSavedUUIDs();
|
||||
uuids.removeAll(usersTable.getContainsBukkitData(uuids));
|
||||
if (uuids.isEmpty()) {
|
||||
Log.debug("No conversion necessary.");
|
||||
return;
|
||||
}
|
||||
Log.info("Beginning Bukkit Data -> DB Conversion for " + uuids.size() + " players");
|
||||
@ -313,9 +311,9 @@ public abstract class SQLDB extends Database {
|
||||
List<SessionData> sessions = sessionsTable.getSessionData(userId);
|
||||
data.addSessions(sessions);
|
||||
data.setPlayerKills(killsTable.getPlayerKills(userId));
|
||||
for (DBCallableProcessor processor : processors) {
|
||||
processors.stream().forEach((processor) -> {
|
||||
processor.process(data);
|
||||
}
|
||||
});
|
||||
Benchmark.stop("DB Give userdata to processors");
|
||||
}
|
||||
|
||||
@ -333,12 +331,9 @@ public abstract class SQLDB extends Database {
|
||||
|
||||
Benchmark.start("DB get UserData for " + uuidsCol.size());
|
||||
Map<UUID, Integer> userIds = usersTable.getAllUserIds();
|
||||
Set<UUID> remove = new HashSet<>();
|
||||
for (UUID uuid : uuidsCol) {
|
||||
if (!userIds.containsKey(uuid)) {
|
||||
remove.add(uuid);
|
||||
}
|
||||
}
|
||||
Set<UUID> remove = uuidsCol.stream()
|
||||
.filter((uuid) -> (!userIds.containsKey(uuid)))
|
||||
.collect(Collectors.toSet());
|
||||
List<UUID> uuids = new ArrayList<>(uuidsCol);
|
||||
Log.debug("Data not found for: " + remove.size());
|
||||
uuids.removeAll(remove);
|
||||
@ -426,23 +421,20 @@ public abstract class SQLDB extends Database {
|
||||
gmTimesTable.saveGMTimes(id, gmTimes.get(id));
|
||||
}
|
||||
Benchmark.stop("Save GMTimes");
|
||||
for (Integer id : locations.keySet()) {
|
||||
UUID uuid = uuids.get(id);
|
||||
if (uuid != null) {
|
||||
UserData uData = userDatas.get(uuid);
|
||||
if (uData != null) {
|
||||
userDatas.values().stream()
|
||||
.filter(u -> u != null)
|
||||
.filter(uData -> uData.isAccessed())
|
||||
.forEach(uData -> {
|
||||
uData.stopAccessing();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// Save leftovers
|
||||
for (UserData userData : saveLast) {
|
||||
saveLast.stream().forEach((userData) -> {
|
||||
try {
|
||||
saveUserData(userData);
|
||||
} catch (SQLException | NullPointerException e) {
|
||||
exceptions.add(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!exceptions.isEmpty()) {
|
||||
Log.error("SEVERE: MULTIPLE ERRORS OCCURRED: " + exceptions.size());
|
||||
Log.toLog(this.getClass().getName(), exceptions);
|
||||
@ -478,74 +470,6 @@ public abstract class SQLDB extends Database {
|
||||
data.stopAccessing();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param userId
|
||||
* @param locations
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Deprecated
|
||||
public void saveAdditionalLocationsList(int userId, List<Location> locations) throws SQLException {
|
||||
locationsTable.saveAdditionalLocationsList(userId, locations);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param userId
|
||||
* @param names
|
||||
* @param lastNick
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Deprecated
|
||||
public void saveNickList(int userId, Set<String> names, String lastNick) throws SQLException {
|
||||
nicknamesTable.saveNickList(userId, names, lastNick);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param userId
|
||||
* @param sessions
|
||||
* @throws SQLException
|
||||
* @deprecated Use sessionsTable instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void saveSessionList(int userId, List<SessionData> sessions) throws SQLException {
|
||||
sessionsTable.saveSessionData(userId, sessions);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param userId
|
||||
* @param kills
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Deprecated
|
||||
public void savePlayerKills(int userId, List<KillData> kills) throws SQLException {
|
||||
killsTable.savePlayerKills(userId, kills);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param userId
|
||||
* @param ips
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Deprecated
|
||||
public void saveIPList(int userId, Set<InetAddress> ips) throws SQLException {
|
||||
ipsTable.saveIPList(userId, ips);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param userId
|
||||
* @param gamemodeTimes
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Deprecated
|
||||
public void saveGMTimes(int userId, Map<GameMode, Long> gamemodeTimes) throws SQLException {
|
||||
gmTimesTable.saveGMTimes(userId, gamemodeTimes);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -4,7 +4,6 @@ import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@ -21,10 +20,7 @@ import main.java.com.djrapitops.plan.data.UserData;
|
||||
import main.java.com.djrapitops.plan.database.databases.SQLDB;
|
||||
import main.java.com.djrapitops.plan.utilities.Benchmark;
|
||||
import main.java.com.djrapitops.plan.utilities.UUIDFetcher;
|
||||
import static org.bukkit.Bukkit.getOfflinePlayer;
|
||||
import static org.bukkit.Bukkit.getOfflinePlayers;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import static org.bukkit.Bukkit.getOfflinePlayer;
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,6 @@
|
||||
package main.java.com.djrapitops.plan.ui.graphs;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
@ -10,9 +11,11 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import main.java.com.djrapitops.plan.Settings;
|
||||
import main.java.com.djrapitops.plan.data.SessionData;
|
||||
import main.java.com.djrapitops.plan.utilities.Benchmark;
|
||||
import main.java.com.djrapitops.plan.utilities.FormatUtils;
|
||||
import main.java.com.djrapitops.plan.utilities.analysis.MathUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -65,13 +68,37 @@ public class PlayerActivityGraphCreator {
|
||||
playersOnline.add(lastPValue);
|
||||
}
|
||||
}
|
||||
if (Settings.ANALYSIS_REMOVE_OUTLIERS.isTrue()) {
|
||||
long average = MathUtils.averageLong(playersOnline.stream());
|
||||
double standardDiviation = getStandardDiviation(playersOnline, average);
|
||||
if (standardDiviation > 3) {
|
||||
for (int i = 0; i < playersOnline.size(); i++) {
|
||||
long value = playersOnline.get(i);
|
||||
if (value - average > 3 * standardDiviation) {
|
||||
playersOnline.set(i, (long) maxPlayers + 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Benchmark.stop("Player Activity Graph Amount Calculation");
|
||||
playersOnline.add(0L);
|
||||
playersOnline.add(0L);
|
||||
playersOnline.add(0L);
|
||||
playersOnline.add(0L);
|
||||
playersOnline.add((long) maxPlayers);
|
||||
Benchmark.stop("Generate Player Activity Graph " + sessionData.size() + " " + scale + " |");
|
||||
return new String[]{playersOnline.toString(), labels.toString()};
|
||||
}
|
||||
|
||||
private static double getStandardDiviation(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 Map<Long, Integer> transformIntoChangeMap(List<Long> sessionStarts, List<Long> sessionEnds) {
|
||||
Benchmark.start("Player Activity Graph Calc. Change");
|
||||
Map<Long, Integer> starts = sessionStarts.stream().distinct().collect(Collectors.toMap(Function.identity(), start -> Collections.frequency(sessionStarts, start)));
|
||||
|
@ -6,12 +6,15 @@
|
||||
package main.java.com.djrapitops.plan.ui.graphs;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import main.java.com.djrapitops.plan.Log;
|
||||
import main.java.com.djrapitops.plan.Settings;
|
||||
import main.java.com.djrapitops.plan.data.SessionData;
|
||||
import main.java.com.djrapitops.plan.utilities.MiscUtils;
|
||||
import main.java.com.djrapitops.plan.utilities.analysis.AnalysisUtils;
|
||||
import main.java.com.djrapitops.plan.utilities.analysis.MathUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -27,7 +30,7 @@ public class PunchCardGraphCreator {
|
||||
public static String generateDataArray(Collection<SessionData> data) {
|
||||
// Initialize dataset
|
||||
List<Long> sessionStarts = getSessionStarts(data);
|
||||
List<int[]> daysAndHours = getDaysAndHours(sessionStarts);
|
||||
List<int[]> daysAndHours = AnalysisUtils.getDaysAndHours(sessionStarts);
|
||||
int[][] dataArray = createDataArray(daysAndHours);
|
||||
int big = findBiggestValue(dataArray);
|
||||
int[][] scaled = scale(dataArray, big);
|
||||
@ -65,36 +68,63 @@ public class PunchCardGraphCreator {
|
||||
int h = dAndH[1];
|
||||
dataArray[d][h] = dataArray[d][h] + 1;
|
||||
}
|
||||
for (int i = 0; i < 7; i++) {
|
||||
Log.debug(" " + Arrays.toString(dataArray[i]));
|
||||
}
|
||||
if (Settings.ANALYSIS_REMOVE_OUTLIERS.isTrue()) {
|
||||
int avg = findAverage(dataArray);
|
||||
double standardDiviation = getStandardDiviation(dataArray, avg);
|
||||
Log.debug("Diviation: " + standardDiviation);
|
||||
for (int i = 0; i < 7; i++) {
|
||||
for (int j = 0; j < 24; j++) {
|
||||
int value = dataArray[i][j];
|
||||
if (value - avg > 3 * standardDiviation) {
|
||||
dataArray[i][j] = (int) (avg);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 7; i++) {
|
||||
Log.debug(" " + Arrays.toString(dataArray[i]));
|
||||
}
|
||||
}
|
||||
return dataArray;
|
||||
}
|
||||
|
||||
private static List<int[]> getDaysAndHours(List<Long> sessionStarts) {
|
||||
List<int[]> daysAndHours = sessionStarts.stream().map(start -> {
|
||||
Calendar day = Calendar.getInstance();
|
||||
day.setTimeInMillis(start);
|
||||
int hourOfDay = day.get(Calendar.HOUR_OF_DAY);
|
||||
private static double getStandardDiviation(int[][] array, int avg) {
|
||||
int[][] valueMinusAvg = new int[7][24];
|
||||
for (int i = 0; i < 7; i++) {
|
||||
for (int j = 0; j < 24; j++) {
|
||||
valueMinusAvg[i][j] = (int) Math.pow(Math.abs(array[i][j] - avg), 2);
|
||||
}
|
||||
}
|
||||
int size = array.length * array[0].length;
|
||||
int sum = sum(valueMinusAvg);
|
||||
return Math.sqrt(sum / size);
|
||||
}
|
||||
|
||||
int dayOfWeek = day.get(Calendar.DAY_OF_WEEK) - 2;
|
||||
if (hourOfDay == 24) {
|
||||
hourOfDay = 0;
|
||||
dayOfWeek += 1;
|
||||
private static int findAverage(int[][] array) {
|
||||
int total = sum(array);
|
||||
int size = array.length * array[0].length;
|
||||
return (int) MathUtils.average(total, size);
|
||||
}
|
||||
if (dayOfWeek > 6) {
|
||||
dayOfWeek = 0;
|
||||
|
||||
private static int sum(int[][] array) {
|
||||
int total = 0;
|
||||
for (int[] is : array) {
|
||||
for (int i : is) {
|
||||
total += i;
|
||||
}
|
||||
if (dayOfWeek < 0) {
|
||||
dayOfWeek = 6;
|
||||
}
|
||||
return new int[]{dayOfWeek, hourOfDay};
|
||||
}).collect(Collectors.toList());
|
||||
return daysAndHours;
|
||||
return total;
|
||||
}
|
||||
|
||||
private static List<Long> getSessionStarts(Collection<SessionData> data) {
|
||||
long now = MiscUtils.getTime();
|
||||
List<Long> sessionStarts = data.stream()
|
||||
.filter(s -> s != null)
|
||||
.filter(s -> s.isValid())
|
||||
.map(s -> s.getSessionStart())
|
||||
.filter(start -> now - start < (long) 2592000 * (long) 1000)
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
return sessionStarts;
|
||||
@ -135,7 +165,10 @@ public class PunchCardGraphCreator {
|
||||
scaled[i][j] = value;
|
||||
}
|
||||
}
|
||||
|
||||
Log.debug("Biggest value: " + big);
|
||||
for (int i = 0; i < 7; i++) {
|
||||
Log.debug(" " + Arrays.toString(scaled[i]));
|
||||
}
|
||||
return scaled;
|
||||
}
|
||||
}
|
||||
|
@ -69,11 +69,11 @@ public class Response {
|
||||
String playerName = requestArgs[3].trim();
|
||||
UUID uuid = UUIDFetcher.getUUIDOf(playerName);
|
||||
if (uuid == null) {
|
||||
String errorMessage = "HTTP/1.1 404 UUID not Found\r\n"
|
||||
String errorMessage = "HTTP/1.1 500 UUID not Found\r\n"
|
||||
+ "Content-Type: text/html;\r\n"
|
||||
+ "Content-Length: 30\r\n"
|
||||
+ "\r\n"
|
||||
+ "<h1>404 - Player doesn't exist</h1>";
|
||||
+ "<h1>500 - Player has no UUID. </h1>";
|
||||
output.write(errorMessage.getBytes());
|
||||
return;
|
||||
}
|
||||
@ -87,11 +87,11 @@ public class Response {
|
||||
output.write((htmlDef + dataHtml).getBytes());
|
||||
} catch (NullPointerException e) {
|
||||
Log.toLog(this.getClass().getName(), e);
|
||||
String errorMessage = "HTTP/1.1 404 Error\r\n"
|
||||
String errorMessage = "HTTP/1.1 500 Error\r\n"
|
||||
+ "Content-Type: text/html;\r\n"
|
||||
+ "Content-Length: 30\r\n"
|
||||
+ "\r\n"
|
||||
+ "<h1>404 - Error has occurred..</h1>";
|
||||
+ "<h1>500 - Error has occurred..</h1>";
|
||||
output.write(errorMessage.getBytes());
|
||||
}
|
||||
return;
|
||||
|
@ -65,6 +65,13 @@ public class PlaceholderUtils {
|
||||
replaceMap.put("%version%", plugin.getDescription().getVersion());
|
||||
replaceMap.put("%planlite%", "");
|
||||
replaceMap.put("%sortabletable%", data.getSortablePlayersTable());
|
||||
replaceMap.put("%uniquejoinsday%", data.getUniqueJoinsDay()+"");
|
||||
replaceMap.put("%uniquejoinsweek%", data.getUniqueJoinsWeek()+"");
|
||||
replaceMap.put("%uniquejoinsmonth%", data.getUniqueJoinsMonth()+"");
|
||||
replaceMap.put("%avguniquejoins%", data.getAvgUniqJoins()+"");
|
||||
replaceMap.put("%avguniquejoinsday%", data.getAvgUniqJoinsDay()+"");
|
||||
replaceMap.put("%avguniquejoinsweek%", data.getAvgUniqJoinsWeek()+"");
|
||||
replaceMap.put("%avguniquejoinsmonth%", data.getAvgUniqJoinsMonth()+"");
|
||||
replaceMap.put("%dataday%", data.getPlayersDataArray()[0]);
|
||||
replaceMap.put("%labelsday%", data.getPlayersDataArray()[1]);
|
||||
replaceMap.put("%dataweek%", data.getPlayersDataArray()[2]);
|
||||
@ -102,14 +109,6 @@ public class PlaceholderUtils {
|
||||
replaceMap.put("%gmlabels%", "[\"Survival\", \"Creative\", \"Adventure\", \"Spectator\"]");
|
||||
replaceMap.put("%gmcolors%", "\"#" + Settings.HCOLOR_GMP_0 + "\",\"#" + Settings.HCOLOR_GMP_1
|
||||
+ "\",\"#" + Settings.HCOLOR_GMP_2 + "\",\"#" + Settings.HCOLOR_GMP_3 + "\"");
|
||||
replaceMap.put("%genderdata%", Arrays.toString(data.getGenderData()));
|
||||
replaceMap.put("%gendermale%", data.getGenderData()[0] + "");
|
||||
replaceMap.put("%genderfemale%", data.getGenderData()[1] + "");
|
||||
replaceMap.put("%genderlabels%", "[\"Male\", \"Female\", \"Unknown\"]");
|
||||
replaceMap.put("%gendercolors%", "\"#" + Settings.HCOLOR_GENP_M + "\",\"#" + Settings.HCOLOR_GENP_F
|
||||
+ "\",\"#" + Settings.HCOLOR_GENP_U + "\"");
|
||||
replaceMap.put("%genderfcolor%", "#" + Settings.HCOLOR_GENP_F);
|
||||
replaceMap.put("%gendermcolor%", "#" + Settings.HCOLOR_GENP_M);
|
||||
replaceMap.put("%sessionaverage%", FormatUtils.formatTimeAmount(data.getSessionAverage()));
|
||||
replaceMap.put("%geomapcountries%", data.getGeomapCountries());
|
||||
replaceMap.put("%geomapz%", data.getGeomapZ());
|
||||
|
@ -128,7 +128,7 @@ public class Analysis {
|
||||
|
||||
// Analyze & Save RawAnalysisData to AnalysisData
|
||||
createCloroplethMap(analysisData, sorted.getGeolocations(), sorted.getGeocodes());
|
||||
createPlayerActivityGraphs(analysisData, sorted.getSessiondata(), sorted.getRegistered());
|
||||
createPlayerActivityGraphs(analysisData, sorted.getSessiondata(), sorted.getRegistered(), sorted.getSortedSessionData());
|
||||
analysisData.setRecentPlayers(RecentPlayersButtonsCreator.createRecentLoginsButtons(sorted.getLatestLogins(), 20));
|
||||
long totalPlaytime = sorted.getTotalPlaytime();
|
||||
analysisData.setTotalPlayTime(totalPlaytime);
|
||||
@ -144,7 +144,6 @@ public class Analysis {
|
||||
analysisData.setTotalkills(sorted.getTotalKills());
|
||||
analysisData.setTotalmobkills(sorted.getTotalMobKills());
|
||||
analysisData.setRefreshDate(now);
|
||||
analysisData.setGenderData(sorted.getGenders());
|
||||
analysisData.setPunchCardData(PunchCardGraphCreator.generateDataArray(sorted.getSessiondata()));
|
||||
analysisData.setSessionDistributionData(SessionLengthDistributionGraphCreator.generateDataArraySessions(sorted.getSessiondata()));
|
||||
analysisData.setPlaytimeDistributionData(SessionLengthDistributionGraphCreator.generateDataArray(sorted.getPlaytimes().values()));
|
||||
@ -156,6 +155,7 @@ public class Analysis {
|
||||
if (Settings.ANALYSIS_LOG_FINISHED.isTrue()) {
|
||||
Log.info(Phrase.ANALYSIS_COMPLETE + "");
|
||||
}
|
||||
// LocationAnalysis.performAnalysis(analysisData, plugin.getDB());
|
||||
if (Settings.ANALYSIS_EXPORT.isTrue()) {
|
||||
ExportUtility.export(plugin, analysisData, rawData);
|
||||
}
|
||||
@ -227,25 +227,11 @@ public class Analysis {
|
||||
sorted.addTotalDeaths(uData.getDeaths());
|
||||
List<SessionData> sessions = uData.getSessions();
|
||||
if (!sessions.isEmpty()) {
|
||||
sorted.getSessiondata().addAll(sessions);
|
||||
sorted.addSessions(uData.getUuid(), sessions);
|
||||
}
|
||||
sorted.getRegistered().add(uData.getRegistered());
|
||||
sorted.addGeoloc(demData.getGeoLocation());
|
||||
uData.stopAccessing();
|
||||
Gender gender = demData.getGender();
|
||||
if (null != gender) {
|
||||
switch (gender) {
|
||||
case MALE:
|
||||
sorted.addToGender(0, 1);
|
||||
break;
|
||||
case FEMALE:
|
||||
sorted.addToGender(1, 1);
|
||||
break;
|
||||
default:
|
||||
sorted.addToGender(2, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
Benchmark.stop("Analysis Fill Dataset");
|
||||
return sorted;
|
||||
@ -300,7 +286,7 @@ public class Analysis {
|
||||
Benchmark.stop("Analysis GMVisualization");
|
||||
}
|
||||
|
||||
private void createPlayerActivityGraphs(AnalysisData data, List<SessionData> sData, List<Long> registered) {
|
||||
private void createPlayerActivityGraphs(AnalysisData data, List<SessionData> sData, List<Long> registered, Map<UUID, List<SessionData>> sortedSData) {
|
||||
long now = new Date().toInstant().getEpochSecond() * (long) 1000;
|
||||
|
||||
long scaleDay = 86400 * 1000;
|
||||
@ -312,6 +298,19 @@ public class Analysis {
|
||||
data.setNewPlayersWeek(AnalysisUtils.getNewPlayers(registered, scaleWeek, now));
|
||||
data.setNewPlayersMonth(AnalysisUtils.getNewPlayers(registered, scaleMonth, now));
|
||||
|
||||
Benchmark.start("Analysis Unique/day");
|
||||
data.setAvgUniqJoins(AnalysisUtils.getUniqueJoinsPerDay(sortedSData, -1));
|
||||
data.setAvgUniqJoinsDay(AnalysisUtils.getUniqueJoinsPerDay(sortedSData, scaleDay));
|
||||
data.setAvgUniqJoinsWeek(AnalysisUtils.getUniqueJoinsPerDay(sortedSData, scaleWeek));
|
||||
data.setAvgUniqJoinsMonth(AnalysisUtils.getUniqueJoinsPerDay(sortedSData, scaleMonth));
|
||||
Benchmark.stop("Analysis Unique/day");
|
||||
|
||||
Benchmark.start("Analysis Unique");
|
||||
data.setUniqueJoinsDay(AnalysisUtils.getUniqueJoins(sortedSData, scaleDay));
|
||||
data.setUniqueJoinsWeek(AnalysisUtils.getUniqueJoins(sortedSData, scaleWeek));
|
||||
data.setUniqueJoinsMonth(AnalysisUtils.getUniqueJoins(sortedSData, scaleMonth));
|
||||
Benchmark.stop("Analysis Unique");
|
||||
|
||||
List<SessionData> sessions = sData.stream()
|
||||
.filter(session -> (session != null))
|
||||
.filter(session -> session.isValid())
|
||||
|
@ -1,8 +1,13 @@
|
||||
package main.java.com.djrapitops.plan.utilities.analysis;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@ -11,7 +16,6 @@ import main.java.com.djrapitops.plan.Settings;
|
||||
import main.java.com.djrapitops.plan.data.SessionData;
|
||||
import main.java.com.djrapitops.plan.data.additional.AnalysisType;
|
||||
import main.java.com.djrapitops.plan.data.additional.PluginData;
|
||||
import main.java.com.djrapitops.plan.utilities.Benchmark;
|
||||
import main.java.com.djrapitops.plan.utilities.FormatUtils;
|
||||
import main.java.com.djrapitops.plan.utilities.MiscUtils;
|
||||
|
||||
@ -65,7 +69,6 @@ public class AnalysisUtils {
|
||||
* @return
|
||||
*/
|
||||
public static int getNewPlayers(List<Long> registered, long scale, long now) {
|
||||
Benchmark.start("Get new players for "+registered.size()+" "+scale+" | ");
|
||||
int newPlayers = 0;
|
||||
if (!registered.isEmpty()) {
|
||||
newPlayers = registered.stream()
|
||||
@ -74,7 +77,6 @@ public class AnalysisUtils {
|
||||
.map((_item) -> 1).reduce(newPlayers, Integer::sum);
|
||||
}
|
||||
// Filters out register dates before scale
|
||||
Benchmark.stop("Get new players for "+registered.size()+" "+scale+" | ");
|
||||
return newPlayers;
|
||||
}
|
||||
|
||||
@ -223,4 +225,74 @@ public class AnalysisUtils {
|
||||
Log.toLog("com.djrapitops.plan.utilities.AnalysisUtils", e);
|
||||
return source.parseContainer("", "Exception during calculation.");
|
||||
}
|
||||
|
||||
public static Integer getUniqueJoins(Map<UUID, List<SessionData>> sessions, long scale) {
|
||||
long now = MiscUtils.getTime();
|
||||
long nowMinusScale = now - scale;
|
||||
Set<UUID> uniqueJoins = new HashSet<>();
|
||||
sessions.keySet().stream().forEach((uuid) -> {
|
||||
List<SessionData> s = sessions.get(uuid);
|
||||
for (SessionData session : s) {
|
||||
if (session.getSessionStart() < nowMinusScale) {
|
||||
continue;
|
||||
}
|
||||
uniqueJoins.add(uuid);
|
||||
}
|
||||
});
|
||||
return uniqueJoins.size();
|
||||
}
|
||||
|
||||
public static Integer getUniqueJoinsPerDay(Map<UUID, List<SessionData>> sessions, long scale) {
|
||||
Map<Integer, Set<UUID>> uniqueJoins = new HashMap<>();
|
||||
long now = MiscUtils.getTime();
|
||||
long nowMinusScale = now - scale;
|
||||
sessions.keySet().stream().forEach((uuid) -> {
|
||||
List<SessionData> s = sessions.get(uuid);
|
||||
for (SessionData session : s) {
|
||||
if (scale != -1) {
|
||||
if (session.getSessionStart() < nowMinusScale) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
int day = getDayOfYear(session);
|
||||
if (!uniqueJoins.containsKey(day)) {
|
||||
uniqueJoins.put(day, new HashSet<>());
|
||||
}
|
||||
uniqueJoins.get(day).add(uuid);
|
||||
}
|
||||
});
|
||||
int total = MathUtils.sumInt(uniqueJoins.values().stream().map(s -> s.size()));
|
||||
int size = uniqueJoins.keySet().size();
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
return total / size;
|
||||
}
|
||||
|
||||
public static List<int[]> getDaysAndHours(List<Long> sessionStarts) {
|
||||
List<int[]> daysAndHours = sessionStarts.stream().map((Long start) -> {
|
||||
Calendar day = Calendar.getInstance();
|
||||
day.setTimeInMillis(start);
|
||||
int hourOfDay = day.get(Calendar.HOUR_OF_DAY);
|
||||
int dayOfWeek = day.get(Calendar.DAY_OF_WEEK) - 2;
|
||||
if (hourOfDay == 24) {
|
||||
hourOfDay = 0;
|
||||
dayOfWeek += 1;
|
||||
}
|
||||
if (dayOfWeek > 6) {
|
||||
dayOfWeek = 0;
|
||||
}
|
||||
if (dayOfWeek < 0) {
|
||||
dayOfWeek = 6;
|
||||
}
|
||||
return new int[]{dayOfWeek, hourOfDay};
|
||||
}).collect(Collectors.toList());
|
||||
return daysAndHours;
|
||||
}
|
||||
|
||||
private static int getDayOfYear(SessionData session) {
|
||||
Calendar day = Calendar.getInstance();
|
||||
day.setTimeInMillis(session.getSessionStart());
|
||||
return day.get(Calendar.DAY_OF_YEAR);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,96 @@
|
||||
package main.java.com.djrapitops.plan.utilities.analysis.locations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import main.java.com.djrapitops.plan.Log;
|
||||
import main.java.com.djrapitops.plan.data.AnalysisData;
|
||||
import main.java.com.djrapitops.plan.database.Database;
|
||||
import main.java.com.djrapitops.plan.utilities.Benchmark;
|
||||
import main.java.com.djrapitops.plan.utilities.analysis.MathUtils;
|
||||
import static org.bukkit.Bukkit.getWorlds;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rsl1122
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public class LocationAnalysis {
|
||||
|
||||
public static void performAnalysis(AnalysisData data, Database db) {
|
||||
Benchmark.start("Location Analysis");
|
||||
try {
|
||||
Map<Integer, List<Location>> playerLocations = db.getLocationsTable().getAllLocations(getWorlds().stream().collect(Collectors.toMap(w -> w.getName(), Function.identity())));
|
||||
List<Location> locations = new ArrayList<>();
|
||||
for (Integer id : playerLocations.keySet()) {
|
||||
locations.addAll(playerLocations.get(id));
|
||||
}
|
||||
Map<String, Map<Point, Integer>> worldPoints = getWorldPoints(locations);
|
||||
for (String world : worldPoints.keySet()) {
|
||||
Map<Point, Integer> worldLocs = worldPoints.get(world);
|
||||
Set<Point> frequentPoints = getFrequentPoints(worldLocs);
|
||||
Log.debug(frequentPoints.toString());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Log.toLog("LocationAnalysis.performAnalysis", ex);
|
||||
}
|
||||
Benchmark.stop("Location Analysis");
|
||||
}
|
||||
|
||||
public static Map<Point, Object> cluster(Collection<Point> freqPoints, Collection<Point> allPoints) {
|
||||
Benchmark.start("LocAnalysis cluster");
|
||||
allPoints.removeAll(freqPoints);
|
||||
for (Point point : freqPoints) {
|
||||
Set<Point> cluster = allPoints.stream().filter(p -> distance(point, p) < 5).collect(Collectors.toSet());
|
||||
}
|
||||
Benchmark.stop("LocAnalysis cluster");
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
public static Set<Point> getFrequentPoints(Map<Point, Integer> points) {
|
||||
Benchmark.start("LocAnalysis getFrequentPoints");
|
||||
if (points.isEmpty()) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
double averageFreq = MathUtils.averageInt(points.values().stream());
|
||||
Set<Point> freqPoints = points.entrySet().stream().filter(e -> e.getValue() > averageFreq).map(e -> e.getKey()).collect(Collectors.toSet());
|
||||
Benchmark.stop("LocAnalysis getFrequentPoints");
|
||||
return freqPoints;
|
||||
}
|
||||
|
||||
public static Map<String, Map<Point, Integer>> getWorldPoints(Collection<Location> locations) {
|
||||
Benchmark.start("LocAnalysis getWorldPoints");
|
||||
Map<String, Map<Point, Integer>> pointMap = new HashMap<>();
|
||||
for (Location location : locations) {
|
||||
World world = location.getWorld();
|
||||
if (world == null) {
|
||||
continue;
|
||||
}
|
||||
String worldName = world.getName();
|
||||
if (!pointMap.containsKey(worldName)) {
|
||||
pointMap.put(worldName, new HashMap<>());
|
||||
}
|
||||
Map<Point, Integer> numOfLocs = pointMap.get(worldName);
|
||||
Point point = new Point(location.getBlockX(), location.getBlockZ());
|
||||
if (!numOfLocs.containsKey(point)) {
|
||||
numOfLocs.put(point, 0);
|
||||
}
|
||||
numOfLocs.replace(point, numOfLocs.get(point) + 1);
|
||||
}
|
||||
Benchmark.stop("LocAnalysis getWorldPoints");
|
||||
return pointMap;
|
||||
}
|
||||
|
||||
public static double distance(Point one, Point two) {
|
||||
return Math.hypot(one.getX() - two.getX(), one.getY() - one.getY());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package main.java.com.djrapitops.plan.utilities.analysis.locations;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Risto
|
||||
*/
|
||||
public class Point {
|
||||
|
||||
final private int x;
|
||||
final private int y;
|
||||
|
||||
public Point(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 3;
|
||||
hash = 97 * hash + this.x;
|
||||
hash = 97 * hash + this.y;
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final Point other = (Point) obj;
|
||||
if (this.x != other.x) {
|
||||
return false;
|
||||
}
|
||||
if (this.y != other.y) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "P[x:" + x + "|y:" + y + ']';
|
||||
}
|
||||
|
||||
}
|
@ -336,7 +336,8 @@ header p {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<canvas id="playerChartDay" width="1000" height="350" style="width: 95%;"></canvas><br/>
|
||||
<canvas id="playerChartDay" width="1000" height="350" style="width: 95%;"></canvas>
|
||||
<p><i class="fa fa-user-circle" aria-hidden="true"></i> Unique Players: %uniquejoinsday% | <i class="fa fa-user-circle-o" aria-hidden="true"></i> Unique/Day: %avguniquejoinsday%</p>
|
||||
</div>
|
||||
<div class=" box column" style="order: 5;">
|
||||
<p class="header-label" style="color: #267F00; "><i class="fa fa-calendar-check-o" aria-hidden="true"></i><span class="header-text"> Recent Logins</span></p>
|
||||
@ -368,6 +369,7 @@ header p {
|
||||
<p><i class="fa fa-clock-o" aria-hidden="true"></i> Total Playtime: %totalplaytime% | <i class="fa fa-clock-o" aria-hidden="true"></i> Player Average: %avgplaytime%<br/>
|
||||
<i class="fa fa-clock-o" aria-hidden="true"></i> Average Session Length: %sessionaverage%<br/>
|
||||
<i class="fa fa-calendar-plus-o" aria-hidden="true"></i> Total Login times: %totallogins%<br/>
|
||||
<i class="fa fa-user-circle-o" aria-hidden="true"></i> Average Unique Players/Day: %avguniquejoins%<br>
|
||||
<b><i class="fa fa-crosshairs" aria-hidden="true"></i></b> 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%</p>
|
||||
</div>
|
||||
<div class=" box column">
|
||||
@ -452,6 +454,7 @@ header p {
|
||||
</div>
|
||||
</div>
|
||||
<canvas id="playerChartDay2" width="1000" height="350" style="width: 95%;"></canvas>
|
||||
<p><i class="fa fa-user-circle" aria-hidden="true"></i> Unique Players: %uniquejoinsday% | <i class="fa fa-user-circle-o" aria-hidden="true"></i> Unique/Day: %avguniquejoinsday%</p>
|
||||
</div>
|
||||
<div class=" box column">
|
||||
<div class="headerbox">
|
||||
@ -473,6 +476,7 @@ header p {
|
||||
</div>
|
||||
</div>
|
||||
<canvas id="playerChartWeek" width="1000" height="350" style="width: 95%;"></canvas>
|
||||
<p><i class="fa fa-user-circle" aria-hidden="true"></i> Unique Players: %uniquejoinsweek% | <i class="fa fa-user-circle-o" aria-hidden="true"></i> Unique/Day: %avguniquejoinsweek%</p>
|
||||
</div>
|
||||
<div class=" box column">
|
||||
<div class="headerbox">
|
||||
@ -494,6 +498,7 @@ header p {
|
||||
</div>
|
||||
</div>
|
||||
<canvas id="playerChartMonth" width="1000" height="350" style="width: 95%;"></canvas>
|
||||
<p><i class="fa fa-user-circle" aria-hidden="true"></i> Unique Playes: %uniquejoinsmonth% | <i class="fa fa-user-circle-o" aria-hidden="true"></i> Unique/Day: %avguniquejoinsmonth%</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
@ -585,7 +590,7 @@ header p {
|
||||
<div class=" 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 class="header-label"><i class="fa fa-braille" aria-hidden="true"></i><span class="header-text"> PunchCard - Last 30d</span></div>
|
||||
</div>
|
||||
<div class="infobox" >
|
||||
<div class="info-icon">
|
||||
@ -730,7 +735,7 @@ header p {
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab">
|
||||
<div class="row">
|
||||
<div class="row" style="width: 100%;">
|
||||
<div class=" box column">
|
||||
<div class="headerbox">
|
||||
<div class="header-icon">
|
||||
@ -753,42 +758,6 @@ header p {
|
||||
<div style="width:100%;"><div id="cloropleth"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class=" box column">
|
||||
<div class="headerbox">
|
||||
<div class="header-icon">
|
||||
<div class="header-label"><i class="fa fa-male" aria-hidden="true"></i><i class="fa fa-female" aria-hidden="true"></i><span class="header-text"> Gender Distribution</span></div>
|
||||
</div>
|
||||
<div class="infobox" style="width: 22%; background-color: %genderfcolor%">
|
||||
<div class="info-icon">
|
||||
<i class="fa fa-female" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div class="info-text">
|
||||
<div class="info-number">
|
||||
%genderfemale%
|
||||
</div>
|
||||
<div class="info-label">
|
||||
Female
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="infobox" style="width: 22%; background-color: %gendermcolor%">
|
||||
<div class="info-icon">
|
||||
<i class="fa fa-male" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div class="info-text">
|
||||
<div class="info-number">
|
||||
%gendermale%
|
||||
</div>
|
||||
<div class="info-label">
|
||||
Male
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<canvas id="genderPie" width="1000" height="600" style="width: 95%;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab" style="display: block;">
|
||||
%plugins%
|
||||
@ -932,7 +901,6 @@ function countUpTimer() {
|
||||
var ctxmonth = document.getElementById("playerChartMonth");
|
||||
var ctxactivitypie = document.getElementById("activityPie");
|
||||
var ctxgmpie = document.getElementById("gmPie");
|
||||
var ctxgenderpie = document.getElementById("genderPie");
|
||||
var dataday = {
|
||||
labels: %labelsday%,
|
||||
datasets: [
|
||||
@ -1055,28 +1023,6 @@ function countUpTimer() {
|
||||
}
|
||||
}
|
||||
});
|
||||
var dataGenderPie = {
|
||||
labels: %genderlabels%,
|
||||
datasets: [
|
||||
{
|
||||
data: %genderdata%,
|
||||
backgroundColor: [%gendercolors%],
|
||||
hoverBackgroundColor: [%gendercolors%]
|
||||
}
|
||||
]
|
||||
}
|
||||
var GenderPie = new Chart(ctxgenderpie, {
|
||||
type: 'doughnut',
|
||||
data: dataGenderPie,
|
||||
options: {
|
||||
legend: {
|
||||
position: 'right',
|
||||
labels: {
|
||||
padding: 7
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
var playersChartDay = new Chart(ctxday, {
|
||||
type: 'line',
|
||||
data: dataday,
|
||||
|
@ -8,6 +8,7 @@ Settings:
|
||||
LogProgressOnConsole: false
|
||||
NotifyWhenFinished: true
|
||||
MinutesPlayedUntilConsidiredActive: 10
|
||||
RemoveOutliersFromVisualization: true
|
||||
Export:
|
||||
Enabled: false
|
||||
DestinationFolder: 'Analysis Results'
|
||||
@ -69,10 +70,6 @@ Customization:
|
||||
Banned: '951800'
|
||||
Inactive: 'A9A9A9'
|
||||
JoinedOnce: '808080'
|
||||
GenderPie:
|
||||
Female: ED97E3
|
||||
Male: 7CB9D6
|
||||
Unknown: A9A9A9
|
||||
DemographicsTriggers:
|
||||
Trigger: "i'm, am, im, bin"
|
||||
Female: 'female, girl, gurl, woman, gal, mrs, she, miss, feminin, weiblich, mädchen, frau'
|
||||
|
@ -1,7 +1,7 @@
|
||||
name: Plan
|
||||
author: Rsl1122
|
||||
main: main.java.com.djrapitops.plan.Plan
|
||||
version: 3.3.0
|
||||
version: 3.4.0
|
||||
|
||||
softdepend:
|
||||
- OnTime
|
||||
|
Loading…
Reference in New Issue
Block a user