mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2024-12-27 03:27:37 +01:00
Player retention
This commit is contained in:
parent
7e420aaf91
commit
566f838a3a
@ -2,10 +2,10 @@ package com.djrapitops.plan.command.commands;
|
|||||||
|
|
||||||
import com.djrapitops.plan.PlanPlugin;
|
import com.djrapitops.plan.PlanPlugin;
|
||||||
import com.djrapitops.plan.api.exceptions.database.DBOpException;
|
import com.djrapitops.plan.api.exceptions.database.DBOpException;
|
||||||
import com.djrapitops.plan.data.calculation.ActivityIndex;
|
|
||||||
import com.djrapitops.plan.data.container.GeoInfo;
|
import com.djrapitops.plan.data.container.GeoInfo;
|
||||||
import com.djrapitops.plan.data.store.containers.PlayerContainer;
|
import com.djrapitops.plan.data.store.containers.PlayerContainer;
|
||||||
import com.djrapitops.plan.data.store.keys.PlayerKeys;
|
import com.djrapitops.plan.data.store.keys.PlayerKeys;
|
||||||
|
import com.djrapitops.plan.data.store.mutators.ActivityIndex;
|
||||||
import com.djrapitops.plan.data.store.mutators.GeoInfoMutator;
|
import com.djrapitops.plan.data.store.mutators.GeoInfoMutator;
|
||||||
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
|
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
|
||||||
import com.djrapitops.plan.data.store.mutators.formatting.Formatter;
|
import com.djrapitops.plan.data.store.mutators.formatting.Formatter;
|
||||||
|
@ -4,11 +4,11 @@
|
|||||||
*/
|
*/
|
||||||
package com.djrapitops.plan.data;
|
package com.djrapitops.plan.data;
|
||||||
|
|
||||||
import com.djrapitops.plan.data.calculation.ActivityIndex;
|
|
||||||
import com.djrapitops.plan.data.container.Action;
|
import com.djrapitops.plan.data.container.Action;
|
||||||
import com.djrapitops.plan.data.container.GeoInfo;
|
import com.djrapitops.plan.data.container.GeoInfo;
|
||||||
import com.djrapitops.plan.data.container.PlayerKill;
|
import com.djrapitops.plan.data.container.PlayerKill;
|
||||||
import com.djrapitops.plan.data.container.Session;
|
import com.djrapitops.plan.data.container.Session;
|
||||||
|
import com.djrapitops.plan.data.store.mutators.ActivityIndex;
|
||||||
import com.djrapitops.plan.data.time.WorldTimes;
|
import com.djrapitops.plan.data.time.WorldTimes;
|
||||||
import com.djrapitops.plan.system.info.server.ServerInfo;
|
import com.djrapitops.plan.system.info.server.ServerInfo;
|
||||||
import com.djrapitops.plan.utilities.comparators.ActionComparator;
|
import com.djrapitops.plan.utilities.comparators.ActionComparator;
|
||||||
|
@ -4,10 +4,10 @@ import com.djrapitops.plan.PlanPlugin;
|
|||||||
import com.djrapitops.plan.data.PlayerProfile;
|
import com.djrapitops.plan.data.PlayerProfile;
|
||||||
import com.djrapitops.plan.data.ServerProfile;
|
import com.djrapitops.plan.data.ServerProfile;
|
||||||
import com.djrapitops.plan.data.container.Session;
|
import com.djrapitops.plan.data.container.Session;
|
||||||
import com.djrapitops.plan.data.container.StickyData;
|
|
||||||
import com.djrapitops.plan.data.container.TPS;
|
import com.djrapitops.plan.data.container.TPS;
|
||||||
import com.djrapitops.plan.data.element.AnalysisContainer;
|
import com.djrapitops.plan.data.element.AnalysisContainer;
|
||||||
import com.djrapitops.plan.data.plugin.PluginData;
|
import com.djrapitops.plan.data.plugin.PluginData;
|
||||||
|
import com.djrapitops.plan.data.store.mutators.RetentionData;
|
||||||
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
|
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
|
||||||
import com.djrapitops.plan.data.store.mutators.formatting.Formatters;
|
import com.djrapitops.plan.data.store.mutators.formatting.Formatters;
|
||||||
import com.djrapitops.plan.data.time.WorldTimes;
|
import com.djrapitops.plan.data.time.WorldTimes;
|
||||||
@ -55,7 +55,7 @@ public class AnalysisData extends RawData {
|
|||||||
private long refreshDate;
|
private long refreshDate;
|
||||||
|
|
||||||
private Map<String, Long> analyzedValues;
|
private Map<String, Long> analyzedValues;
|
||||||
private Set<StickyData> stickyMonthData;
|
private Set<RetentionData> stickyMonthData;
|
||||||
private List<PlayerProfile> players;
|
private List<PlayerProfile> players;
|
||||||
|
|
||||||
public AnalysisData() {
|
public AnalysisData() {
|
||||||
@ -250,7 +250,7 @@ public class AnalysisData extends RawData {
|
|||||||
got("stuckPerM", stuckPerM);
|
got("stuckPerM", stuckPerM);
|
||||||
got("stuckPerW", stuckPerW);
|
got("stuckPerW", stuckPerW);
|
||||||
|
|
||||||
stickyMonthData = newMonth.stream().map(StickyData::new).distinct().collect(Collectors.toSet());
|
stickyMonthData = newMonth.stream().map(RetentionData::new).distinct().collect(Collectors.toSet());
|
||||||
|
|
||||||
addValue("playersStuckMonth", stuckPerM);
|
addValue("playersStuckMonth", stuckPerM);
|
||||||
addValue("playersStuckWeek", stuckPerW);
|
addValue("playersStuckWeek", stuckPerW);
|
||||||
@ -288,16 +288,16 @@ public class AnalysisData extends RawData {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<StickyData> stuck = stuckAfterMonth.stream().map(StickyData::new).collect(Collectors.toList());
|
List<RetentionData> stuck = stuckAfterMonth.stream().map(RetentionData::new).collect(Collectors.toList());
|
||||||
List<StickyData> nonStuck = notStuckAfterMonth.stream().map(StickyData::new).collect(Collectors.toList());
|
List<RetentionData> nonStuck = notStuckAfterMonth.stream().map(RetentionData::new).collect(Collectors.toList());
|
||||||
|
|
||||||
StickyData avgStuck = AnalysisUtils.average(stuck);
|
RetentionData avgStuck = AnalysisUtils.average(stuck);
|
||||||
StickyData avgNonStuck = AnalysisUtils.average(nonStuck);
|
RetentionData avgNonStuck = AnalysisUtils.average(nonStuck);
|
||||||
|
|
||||||
int stuckPerD = 0;
|
int stuckPerD = 0;
|
||||||
for (PlayerProfile player : newDay) {
|
for (PlayerProfile player : newDay) {
|
||||||
StickyData stickyData = new StickyData(player);
|
RetentionData retentionData = new RetentionData(player);
|
||||||
if (stickyData.distance(avgStuck) < stickyData.distance(avgNonStuck)) {
|
if (retentionData.distance(avgStuck) < retentionData.distance(avgNonStuck)) {
|
||||||
stuckPerD++;
|
stuckPerD++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -403,7 +403,7 @@ public class AnalysisData extends RawData {
|
|||||||
return analyzedValues.getOrDefault(key, 0L);
|
return analyzedValues.getOrDefault(key, 0L);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<StickyData> getStickyMonthData() {
|
public Set<RetentionData> getStickyMonthData() {
|
||||||
return stickyMonthData;
|
return stickyMonthData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@ package com.djrapitops.plan.data.calculation;
|
|||||||
|
|
||||||
import com.djrapitops.plan.data.PlayerProfile;
|
import com.djrapitops.plan.data.PlayerProfile;
|
||||||
import com.djrapitops.plan.data.ServerProfile;
|
import com.djrapitops.plan.data.ServerProfile;
|
||||||
import com.djrapitops.plan.data.container.StickyData;
|
|
||||||
import com.djrapitops.plan.data.container.TPS;
|
import com.djrapitops.plan.data.container.TPS;
|
||||||
|
import com.djrapitops.plan.data.store.mutators.RetentionData;
|
||||||
import com.djrapitops.plan.data.store.mutators.formatting.Formatter;
|
import com.djrapitops.plan.data.store.mutators.formatting.Formatter;
|
||||||
import com.djrapitops.plan.data.store.mutators.formatting.Formatters;
|
import com.djrapitops.plan.data.store.mutators.formatting.Formatters;
|
||||||
import com.djrapitops.plan.system.settings.Settings;
|
import com.djrapitops.plan.system.settings.Settings;
|
||||||
@ -128,7 +128,7 @@ public class HealthNotes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void newPlayerNote() {
|
private void newPlayerNote() {
|
||||||
double avgOnlineOnRegister = MathUtils.averageDouble(analysisData.getStickyMonthData().stream().map(StickyData::getOnlineOnJoin));
|
double avgOnlineOnRegister = MathUtils.averageDouble(analysisData.getStickyMonthData().stream().map(RetentionData::getOnlineOnJoin));
|
||||||
if (avgOnlineOnRegister >= 1) {
|
if (avgOnlineOnRegister >= 1) {
|
||||||
notes.add("<p>" + Html.GREEN_THUMB.parse() + " New Players have players to play with when they join ("
|
notes.add("<p>" + Html.GREEN_THUMB.parse() + " New Players have players to play with when they join ("
|
||||||
+ FormatUtils.cutDecimals(avgOnlineOnRegister) + " on average)</p>");
|
+ FormatUtils.cutDecimals(avgOnlineOnRegister) + " on average)</p>");
|
||||||
|
@ -1,108 +0,0 @@
|
|||||||
/*
|
|
||||||
* Licence is provided in the jar as license.yml also here:
|
|
||||||
* https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/license.yml
|
|
||||||
*/
|
|
||||||
package com.djrapitops.plan.data.container;
|
|
||||||
|
|
||||||
import com.djrapitops.plan.data.Actions;
|
|
||||||
import com.djrapitops.plan.data.PlayerProfile;
|
|
||||||
import com.djrapitops.plugin.api.TimeAmount;
|
|
||||||
import com.google.common.base.Objects;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class StickyData {
|
|
||||||
private final double activityIndex;
|
|
||||||
private Double messagesSent;
|
|
||||||
private Double onlineOnJoin;
|
|
||||||
|
|
||||||
public StickyData(PlayerProfile player) {
|
|
||||||
activityIndex = player.getActivityIndex(player.getRegistered() + TimeAmount.DAY.ms()).getValue();
|
|
||||||
loadActionVariables(player.getActions());
|
|
||||||
}
|
|
||||||
|
|
||||||
public StickyData(double activityIndex, Double messagesSent, Double onlineOnJoin) {
|
|
||||||
this.activityIndex = activityIndex;
|
|
||||||
this.messagesSent = messagesSent;
|
|
||||||
this.onlineOnJoin = onlineOnJoin;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadActionVariables(List<Action> actions) {
|
|
||||||
for (Action action : actions) {
|
|
||||||
try {
|
|
||||||
if (messagesSent == null && action.getDoneAction() == Actions.FIRST_LOGOUT) {
|
|
||||||
messagesSent = (double) loadSentMessages(action);
|
|
||||||
}
|
|
||||||
if (onlineOnJoin == null && action.getDoneAction() == Actions.FIRST_SESSION) {
|
|
||||||
onlineOnJoin = (double) loadOnlineOnJoin(action);
|
|
||||||
}
|
|
||||||
} catch (IllegalArgumentException ignore) {
|
|
||||||
/* continue */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setDefaultValuesIfNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setDefaultValuesIfNull() {
|
|
||||||
if (messagesSent == null) {
|
|
||||||
messagesSent = 0.0;
|
|
||||||
}
|
|
||||||
if (onlineOnJoin == null) {
|
|
||||||
onlineOnJoin = 0.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int loadOnlineOnJoin(Action action) {
|
|
||||||
String additionalInfo = action.getAdditionalInfo();
|
|
||||||
String[] split = additionalInfo.split(" ");
|
|
||||||
if (split.length == 3) {
|
|
||||||
return Integer.parseInt(split[1]);
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("Improper Action");
|
|
||||||
}
|
|
||||||
|
|
||||||
private int loadSentMessages(Action action) {
|
|
||||||
String additionalInfo = action.getAdditionalInfo();
|
|
||||||
String[] split = additionalInfo.split(": ");
|
|
||||||
if (split.length == 2) {
|
|
||||||
return Integer.parseInt(split[1]);
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("Improper Action");
|
|
||||||
}
|
|
||||||
|
|
||||||
public double distance(StickyData data) {
|
|
||||||
double num = 0;
|
|
||||||
num += Math.abs(data.activityIndex - activityIndex) * 2.0;
|
|
||||||
num += Math.abs(data.onlineOnJoin - onlineOnJoin) / 10.0;
|
|
||||||
num += Math.abs(data.messagesSent - messagesSent) / 10.0;
|
|
||||||
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
StickyData that = (StickyData) o;
|
|
||||||
return Double.compare(that.activityIndex, activityIndex) == 0 &&
|
|
||||||
Objects.equal(messagesSent, that.messagesSent) &&
|
|
||||||
Objects.equal(onlineOnJoin, that.onlineOnJoin);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hashCode(activityIndex, messagesSent, onlineOnJoin);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getOnlineOnJoin() {
|
|
||||||
return onlineOnJoin;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getActivityIndex() {
|
|
||||||
return activityIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Double getMessagesSent() {
|
|
||||||
return messagesSent;
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,10 +5,7 @@ import com.djrapitops.plan.data.store.Key;
|
|||||||
import com.djrapitops.plan.data.store.keys.AnalysisKeys;
|
import com.djrapitops.plan.data.store.keys.AnalysisKeys;
|
||||||
import com.djrapitops.plan.data.store.keys.PlayerKeys;
|
import com.djrapitops.plan.data.store.keys.PlayerKeys;
|
||||||
import com.djrapitops.plan.data.store.keys.ServerKeys;
|
import com.djrapitops.plan.data.store.keys.ServerKeys;
|
||||||
import com.djrapitops.plan.data.store.mutators.CommandUseMutator;
|
import com.djrapitops.plan.data.store.mutators.*;
|
||||||
import com.djrapitops.plan.data.store.mutators.PlayersMutator;
|
|
||||||
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
|
|
||||||
import com.djrapitops.plan.data.store.mutators.TPSMutator;
|
|
||||||
import com.djrapitops.plan.data.store.mutators.formatting.Formatters;
|
import com.djrapitops.plan.data.store.mutators.formatting.Formatters;
|
||||||
import com.djrapitops.plan.data.time.WorldTimes;
|
import com.djrapitops.plan.data.time.WorldTimes;
|
||||||
import com.djrapitops.plan.system.database.databases.Database;
|
import com.djrapitops.plan.system.database.databases.Database;
|
||||||
@ -32,10 +29,7 @@ import com.djrapitops.plan.utilities.html.tables.PlayersTable;
|
|||||||
import com.djrapitops.plan.utilities.html.tables.ServerSessionTable;
|
import com.djrapitops.plan.utilities.html.tables.ServerSessionTable;
|
||||||
import com.djrapitops.plugin.api.TimeAmount;
|
import com.djrapitops.plugin.api.TimeAmount;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -172,6 +166,28 @@ public class AnalysisContainer extends DataContainer {
|
|||||||
putSupplier(AnalysisKeys.AVG_PLAYERS_NEW_DAY, () -> getUnsafe(uniqueDay).newPerDay());
|
putSupplier(AnalysisKeys.AVG_PLAYERS_NEW_DAY, () -> getUnsafe(uniqueDay).newPerDay());
|
||||||
putSupplier(AnalysisKeys.AVG_PLAYERS_NEW_WEEK, () -> getUnsafe(uniqueWeek).newPerDay());
|
putSupplier(AnalysisKeys.AVG_PLAYERS_NEW_WEEK, () -> getUnsafe(uniqueWeek).newPerDay());
|
||||||
putSupplier(AnalysisKeys.AVG_PLAYERS_NEW_MONTH, () -> getUnsafe(uniqueMonth).newPerDay());
|
putSupplier(AnalysisKeys.AVG_PLAYERS_NEW_MONTH, () -> getUnsafe(uniqueMonth).newPerDay());
|
||||||
|
|
||||||
|
Key<Integer> retentionDay = new Key<>(Integer.class, "RETENTION_DAY");
|
||||||
|
// compareAndFindThoseLikelyToBeRetained can throw exception.
|
||||||
|
putSupplier(retentionDay, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR).compareAndFindThoseLikelyToBeRetained(
|
||||||
|
getUnsafe(newDay).all(), getUnsafe(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO),
|
||||||
|
getUnsafe(AnalysisKeys.PLAYERS_ONLINE_RESOLVER)
|
||||||
|
).count()
|
||||||
|
);
|
||||||
|
putSupplier(AnalysisKeys.PLAYERS_RETAINED_DAY, () -> {
|
||||||
|
try {
|
||||||
|
return getUnsafe(retentionDay);
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
putSupplier(AnalysisKeys.PLAYERS_RETAINED_DAY_PERC, () -> {
|
||||||
|
try {
|
||||||
|
return Formatters.percentage().apply(1.0 * getUnsafe(retentionDay) / getUnsafe(AnalysisKeys.PLAYERS_NEW_DAY));
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
return "Not enough data";
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addSessionSuppliers() {
|
private void addSessionSuppliers() {
|
||||||
@ -228,6 +244,24 @@ public class AnalysisContainer extends DataContainer {
|
|||||||
putSupplier(AnalysisKeys.AVG_PLAYERS_DAY, () -> getUnsafe(sessionsDay).toUniqueJoinsPerDay());
|
putSupplier(AnalysisKeys.AVG_PLAYERS_DAY, () -> getUnsafe(sessionsDay).toUniqueJoinsPerDay());
|
||||||
putSupplier(AnalysisKeys.AVG_PLAYERS_WEEK, () -> getUnsafe(sessionsWeek).toUniqueJoinsPerDay());
|
putSupplier(AnalysisKeys.AVG_PLAYERS_WEEK, () -> getUnsafe(sessionsWeek).toUniqueJoinsPerDay());
|
||||||
putSupplier(AnalysisKeys.AVG_PLAYERS_MONTH, () -> getUnsafe(sessionsMonth).toUniqueJoinsPerDay());
|
putSupplier(AnalysisKeys.AVG_PLAYERS_MONTH, () -> getUnsafe(sessionsMonth).toUniqueJoinsPerDay());
|
||||||
|
putSupplier(AnalysisKeys.PLAYERS_RETAINED_WEEK, () ->
|
||||||
|
PlayersMutator.copyOf(getUnsafe(AnalysisKeys.PLAYERS_MUTATOR)).filterRetained(
|
||||||
|
getUnsafe(AnalysisKeys.ANALYSIS_TIME_WEEK_AGO),
|
||||||
|
getUnsafe(AnalysisKeys.ANALYSIS_TIME)
|
||||||
|
).count()
|
||||||
|
);
|
||||||
|
putSupplier(AnalysisKeys.PLAYERS_RETAINED_MONTH, () ->
|
||||||
|
PlayersMutator.copyOf(getUnsafe(AnalysisKeys.PLAYERS_MUTATOR)).filterRetained(
|
||||||
|
getUnsafe(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO),
|
||||||
|
getUnsafe(AnalysisKeys.ANALYSIS_TIME)
|
||||||
|
).count()
|
||||||
|
);
|
||||||
|
putSupplier(AnalysisKeys.PLAYERS_RETAINED_WEEK_PERC, () -> Formatters.percentage().apply(
|
||||||
|
1.0 * getUnsafe(AnalysisKeys.PLAYERS_RETAINED_WEEK) / getUnsafe(AnalysisKeys.PLAYERS_NEW_WEEK))
|
||||||
|
);
|
||||||
|
putSupplier(AnalysisKeys.PLAYERS_RETAINED_MONTH_PERC, () -> Formatters.percentage().apply(
|
||||||
|
1.0 * getUnsafe(AnalysisKeys.PLAYERS_RETAINED_MONTH) / getUnsafe(AnalysisKeys.PLAYERS_NEW_MONTH))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addGraphSuppliers() {
|
private void addGraphSuppliers() {
|
||||||
@ -256,6 +290,13 @@ public class AnalysisContainer extends DataContainer {
|
|||||||
putSupplier(AnalysisKeys.ACTIVITY_PIE_SERIES, () ->
|
putSupplier(AnalysisKeys.ACTIVITY_PIE_SERIES, () ->
|
||||||
new ActivityPie(getUnsafe(AnalysisKeys.ACTIVITY_DATA).get(getUnsafe(AnalysisKeys.ANALYSIS_TIME))).toHighChartsSeries()
|
new ActivityPie(getUnsafe(AnalysisKeys.ACTIVITY_DATA).get(getUnsafe(AnalysisKeys.ANALYSIS_TIME))).toHighChartsSeries()
|
||||||
);
|
);
|
||||||
|
putSupplier(AnalysisKeys.PLAYERS_REGULAR, () -> {
|
||||||
|
Map<String, Set<UUID>> activityNow = getUnsafe(AnalysisKeys.ACTIVITY_DATA).get(getUnsafe(AnalysisKeys.ANALYSIS_TIME));
|
||||||
|
Set<UUID> veryActiveNow = activityNow.getOrDefault("Very Active", new HashSet<>());
|
||||||
|
Set<UUID> activeNow = activityNow.getOrDefault("Active", new HashSet<>());
|
||||||
|
Set<UUID> regularNow = activityNow.getOrDefault("Regular", new HashSet<>());
|
||||||
|
return veryActiveNow.size() + activeNow.size() + regularNow.size();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addTPSAverageSuppliers() {
|
private void addTPSAverageSuppliers() {
|
||||||
@ -273,6 +314,8 @@ public class AnalysisContainer extends DataContainer {
|
|||||||
.filterDataBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_DAY_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME))
|
.filterDataBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_DAY_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
putSupplier(AnalysisKeys.PLAYERS_ONLINE_RESOLVER, () -> new PlayersOnlineResolver(getUnsafe(AnalysisKeys.TPS_MUTATOR)));
|
||||||
|
|
||||||
putSupplier(AnalysisKeys.TPS_SPIKE_MONTH, () -> getUnsafe(tpsMonth).lowTpsSpikeCount());
|
putSupplier(AnalysisKeys.TPS_SPIKE_MONTH, () -> getUnsafe(tpsMonth).lowTpsSpikeCount());
|
||||||
putSupplier(AnalysisKeys.AVG_TPS_MONTH, () -> getUnsafe(tpsMonth).averageTPS());
|
putSupplier(AnalysisKeys.AVG_TPS_MONTH, () -> getUnsafe(tpsMonth).averageTPS());
|
||||||
putSupplier(AnalysisKeys.AVG_CPU_MONTH, () -> getUnsafe(tpsMonth).averageCPU());
|
putSupplier(AnalysisKeys.AVG_CPU_MONTH, () -> getUnsafe(tpsMonth).averageCPU());
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.djrapitops.plan.data.store.containers;
|
package com.djrapitops.plan.data.store.containers;
|
||||||
|
|
||||||
|
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DataContainer about a Player.
|
* DataContainer about a Player.
|
||||||
* <p>
|
* <p>
|
||||||
@ -9,4 +11,9 @@ package com.djrapitops.plan.data.store.containers;
|
|||||||
* @see com.djrapitops.plan.data.store.keys.PlayerKeys For Key objects.
|
* @see com.djrapitops.plan.data.store.keys.PlayerKeys For Key objects.
|
||||||
*/
|
*/
|
||||||
public class PlayerContainer extends DataContainer {
|
public class PlayerContainer extends DataContainer {
|
||||||
|
|
||||||
|
public boolean playedBetween(long after, long before) {
|
||||||
|
return SessionsMutator.forContainer(this).playedBetween(after, before);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -4,6 +4,7 @@ import com.djrapitops.plan.data.store.Key;
|
|||||||
import com.djrapitops.plan.data.store.PlaceholderKey;
|
import com.djrapitops.plan.data.store.PlaceholderKey;
|
||||||
import com.djrapitops.plan.data.store.Type;
|
import com.djrapitops.plan.data.store.Type;
|
||||||
import com.djrapitops.plan.data.store.mutators.PlayersMutator;
|
import com.djrapitops.plan.data.store.mutators.PlayersMutator;
|
||||||
|
import com.djrapitops.plan.data.store.mutators.PlayersOnlineResolver;
|
||||||
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
|
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
|
||||||
import com.djrapitops.plan.data.store.mutators.TPSMutator;
|
import com.djrapitops.plan.data.store.mutators.TPSMutator;
|
||||||
|
|
||||||
@ -86,12 +87,12 @@ public class AnalysisKeys {
|
|||||||
public static final PlaceholderKey<Integer> AVG_PLAYERS_NEW_DAY = new PlaceholderKey<>(Integer.class, "playersNewAverageDay");
|
public static final PlaceholderKey<Integer> AVG_PLAYERS_NEW_DAY = new PlaceholderKey<>(Integer.class, "playersNewAverageDay");
|
||||||
public static final PlaceholderKey<Integer> AVG_PLAYERS_NEW_WEEK = new PlaceholderKey<>(Integer.class, "playersNewAverageWeek");
|
public static final PlaceholderKey<Integer> AVG_PLAYERS_NEW_WEEK = new PlaceholderKey<>(Integer.class, "playersNewAverageWeek");
|
||||||
public static final PlaceholderKey<Integer> AVG_PLAYERS_NEW_MONTH = new PlaceholderKey<>(Integer.class, "playersNewAverageMonth");
|
public static final PlaceholderKey<Integer> AVG_PLAYERS_NEW_MONTH = new PlaceholderKey<>(Integer.class, "playersNewAverageMonth");
|
||||||
public static final PlaceholderKey<Integer> PLAYERS_STUCK_DAY = new PlaceholderKey<>(Integer.class, "playersStuckDay");
|
public static final PlaceholderKey<Integer> PLAYERS_RETAINED_DAY = new PlaceholderKey<>(Integer.class, "playersStuckDay");
|
||||||
public static final PlaceholderKey<String> PLAYERS_STUCK_DAY_PERC = new PlaceholderKey<>(String.class, "playersStuckPercDay");
|
public static final PlaceholderKey<String> PLAYERS_RETAINED_DAY_PERC = new PlaceholderKey<>(String.class, "playersStuckPercDay");
|
||||||
public static final PlaceholderKey<Integer> PLAYERS_STUCK_WEEK = new PlaceholderKey<>(Integer.class, "playersStuckWeek");
|
public static final PlaceholderKey<Integer> PLAYERS_RETAINED_WEEK = new PlaceholderKey<>(Integer.class, "playersStuckWeek");
|
||||||
public static final PlaceholderKey<String> PLAYERS_STUCK_WEEK_PERC = new PlaceholderKey<>(String.class, "playersStuckPercWeek");
|
public static final PlaceholderKey<String> PLAYERS_RETAINED_WEEK_PERC = new PlaceholderKey<>(String.class, "playersStuckPercWeek");
|
||||||
public static final PlaceholderKey<Integer> PLAYERS_STUCK_MONTH = new PlaceholderKey<>(Integer.class, "playersStuckMonth");
|
public static final PlaceholderKey<Integer> PLAYERS_RETAINED_MONTH = new PlaceholderKey<>(Integer.class, "playersStuckMonth");
|
||||||
public static final PlaceholderKey<String> PLAYERS_STUCK_MONTH_PERC = new PlaceholderKey<>(String.class, "playersStuckPercMonth");
|
public static final PlaceholderKey<String> PLAYERS_RETAINED_MONTH_PERC = new PlaceholderKey<>(String.class, "playersStuckPercMonth");
|
||||||
//
|
//
|
||||||
public static final PlaceholderKey<Integer> TPS_SPIKE_MONTH = new PlaceholderKey<>(Integer.class, "tpsSpikeMonth");
|
public static final PlaceholderKey<Integer> TPS_SPIKE_MONTH = new PlaceholderKey<>(Integer.class, "tpsSpikeMonth");
|
||||||
public static final PlaceholderKey<Integer> TPS_SPIKE_WEEK = new PlaceholderKey<>(Integer.class, "tpsSpikeWeek");
|
public static final PlaceholderKey<Integer> TPS_SPIKE_WEEK = new PlaceholderKey<>(Integer.class, "tpsSpikeWeek");
|
||||||
@ -130,6 +131,7 @@ public class AnalysisKeys {
|
|||||||
public static final Key<SessionsMutator> SESSIONS_MUTATOR = new Key<>(SessionsMutator.class, "SESSIONS_MUTATOR");
|
public static final Key<SessionsMutator> SESSIONS_MUTATOR = new Key<>(SessionsMutator.class, "SESSIONS_MUTATOR");
|
||||||
public static final Key<TPSMutator> TPS_MUTATOR = new Key<>(TPSMutator.class, "TPS_MUTATOR");
|
public static final Key<TPSMutator> TPS_MUTATOR = new Key<>(TPSMutator.class, "TPS_MUTATOR");
|
||||||
public static final Key<PlayersMutator> PLAYERS_MUTATOR = new Key<>(PlayersMutator.class, "PLAYERS_MUTATOR");
|
public static final Key<PlayersMutator> PLAYERS_MUTATOR = new Key<>(PlayersMutator.class, "PLAYERS_MUTATOR");
|
||||||
|
public static final Key<PlayersOnlineResolver> PLAYERS_ONLINE_RESOLVER = new Key<>(PlayersOnlineResolver.class, "PLAYERS_ONLINE_RESOLVER");
|
||||||
public static final Key<Long> PLAYTIME_TOTAL = new Key<>(Long.class, "PLAYTIME_TOTAL");
|
public static final Key<Long> PLAYTIME_TOTAL = new Key<>(Long.class, "PLAYTIME_TOTAL");
|
||||||
public static final Key<Long> ANALYSIS_TIME = new Key<>(Long.class, "ANALYSIS_TIME");
|
public static final Key<Long> ANALYSIS_TIME = new Key<>(Long.class, "ANALYSIS_TIME");
|
||||||
public static final Key<Long> ANALYSIS_TIME_DAY_AGO = new Key<>(Long.class, "ANALYSIS_TIME_DAY_AGO");
|
public static final Key<Long> ANALYSIS_TIME_DAY_AGO = new Key<>(Long.class, "ANALYSIS_TIME_DAY_AGO");
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.djrapitops.plan.data.calculation;
|
package com.djrapitops.plan.data.store.mutators;
|
||||||
|
|
||||||
import com.djrapitops.plan.data.store.containers.DataContainer;
|
import com.djrapitops.plan.data.store.containers.DataContainer;
|
||||||
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
|
|
||||||
import com.djrapitops.plan.system.settings.Settings;
|
import com.djrapitops.plan.system.settings.Settings;
|
||||||
import com.djrapitops.plan.utilities.FormatUtils;
|
import com.djrapitops.plan.utilities.FormatUtils;
|
||||||
import com.djrapitops.plugin.api.TimeAmount;
|
import com.djrapitops.plugin.api.TimeAmount;
|
@ -1,6 +1,5 @@
|
|||||||
package com.djrapitops.plan.data.store.mutators;
|
package com.djrapitops.plan.data.store.mutators;
|
||||||
|
|
||||||
import com.djrapitops.plan.data.calculation.ActivityIndex;
|
|
||||||
import com.djrapitops.plan.data.container.GeoInfo;
|
import com.djrapitops.plan.data.container.GeoInfo;
|
||||||
import com.djrapitops.plan.data.store.containers.DataContainer;
|
import com.djrapitops.plan.data.store.containers.DataContainer;
|
||||||
import com.djrapitops.plan.data.store.containers.PlayerContainer;
|
import com.djrapitops.plan.data.store.containers.PlayerContainer;
|
||||||
@ -8,6 +7,7 @@ import com.djrapitops.plan.data.store.keys.PlayerKeys;
|
|||||||
import com.djrapitops.plan.data.store.keys.ServerKeys;
|
import com.djrapitops.plan.data.store.keys.ServerKeys;
|
||||||
import com.djrapitops.plan.data.store.keys.SessionKeys;
|
import com.djrapitops.plan.data.store.keys.SessionKeys;
|
||||||
import com.djrapitops.plan.data.store.mutators.formatting.Formatters;
|
import com.djrapitops.plan.data.store.mutators.formatting.Formatters;
|
||||||
|
import com.djrapitops.plan.utilities.analysis.AnalysisUtils;
|
||||||
import com.djrapitops.plugin.api.TimeAmount;
|
import com.djrapitops.plugin.api.TimeAmount;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -54,6 +54,19 @@ public class PlayersMutator {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PlayersMutator filterRetained(long after, long before) {
|
||||||
|
players = players.stream()
|
||||||
|
.filter(player -> {
|
||||||
|
long backLimit = Math.max(after, player.getValue(PlayerKeys.REGISTERED).orElse(0L));
|
||||||
|
long half = backLimit + ((before - backLimit) / 2L);
|
||||||
|
SessionsMutator firstHalf = SessionsMutator.forContainer(player);
|
||||||
|
SessionsMutator secondHalf = SessionsMutator.copyOf(firstHalf);
|
||||||
|
return !firstHalf.playedBetween(backLimit, half) && !secondHalf.playedBetween(half, before);
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public List<PlayerContainer> all() {
|
public List<PlayerContainer> all() {
|
||||||
return players;
|
return players;
|
||||||
}
|
}
|
||||||
@ -117,4 +130,59 @@ public class PlayersMutator {
|
|||||||
}
|
}
|
||||||
return total / numberOfDays;
|
return total / numberOfDays;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares players in the mutator to other players in terms of player retention.
|
||||||
|
*
|
||||||
|
* @param compareTo Players to compare to.
|
||||||
|
* @param dateLimit Epoch ms back limit, if the player registered after this their value is not used.
|
||||||
|
* @return Mutator containing the players that are considered to be retained.
|
||||||
|
* @throws IllegalStateException If all players are rejected due to dateLimit.
|
||||||
|
*/
|
||||||
|
public PlayersMutator compareAndFindThoseLikelyToBeRetained(Iterable<PlayerContainer> compareTo,
|
||||||
|
long dateLimit,
|
||||||
|
PlayersOnlineResolver onlineResolver) {
|
||||||
|
Collection<PlayerContainer> retainedAfterMonth = new ArrayList<>();
|
||||||
|
Collection<PlayerContainer> notRetainedAfterMonth = new ArrayList<>();
|
||||||
|
|
||||||
|
for (PlayerContainer player : players) {
|
||||||
|
long registered = player.getValue(PlayerKeys.REGISTERED).orElse(System.currentTimeMillis());
|
||||||
|
|
||||||
|
// Discard uncertain data
|
||||||
|
if (registered > dateLimit) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
long monthAfterRegister = registered + TimeAmount.MONTH.ms();
|
||||||
|
long half = registered + (TimeAmount.MONTH.ms() / 2L);
|
||||||
|
if (player.playedBetween(registered, half) && player.playedBetween(half, monthAfterRegister)) {
|
||||||
|
retainedAfterMonth.add(player);
|
||||||
|
} else {
|
||||||
|
notRetainedAfterMonth.add(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retainedAfterMonth.isEmpty() || notRetainedAfterMonth.isEmpty()) {
|
||||||
|
throw new IllegalStateException("No players to compare to after rejecting with dateLimit");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<RetentionData> retained = retainedAfterMonth.stream()
|
||||||
|
.map(player -> new RetentionData(player, onlineResolver))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
List<RetentionData> notRetained = notRetainedAfterMonth.stream()
|
||||||
|
.map(player -> new RetentionData(player, onlineResolver))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
RetentionData avgRetained = AnalysisUtils.average(retained);
|
||||||
|
RetentionData avgNotRetained = AnalysisUtils.average(notRetained);
|
||||||
|
|
||||||
|
List<PlayerContainer> toBeRetained = new ArrayList<>();
|
||||||
|
for (PlayerContainer player : compareTo) {
|
||||||
|
RetentionData retentionData = new RetentionData(player, onlineResolver);
|
||||||
|
if (retentionData.distance(avgRetained) < retentionData.distance(avgNotRetained)) {
|
||||||
|
toBeRetained.add(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new PlayersMutator(toBeRetained);
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package com.djrapitops.plan.data.store.mutators;
|
||||||
|
|
||||||
|
import com.djrapitops.plan.utilities.html.graphs.line.Point;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves dates into players online numbers with a help of a NavigableMap.
|
||||||
|
* <p>
|
||||||
|
* Time Complexity of O(n / 2) with the use of TreeMap.
|
||||||
|
*
|
||||||
|
* @author Rsl1122
|
||||||
|
*/
|
||||||
|
public class PlayersOnlineResolver {
|
||||||
|
|
||||||
|
private final NavigableMap<Long, Integer> onlineNumberMap;
|
||||||
|
|
||||||
|
public PlayersOnlineResolver(TPSMutator mutator) {
|
||||||
|
List<Point> points = mutator.playersOnlinePoints();
|
||||||
|
onlineNumberMap = new TreeMap<>();
|
||||||
|
for (Point point : points) {
|
||||||
|
double date = point.getX();
|
||||||
|
double value = point.getY();
|
||||||
|
onlineNumberMap.put((long) date, (int) value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Integer> getOnlineOn(long date) {
|
||||||
|
Map.Entry<Long, Integer> entry = onlineNumberMap.floorEntry(date);
|
||||||
|
if (entry == null) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
return Optional.of(entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Licence is provided in the jar as license.yml also here:
|
||||||
|
* https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/license.yml
|
||||||
|
*/
|
||||||
|
package com.djrapitops.plan.data.store.mutators;
|
||||||
|
|
||||||
|
import com.djrapitops.plan.data.PlayerProfile;
|
||||||
|
import com.djrapitops.plan.data.store.containers.PlayerContainer;
|
||||||
|
import com.djrapitops.plan.data.store.keys.PlayerKeys;
|
||||||
|
import com.djrapitops.plugin.api.TimeAmount;
|
||||||
|
import com.google.common.base.Objects;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for player retention calculations.
|
||||||
|
* <p>
|
||||||
|
* Previously known as StickyData.
|
||||||
|
*
|
||||||
|
* @author Rsl1122
|
||||||
|
*/
|
||||||
|
public class RetentionData {
|
||||||
|
private final double activityIndex;
|
||||||
|
private double onlineOnJoin;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public RetentionData(PlayerProfile player) {
|
||||||
|
activityIndex = player.getActivityIndex(player.getRegistered() + TimeAmount.DAY.ms()).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public RetentionData(double activityIndex, double onlineOnJoin) {
|
||||||
|
this.activityIndex = activityIndex;
|
||||||
|
this.onlineOnJoin = onlineOnJoin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RetentionData(PlayerContainer player, PlayersOnlineResolver onlineOnJoin) {
|
||||||
|
Optional<Long> registeredValue = player.getValue(PlayerKeys.REGISTERED);
|
||||||
|
activityIndex = registeredValue
|
||||||
|
.map(registered -> new ActivityIndex(player, registered + TimeAmount.DAY.ms()).getValue())
|
||||||
|
.orElse(0.0);
|
||||||
|
this.onlineOnJoin = registeredValue
|
||||||
|
.map(registered -> onlineOnJoin.getOnlineOn(registered).orElse(-1))
|
||||||
|
.orElse(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double distance(RetentionData data) {
|
||||||
|
double num = 0;
|
||||||
|
num += Math.abs(data.activityIndex - activityIndex) * 2.0;
|
||||||
|
num += data.onlineOnJoin != -1 && onlineOnJoin != -1
|
||||||
|
? Math.abs(data.onlineOnJoin - onlineOnJoin) / 10.0
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
RetentionData that = (RetentionData) o;
|
||||||
|
return Double.compare(that.activityIndex, activityIndex) == 0 &&
|
||||||
|
Objects.equal(onlineOnJoin, that.onlineOnJoin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(activityIndex, onlineOnJoin);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getOnlineOnJoin() {
|
||||||
|
return onlineOnJoin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getActivityIndex() {
|
||||||
|
return activityIndex;
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ import com.djrapitops.plan.utilities.analysis.MathUtils;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,8 +43,7 @@ public class SessionsMutator {
|
|||||||
|
|
||||||
public SessionsMutator filterSessionsBetween(long after, long before) {
|
public SessionsMutator filterSessionsBetween(long after, long before) {
|
||||||
sessions = sessions.stream()
|
sessions = sessions.stream()
|
||||||
.filter(session -> after <= session.getValue(SessionKeys.END).orElse(System.currentTimeMillis())
|
.filter(getBetweenPredicate(after, before))
|
||||||
&& session.getUnsafe(SessionKeys.START) <= before)
|
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -164,4 +164,16 @@ public class SessionsMutator {
|
|||||||
public int toPlayerKillCount() {
|
public int toPlayerKillCount() {
|
||||||
return toPlayerKillList().size();
|
return toPlayerKillList().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean playedBetween(long after, long before) {
|
||||||
|
return sessions.stream().anyMatch(getBetweenPredicate(after, before));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Predicate<Session> getBetweenPredicate(long after, long before) {
|
||||||
|
return session -> {
|
||||||
|
Long start = session.getUnsafe(SessionKeys.START);
|
||||||
|
Long end = session.getValue(SessionKeys.END).orElse(System.currentTimeMillis());
|
||||||
|
return (after <= start && start <= before) || (after <= end && end <= before);
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
@ -25,9 +25,7 @@ public class Formatters {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Formatter<Long> yearLongValue() {
|
public static Formatter<Long> yearLongValue() {
|
||||||
return date -> {
|
return date -> date > 0 ? FormatUtils.formatTimeStampYear(date) : "-";
|
||||||
return date > 0 ? FormatUtils.formatTimeStampYear(date) : "-";
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Formatter<DateHolder> day() {
|
public static Formatter<DateHolder> day() {
|
||||||
@ -66,4 +64,8 @@ public class Formatters {
|
|||||||
return day.get(Calendar.DAY_OF_YEAR);
|
return day.get(Calendar.DAY_OF_YEAR);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Formatter<Double> percentage() {
|
||||||
|
return value -> value >= 0 ? FormatUtils.cutDecimals(value * 100.0) + "%" : "-";
|
||||||
|
}
|
||||||
}
|
}
|
@ -62,8 +62,8 @@ public class AnalysisPage extends Page {
|
|||||||
// AVG_PLAYERS, AVG_PLAYERS_DAY, AVG_PLAYERS_WEEK,
|
// AVG_PLAYERS, AVG_PLAYERS_DAY, AVG_PLAYERS_WEEK,
|
||||||
// AVG_PLAYERS_MONTH, AVG_PLAYERS_NEW, AVG_PLAYERS_NEW_DAY,
|
// AVG_PLAYERS_MONTH, AVG_PLAYERS_NEW, AVG_PLAYERS_NEW_DAY,
|
||||||
// AVG_PLAYERS_NEW_WEEK, AVG_PLAYERS_NEW_MONTH, PLAYERS_STUCK_DAY,
|
// AVG_PLAYERS_NEW_WEEK, AVG_PLAYERS_NEW_MONTH, PLAYERS_STUCK_DAY,
|
||||||
// PLAYERS_STUCK_DAY_PERC, PLAYERS_STUCK_WEEK, PLAYERS_STUCK_WEEK_PERC,
|
// PLAYERS_STUCK_DAY_PERC, PLAYERS_RETAINED_WEEK, PLAYERS_RETAINED_WEEK_PERC,
|
||||||
// PLAYERS_STUCK_MONTH, PLAYERS_STUCK_MONTH_PERC,
|
// PLAYERS_RETAINED_MONTH, PLAYERS_RETAINED_MONTH_PERC,
|
||||||
//
|
//
|
||||||
// TPS_SPIKE_MONTH, TPS_SPIKE_WEEK, TPS_SPIKE_DAY,
|
// TPS_SPIKE_MONTH, TPS_SPIKE_WEEK, TPS_SPIKE_DAY,
|
||||||
// AVG_TPS_MONTH, AVG_TPS_WEEK, AVG_TPS_DAY,
|
// AVG_TPS_MONTH, AVG_TPS_WEEK, AVG_TPS_DAY,
|
||||||
|
@ -6,12 +6,12 @@ package com.djrapitops.plan.system.webserver.pages.parsing;
|
|||||||
|
|
||||||
import com.djrapitops.plan.api.exceptions.ParseException;
|
import com.djrapitops.plan.api.exceptions.ParseException;
|
||||||
import com.djrapitops.plan.data.Actions;
|
import com.djrapitops.plan.data.Actions;
|
||||||
import com.djrapitops.plan.data.calculation.ActivityIndex;
|
|
||||||
import com.djrapitops.plan.data.container.Action;
|
import com.djrapitops.plan.data.container.Action;
|
||||||
import com.djrapitops.plan.data.container.Session;
|
import com.djrapitops.plan.data.container.Session;
|
||||||
import com.djrapitops.plan.data.store.containers.PerServerContainer;
|
import com.djrapitops.plan.data.store.containers.PerServerContainer;
|
||||||
import com.djrapitops.plan.data.store.containers.PlayerContainer;
|
import com.djrapitops.plan.data.store.containers.PlayerContainer;
|
||||||
import com.djrapitops.plan.data.store.keys.PlayerKeys;
|
import com.djrapitops.plan.data.store.keys.PlayerKeys;
|
||||||
|
import com.djrapitops.plan.data.store.mutators.ActivityIndex;
|
||||||
import com.djrapitops.plan.data.store.mutators.PerServerDataMutator;
|
import com.djrapitops.plan.data.store.mutators.PerServerDataMutator;
|
||||||
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
|
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
|
||||||
import com.djrapitops.plan.data.store.mutators.formatting.Formatter;
|
import com.djrapitops.plan.data.store.mutators.formatting.Formatter;
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package com.djrapitops.plan.utilities.analysis;
|
package com.djrapitops.plan.utilities.analysis;
|
||||||
|
|
||||||
import com.djrapitops.plan.data.PlayerProfile;
|
import com.djrapitops.plan.data.PlayerProfile;
|
||||||
import com.djrapitops.plan.data.calculation.ActivityIndex;
|
|
||||||
import com.djrapitops.plan.data.container.Session;
|
import com.djrapitops.plan.data.container.Session;
|
||||||
import com.djrapitops.plan.data.container.StickyData;
|
|
||||||
import com.djrapitops.plan.data.store.keys.SessionKeys;
|
import com.djrapitops.plan.data.store.keys.SessionKeys;
|
||||||
|
import com.djrapitops.plan.data.store.mutators.ActivityIndex;
|
||||||
|
import com.djrapitops.plan.data.store.mutators.RetentionData;
|
||||||
import com.djrapitops.plan.data.time.GMTimes;
|
import com.djrapitops.plan.data.time.GMTimes;
|
||||||
import com.djrapitops.plan.data.time.WorldTimes;
|
import com.djrapitops.plan.data.time.WorldTimes;
|
||||||
import com.djrapitops.plan.system.settings.WorldAliasSettings;
|
import com.djrapitops.plan.system.settings.WorldAliasSettings;
|
||||||
@ -243,24 +243,21 @@ public class AnalysisUtils {
|
|||||||
return gmTimesPerAlias;
|
return gmTimesPerAlias;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static StickyData average(Collection<StickyData> stuck) {
|
public static RetentionData average(Collection<RetentionData> stuck) {
|
||||||
int size = stuck.size();
|
int size = stuck.size();
|
||||||
|
|
||||||
double totalIndex = 0.0;
|
double totalIndex = 0.0;
|
||||||
double totalMsgsSent = 0.0;
|
|
||||||
double totalPlayersOnline = 0.0;
|
double totalPlayersOnline = 0.0;
|
||||||
|
|
||||||
for (StickyData stickyData : stuck) {
|
for (RetentionData retentionData : stuck) {
|
||||||
totalIndex += stickyData.getActivityIndex();
|
totalIndex += retentionData.getActivityIndex();
|
||||||
totalMsgsSent += stickyData.getMessagesSent();
|
totalPlayersOnline += retentionData.getOnlineOnJoin();
|
||||||
totalPlayersOnline += stickyData.getOnlineOnJoin();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double averageIndex = totalIndex / (double) size;
|
double averageIndex = totalIndex / (double) size;
|
||||||
double averageMessagesSent = totalMsgsSent / (double) size;
|
|
||||||
double averagePlayersOnline = totalPlayersOnline / (double) size;
|
double averagePlayersOnline = totalPlayersOnline / (double) size;
|
||||||
|
|
||||||
return new StickyData(averageIndex, averageMessagesSent, averagePlayersOnline);
|
return new RetentionData(averageIndex, averagePlayersOnline);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getLongestWorldPlayed(Session session) {
|
public static String getLongestWorldPlayed(Session session) {
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.djrapitops.plan.utilities.html.graphs;
|
package com.djrapitops.plan.utilities.html.graphs;
|
||||||
|
|
||||||
import com.djrapitops.plan.data.calculation.ActivityIndex;
|
import com.djrapitops.plan.data.store.mutators.ActivityIndex;
|
||||||
import com.djrapitops.plan.data.store.mutators.PlayersMutator;
|
import com.djrapitops.plan.data.store.mutators.PlayersMutator;
|
||||||
import com.djrapitops.plan.system.settings.theme.Theme;
|
import com.djrapitops.plan.system.settings.theme.Theme;
|
||||||
import com.djrapitops.plan.system.settings.theme.ThemeVal;
|
import com.djrapitops.plan.system.settings.theme.ThemeVal;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.djrapitops.plan.utilities.html.graphs.pie;
|
package com.djrapitops.plan.utilities.html.graphs.pie;
|
||||||
|
|
||||||
import com.djrapitops.plan.data.calculation.ActivityIndex;
|
import com.djrapitops.plan.data.store.mutators.ActivityIndex;
|
||||||
import com.djrapitops.plan.system.settings.theme.Theme;
|
import com.djrapitops.plan.system.settings.theme.Theme;
|
||||||
import com.djrapitops.plan.system.settings.theme.ThemeVal;
|
import com.djrapitops.plan.system.settings.theme.ThemeVal;
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package com.djrapitops.plan.utilities.html.tables;
|
package com.djrapitops.plan.utilities.html.tables;
|
||||||
|
|
||||||
import com.djrapitops.plan.api.PlanAPI;
|
import com.djrapitops.plan.api.PlanAPI;
|
||||||
import com.djrapitops.plan.data.calculation.ActivityIndex;
|
|
||||||
import com.djrapitops.plan.data.container.GeoInfo;
|
import com.djrapitops.plan.data.container.GeoInfo;
|
||||||
import com.djrapitops.plan.data.element.TableContainer;
|
import com.djrapitops.plan.data.element.TableContainer;
|
||||||
import com.djrapitops.plan.data.store.containers.PlayerContainer;
|
import com.djrapitops.plan.data.store.containers.PlayerContainer;
|
||||||
import com.djrapitops.plan.data.store.keys.PlayerKeys;
|
import com.djrapitops.plan.data.store.keys.PlayerKeys;
|
||||||
|
import com.djrapitops.plan.data.store.mutators.ActivityIndex;
|
||||||
import com.djrapitops.plan.data.store.mutators.GeoInfoMutator;
|
import com.djrapitops.plan.data.store.mutators.GeoInfoMutator;
|
||||||
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
|
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
|
||||||
import com.djrapitops.plan.data.store.mutators.formatting.Formatters;
|
import com.djrapitops.plan.data.store.mutators.formatting.Formatters;
|
||||||
|
@ -2,7 +2,7 @@ package com.djrapitops.plan.utilities.html.tables;
|
|||||||
|
|
||||||
import com.djrapitops.plan.api.PlanAPI;
|
import com.djrapitops.plan.api.PlanAPI;
|
||||||
import com.djrapitops.plan.data.PlayerProfile;
|
import com.djrapitops.plan.data.PlayerProfile;
|
||||||
import com.djrapitops.plan.data.calculation.ActivityIndex;
|
import com.djrapitops.plan.data.store.mutators.ActivityIndex;
|
||||||
import com.djrapitops.plan.data.store.mutators.formatting.Formatters;
|
import com.djrapitops.plan.data.store.mutators.formatting.Formatters;
|
||||||
import com.djrapitops.plan.system.info.server.ServerInfo;
|
import com.djrapitops.plan.system.info.server.ServerInfo;
|
||||||
import com.djrapitops.plan.system.settings.Settings;
|
import com.djrapitops.plan.system.settings.Settings;
|
||||||
|
@ -3,6 +3,7 @@ package com.djrapitops.plan.data.calculation;
|
|||||||
import com.djrapitops.plan.data.container.Session;
|
import com.djrapitops.plan.data.container.Session;
|
||||||
import com.djrapitops.plan.data.store.containers.PlayerContainer;
|
import com.djrapitops.plan.data.store.containers.PlayerContainer;
|
||||||
import com.djrapitops.plan.data.store.keys.PlayerKeys;
|
import com.djrapitops.plan.data.store.keys.PlayerKeys;
|
||||||
|
import com.djrapitops.plan.data.store.mutators.ActivityIndex;
|
||||||
import com.djrapitops.plan.system.settings.Settings;
|
import com.djrapitops.plan.system.settings.Settings;
|
||||||
import com.djrapitops.plugin.api.TimeAmount;
|
import com.djrapitops.plugin.api.TimeAmount;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.djrapitops.plan.utilities.html.graphs;
|
package com.djrapitops.plan.utilities.html.graphs;
|
||||||
|
|
||||||
import com.djrapitops.plan.data.calculation.ActivityIndex;
|
|
||||||
import com.djrapitops.plan.data.container.TPS;
|
import com.djrapitops.plan.data.container.TPS;
|
||||||
|
import com.djrapitops.plan.data.store.mutators.ActivityIndex;
|
||||||
import com.djrapitops.plan.system.settings.Settings;
|
import com.djrapitops.plan.system.settings.Settings;
|
||||||
import com.djrapitops.plan.utilities.html.graphs.line.*;
|
import com.djrapitops.plan.utilities.html.graphs.line.*;
|
||||||
import com.djrapitops.plan.utilities.html.graphs.stack.AbstractStackGraph;
|
import com.djrapitops.plan.utilities.html.graphs.stack.AbstractStackGraph;
|
||||||
|
Loading…
Reference in New Issue
Block a user