mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2024-12-21 05:50:18 +08:00
Added Session Calendar to Server page (#115 100%)
This commit is contained in:
parent
e2d11e6dfe
commit
517ffa0bcf
@ -23,6 +23,7 @@ import com.djrapitops.plan.utilities.html.Html;
|
|||||||
import com.djrapitops.plan.utilities.html.graphs.ActivityStackGraph;
|
import com.djrapitops.plan.utilities.html.graphs.ActivityStackGraph;
|
||||||
import com.djrapitops.plan.utilities.html.graphs.PunchCardGraph;
|
import com.djrapitops.plan.utilities.html.graphs.PunchCardGraph;
|
||||||
import com.djrapitops.plan.utilities.html.graphs.WorldMap;
|
import com.djrapitops.plan.utilities.html.graphs.WorldMap;
|
||||||
|
import com.djrapitops.plan.utilities.html.graphs.calendar.ServerCalendar;
|
||||||
import com.djrapitops.plan.utilities.html.graphs.line.*;
|
import com.djrapitops.plan.utilities.html.graphs.line.*;
|
||||||
import com.djrapitops.plan.utilities.html.graphs.pie.ActivityPie;
|
import com.djrapitops.plan.utilities.html.graphs.pie.ActivityPie;
|
||||||
import com.djrapitops.plan.utilities.html.graphs.pie.WorldPie;
|
import com.djrapitops.plan.utilities.html.graphs.pie.WorldPie;
|
||||||
@ -128,6 +129,11 @@ public class AnalysisData extends RawData {
|
|||||||
geolocationsTab(geoLocations);
|
geolocationsTab(geoLocations);
|
||||||
commandUsage(commandUsage);
|
commandUsage(commandUsage);
|
||||||
|
|
||||||
|
List<Long> registered = profile.getPlayers().stream().map(PlayerProfile::getRegistered).collect(Collectors.toList());
|
||||||
|
ServerCalendar serverCalendar = new ServerCalendar(registered, sessions);
|
||||||
|
addValue("calendarSeries", serverCalendar.toCalendarSeries());
|
||||||
|
addValue("firstDay", 1);
|
||||||
|
|
||||||
addValue("ops", ops.size());
|
addValue("ops", ops.size());
|
||||||
addValue("playersTotal", playersTotal);
|
addValue("playersTotal", playersTotal);
|
||||||
|
|
||||||
|
@ -25,8 +25,8 @@ import com.djrapitops.plan.utilities.comparators.SessionStartComparator;
|
|||||||
import com.djrapitops.plan.utilities.file.FileUtil;
|
import com.djrapitops.plan.utilities.file.FileUtil;
|
||||||
import com.djrapitops.plan.utilities.html.HtmlStructure;
|
import com.djrapitops.plan.utilities.html.HtmlStructure;
|
||||||
import com.djrapitops.plan.utilities.html.HtmlUtils;
|
import com.djrapitops.plan.utilities.html.HtmlUtils;
|
||||||
import com.djrapitops.plan.utilities.html.graphs.PlayerCalendar;
|
|
||||||
import com.djrapitops.plan.utilities.html.graphs.PunchCardGraph;
|
import com.djrapitops.plan.utilities.html.graphs.PunchCardGraph;
|
||||||
|
import com.djrapitops.plan.utilities.html.graphs.calendar.PlayerCalendar;
|
||||||
import com.djrapitops.plan.utilities.html.graphs.pie.ServerPreferencePie;
|
import com.djrapitops.plan.utilities.html.graphs.pie.ServerPreferencePie;
|
||||||
import com.djrapitops.plan.utilities.html.graphs.pie.WorldPie;
|
import com.djrapitops.plan.utilities.html.graphs.pie.WorldPie;
|
||||||
import com.djrapitops.plan.utilities.html.structure.ServerAccordion;
|
import com.djrapitops.plan.utilities.html.structure.ServerAccordion;
|
||||||
|
@ -138,7 +138,8 @@ public class HtmlExport extends SpecificExport {
|
|||||||
"web/js/charts/serverPie.js",
|
"web/js/charts/serverPie.js",
|
||||||
"web/js/charts/worldPie.js",
|
"web/js/charts/worldPie.js",
|
||||||
"web/js/charts/healthGauge.js",
|
"web/js/charts/healthGauge.js",
|
||||||
"web/js/charts/sessionCalendar.js"
|
"web/js/charts/sessionCalendar.js",
|
||||||
|
"web/js/charts/onlineActivityCalendar.js"
|
||||||
};
|
};
|
||||||
copyFromJar(resources);
|
copyFromJar(resources);
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
* Licence is provided in the jar as license.yml also here:
|
* 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
|
* https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/license.yml
|
||||||
*/
|
*/
|
||||||
package com.djrapitops.plan.utilities.html.graphs;
|
package com.djrapitops.plan.utilities.html.graphs.calendar;
|
||||||
|
|
||||||
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;
|
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* 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.utilities.html.graphs.calendar;
|
||||||
|
|
||||||
|
import com.djrapitops.plan.data.container.Session;
|
||||||
|
import com.djrapitops.plan.utilities.FormatUtils;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility for creating FullCalendar calendar event array on Player page.
|
||||||
|
*
|
||||||
|
* @author Rsl1122
|
||||||
|
*/
|
||||||
|
public class ServerCalendar {
|
||||||
|
|
||||||
|
private final List<Long> registerDates;
|
||||||
|
private final Map<UUID, List<Session>> sessions;
|
||||||
|
|
||||||
|
public ServerCalendar(List<Long> registerDates, Map<UUID, List<Session>> sessions) {
|
||||||
|
this.registerDates = registerDates;
|
||||||
|
this.sessions = sessions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toCalendarSeries() {
|
||||||
|
StringBuilder series = new StringBuilder("[");
|
||||||
|
|
||||||
|
series.append("{title: 'badcode',start:0}");
|
||||||
|
appendSessionRelatedData(series);
|
||||||
|
appendRegistered(series);
|
||||||
|
|
||||||
|
return series.append("]").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendRegistered(StringBuilder series) {
|
||||||
|
Map<String, Integer> registeredByDay = getRegisteredByDay();
|
||||||
|
|
||||||
|
for (Map.Entry<String, Integer> entry : registeredByDay.entrySet()) {
|
||||||
|
String day = entry.getKey();
|
||||||
|
Integer newPlayers = entry.getValue();
|
||||||
|
|
||||||
|
series.append(",{title: 'New: ").append(newPlayers)
|
||||||
|
.append("',start:'").append(day)
|
||||||
|
.append("',color: '#8BC34A'")
|
||||||
|
.append("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendSessionRelatedData(StringBuilder series) {
|
||||||
|
Map<String, Map<UUID, List<Session>>> sessionsByDay = getSessionsByDay();
|
||||||
|
|
||||||
|
for (Map.Entry<String, Map<UUID, List<Session>>> entry : sessionsByDay.entrySet()) {
|
||||||
|
String day = entry.getKey();
|
||||||
|
|
||||||
|
Map<UUID, List<Session>> sessionsPerUsers = entry.getValue();
|
||||||
|
long sessionCount = sessionsPerUsers.values().stream().mapToLong(Collection::size).sum();
|
||||||
|
long playtime = sessionsPerUsers.values().stream().flatMap(Collection::stream).mapToLong(Session::getLength).sum();
|
||||||
|
long uniquePlayers = sessionsPerUsers.size();
|
||||||
|
|
||||||
|
series.append(",{title: 'Playtime: ").append(FormatUtils.formatTimeAmount(playtime))
|
||||||
|
.append("',start:'").append(day)
|
||||||
|
.append("',color: '#4CAF50'")
|
||||||
|
.append("}");
|
||||||
|
|
||||||
|
series.append(",{title: 'Sessions: ").append(sessionCount)
|
||||||
|
.append("',start:'").append(day)
|
||||||
|
.append("',color: '#009688'")
|
||||||
|
.append("}");
|
||||||
|
|
||||||
|
series.append(",{title: 'Unique: ").append(uniquePlayers)
|
||||||
|
.append("',start:'").append(day)
|
||||||
|
.append("'}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Map<UUID, List<Session>>> getSessionsByDay() {
|
||||||
|
Map<String, Map<UUID, List<Session>>> sessionsByDay = new HashMap<>();
|
||||||
|
for (Map.Entry<UUID, List<Session>> entry : sessions.entrySet()) {
|
||||||
|
UUID player = entry.getKey();
|
||||||
|
List<Session> sessions = entry.getValue();
|
||||||
|
|
||||||
|
for (Session session : sessions) {
|
||||||
|
String day = FormatUtils.formatTimeStampISO8601NoClock(session.getSessionStart());
|
||||||
|
|
||||||
|
Map<UUID, List<Session>> sessionsPerUserOfDay = sessionsByDay.getOrDefault(day, new HashMap<>());
|
||||||
|
List<Session> sessionsOfUser = sessionsPerUserOfDay.getOrDefault(player, new ArrayList<>());
|
||||||
|
sessionsOfUser.add(session);
|
||||||
|
sessionsPerUserOfDay.put(player, sessionsOfUser);
|
||||||
|
sessionsByDay.put(day, sessionsPerUserOfDay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sessionsByDay;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Integer> getRegisteredByDay() {
|
||||||
|
Map<String, Integer> RegisteredByDay = new HashMap<>();
|
||||||
|
for (Long registered : registerDates) {
|
||||||
|
String day = FormatUtils.formatTimeStampISO8601NoClock(registered);
|
||||||
|
|
||||||
|
int registeredPerDay = RegisteredByDay.getOrDefault(day, 0);
|
||||||
|
registeredPerDay += 1;
|
||||||
|
RegisteredByDay.put(day, registeredPerDay);
|
||||||
|
}
|
||||||
|
return RegisteredByDay;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
function onlineActivityCalendar(id, events, firstDay) {
|
||||||
|
$(id).fullCalendar({
|
||||||
|
eventColor: '#2196F3',
|
||||||
|
firstDay: firstDay,
|
||||||
|
|
||||||
|
eventRender: function (eventObj, $el) {
|
||||||
|
$el.popover({
|
||||||
|
content: eventObj.title,
|
||||||
|
trigger: 'hover',
|
||||||
|
placement: 'top',
|
||||||
|
container: 'body'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
events: events,
|
||||||
|
|
||||||
|
height: 'parent',
|
||||||
|
header: {
|
||||||
|
left: 'title',
|
||||||
|
center: '',
|
||||||
|
right: 'month prev,next'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
$(id).fullCalendar('render')
|
||||||
|
}, 1000);
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
function sessionCalendar(id, events, firstDay) {
|
function sessionCalendar(id, events, firstDay) {
|
||||||
$(id).fullCalendar({
|
$(id).fullCalendar({
|
||||||
eventColor: '#009688',
|
eventColor: '#009688',
|
||||||
eventLimit: true,
|
eventLimit: 4,
|
||||||
firstDay: firstDay,
|
firstDay: firstDay,
|
||||||
|
|
||||||
eventRender: function (eventObj, $el) {
|
eventRender: function (eventObj, $el) {
|
||||||
@ -20,7 +20,7 @@ function sessionCalendar(id, events, firstDay) {
|
|||||||
header: {
|
header: {
|
||||||
left: 'title',
|
left: 'title',
|
||||||
center: '',
|
center: '',
|
||||||
right: 'month agendaWeek agendaDay prev,next'
|
right: 'month agendaWeek agendaDay today prev,next'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -708,10 +708,8 @@
|
|||||||
color: '#222',
|
color: '#222',
|
||||||
data: ${punchCardSeries}
|
data: ${punchCardSeries}
|
||||||
};
|
};
|
||||||
var calendarSeries =;
|
var calendarSeries = ${calendarSeries};
|
||||||
${calendarSeries}
|
var firstDay = ${firstDay};
|
||||||
var firstDay =
|
|
||||||
${firstDay}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Plan laod script -->
|
<!-- Plan laod script -->
|
||||||
|
@ -454,12 +454,10 @@
|
|||||||
<div class="row clearfix">
|
<div class="row clearfix">
|
||||||
<div class="col-xs-12 col-sm-12 col-md-6 col-lg-6">
|
<div class="col-xs-12 col-sm-12 col-md-6 col-lg-6">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="header">
|
<div class="body">
|
||||||
|
<!-- Nav tabs -->
|
||||||
<div class="row clearfix">
|
<div class="row clearfix">
|
||||||
<div class="col-xs-6 col-sm-6 col-lg-6">
|
<div class="col-xs-12 col-sm-12 col-lg-12">
|
||||||
<h2><i class="col-blue fa fa-area-chart"></i> Online Activity</h2>
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-6 col-sm-6 col-lg-6">
|
|
||||||
<a href="javascript:void(0)" class="help material-icons pull-right"
|
<a href="javascript:void(0)" class="help material-icons pull-right"
|
||||||
data-trigger="focus" data-toggle="popover" data-placement="left"
|
data-trigger="focus" data-toggle="popover" data-placement="left"
|
||||||
data-container="body" data-html="true"
|
data-container="body" data-html="true"
|
||||||
@ -467,33 +465,62 @@
|
|||||||
data-content="Chart of Player Counts, recorded by TPS task.
|
data-content="Chart of Player Counts, recorded by TPS task.
|
||||||
<br><br>TPS task starts 50 seconds after Plan enables and records Maximum player count for each minute."
|
<br><br>TPS task starts 50 seconds after Plan enables and records Maximum player count for each minute."
|
||||||
>help_outline</a>
|
>help_outline</a>
|
||||||
|
<ul class="nav nav-tabs tab-nav-right" role="tablist">
|
||||||
|
<li role="presentation" class="active"><a href="#playersOnline"
|
||||||
|
data-toggle="tab"><i
|
||||||
|
class="fa fa-area-chart"></i> ONLINE ACTIVITY</a></li>
|
||||||
|
<li role="presentation"><a href="#uniqueAndNew" data-toggle="tab"><i
|
||||||
|
class="fa fa-area-chart"></i> UNIQUE PLAYERS</a></li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Tab panes -->
|
||||||
|
<div class="tab-content">
|
||||||
|
<div role="tabpanel" class="tab-pane fade in active" id="playersOnline">
|
||||||
|
<div id="playerChartMonth" style="height: 500px;"
|
||||||
|
class="dashboard-flot-chart"></div>
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="tab-pane fade" id="uniqueAndNew">
|
||||||
|
<div id="uniqueChart" style="height: 500px;" class="dashboard-flot-chart"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="body">
|
|
||||||
<div id="playerChartMonth" class="dashboard-flot-chart"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 col-sm-12 col-md-6 col-lg-6">
|
<div class="col-xs-12 col-sm-12 col-md-6 col-lg-6">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="header">
|
<div class="body">
|
||||||
|
<!-- Nav tabs -->
|
||||||
<div class="row clearfix">
|
<div class="row clearfix">
|
||||||
<div class="col-xs-6 col-sm-6 col-lg-6">
|
<div class="col-xs-12 col-sm-12 col-lg-12">
|
||||||
<h2><i class="col-black fa fa-braille"></i> Punchcard</h2>
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-6 col-sm-6 col-lg-6">
|
|
||||||
<a href="javascript:void(0)" class="help material-icons pull-right"
|
<a href="javascript:void(0)" class="help material-icons pull-right"
|
||||||
data-trigger="focus" data-toggle="popover" data-placement="left"
|
data-trigger="focus" data-toggle="popover" data-placement="left"
|
||||||
data-container="body" data-html="true"
|
data-container="body" data-html="true"
|
||||||
data-original-title="Player Join PunchCard"
|
data-original-title="Calendar and Punchcard"
|
||||||
data-content="Chart displays the time and days players usually join the server.<br><br>Unlike Player page punchcard, only the last 30 days are included.<br><br><b>Relative Activity:</b> Size of a dot is relative to amount of sessions started in that hour."
|
data-content="Chart displays the time and days players usually join the server.
|
||||||
|
<br><br>Unlike Player page punchcard, only the last 30 days are included.
|
||||||
|
<br><br><b>Relative Activity:</b> Size of a dot is relative to amount of sessions started in that hour."
|
||||||
>help_outline</a>
|
>help_outline</a>
|
||||||
|
<ul class="nav nav-tabs tab-nav-right" role="tablist">
|
||||||
|
<li role="presentation" class="active"><a href="#calendarTab"
|
||||||
|
data-toggle="tab"><i
|
||||||
|
class="fa fa-calendar"></i> CALENDAR</a></li>
|
||||||
|
<li role="presentation"><a href="#punchCardTab" data-toggle="tab"><i
|
||||||
|
class="fa fa-braille"></i> PUNCHCARD</a></li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Tab panes -->
|
||||||
|
<div class="tab-content">
|
||||||
|
<div role="tabpanel" class="tab-pane fade in active" id="calendarTab">
|
||||||
|
<div id="calendar" style="height: 500px;" class="dashboard-flot-chart"></div>
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="tab-pane fade" id="punchCardTab">
|
||||||
|
<div id="punchCard" style="height: 500px;" class="dashboard-flot-chart"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="body">
|
|
||||||
<div id="punchCard" class="dashboard-flot-chart"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -1086,6 +1113,11 @@
|
|||||||
<!-- Header, Sidenav & Skin changer -->
|
<!-- Header, Sidenav & Skin changer -->
|
||||||
<script src="js/admin.js"></script>
|
<script src="js/admin.js"></script>
|
||||||
|
|
||||||
|
<!-- FullCalendar -->
|
||||||
|
<link rel='stylesheet' href='../plugins/fullcalendar/fullcalendar.min.css'/>
|
||||||
|
<script src='../plugins/momentjs/moment.js'></script>
|
||||||
|
<script src='../plugins/fullcalendar/fullcalendar.min.js'></script>
|
||||||
|
|
||||||
<!-- Plan Charts -->
|
<!-- Plan Charts -->
|
||||||
<script src="js/charts/playerGraph.js"></script>
|
<script src="js/charts/playerGraph.js"></script>
|
||||||
<script src="js/charts/punchCard.js"></script>
|
<script src="js/charts/punchCard.js"></script>
|
||||||
@ -1098,6 +1130,7 @@
|
|||||||
<script src="js/charts/resourceGraph.js"></script>
|
<script src="js/charts/resourceGraph.js"></script>
|
||||||
<script src="js/charts/worldGraph.js"></script>
|
<script src="js/charts/worldGraph.js"></script>
|
||||||
<script src="js/charts/worldMap.js"></script>
|
<script src="js/charts/worldMap.js"></script>
|
||||||
|
<script src="js/charts/onlineActivityCalendar.js"></script>
|
||||||
|
|
||||||
<!-- Chart Data -->
|
<!-- Chart Data -->
|
||||||
<script>
|
<script>
|
||||||
@ -1125,7 +1158,8 @@
|
|||||||
},
|
},
|
||||||
values: {
|
values: {
|
||||||
tpsMed: ${tpsMedium},
|
tpsMed: ${tpsMedium},
|
||||||
tpsHigh: ${tpsHigh}
|
tpsHigh: ${tpsHigh},
|
||||||
|
firstDay: ${firstDay}
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
playersOnline: ${playersOnlineSeries},
|
playersOnline: ${playersOnlineSeries},
|
||||||
@ -1141,7 +1175,8 @@
|
|||||||
punchCard: ${punchCardSeries},
|
punchCard: ${punchCardSeries},
|
||||||
activityStack: ${activityStackSeries},
|
activityStack: ${activityStackSeries},
|
||||||
activityStackCategories: ${activityStackCategories},
|
activityStackCategories: ${activityStackCategories},
|
||||||
healthIndex: ${healthIndex}
|
healthIndex: ${healthIndex},
|
||||||
|
calendar: ${calendarSeries}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1326,6 +1361,7 @@
|
|||||||
worldMap('worldMap', v.colors.geolocationsLow, v.colors.geolocationsHigh, series.geolocations);
|
worldMap('worldMap', v.colors.geolocationsLow, v.colors.geolocationsHigh, series.geolocations);
|
||||||
punchCard('punchCard', series.punchCard);
|
punchCard('punchCard', series.punchCard);
|
||||||
healthGauge('healthGauge', [v.data.healthIndex]);
|
healthGauge('healthGauge', [v.data.healthIndex]);
|
||||||
|
onlineActivityCalendar('#calendar', v.data.calendar, v.values.firstDay);
|
||||||
${sessionTabGraphViewFunctions}
|
${sessionTabGraphViewFunctions}
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
|
Loading…
Reference in New Issue
Block a user