diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/pages/parsing/InspectPage.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/pages/parsing/InspectPage.java index 1e88ee438..40f30f5fb 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/webserver/pages/parsing/InspectPage.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/pages/parsing/InspectPage.java @@ -25,6 +25,7 @@ import com.djrapitops.plan.utilities.comparators.SessionStartComparator; import com.djrapitops.plan.utilities.file.FileUtil; import com.djrapitops.plan.utilities.html.HtmlStructure; 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.pie.ServerPreferencePie; import com.djrapitops.plan.utilities.html.graphs.pie.WorldPie; @@ -126,6 +127,11 @@ public class InspectPage extends Page { ServerAccordion serverAccordion = new ServerAccordion(profile, serverNames); + PlayerCalendar playerCalendar = new PlayerCalendar(allSessions, registered); + + addValue("calendarSeries", playerCalendar.toCalendarSeries()); + addValue("firstDay", 1); + addValue("accordionSessions", sessionsAccordion[0]); addValue("accordionServers", serverAccordion.toHtml()); addValue("sessionTabGraphViewFunctions", sessionsAccordion[1] + serverAccordion.toViewScript()); diff --git a/Plan/src/main/java/com/djrapitops/plan/utilities/FormatUtils.java b/Plan/src/main/java/com/djrapitops/plan/utilities/FormatUtils.java index d63beb564..6551cfb26 100644 --- a/Plan/src/main/java/com/djrapitops/plan/utilities/FormatUtils.java +++ b/Plan/src/main/java/com/djrapitops/plan/utilities/FormatUtils.java @@ -47,6 +47,12 @@ public class FormatUtils { return formatMilliseconds(Math.abs(after - before)); } + public static String formatTimeStampISO8601NoClock(long epochMs) { + String format = "yyyy-MM-dd"; + + return format(epochMs, format); + } + public static String formatTimeStampDay(long epochMs) { String format = "MMMMM d"; diff --git a/Plan/src/main/java/com/djrapitops/plan/utilities/file/export/HtmlExport.java b/Plan/src/main/java/com/djrapitops/plan/utilities/file/export/HtmlExport.java index ce90639d1..51f0558f7 100644 --- a/Plan/src/main/java/com/djrapitops/plan/utilities/file/export/HtmlExport.java +++ b/Plan/src/main/java/com/djrapitops/plan/utilities/file/export/HtmlExport.java @@ -137,7 +137,8 @@ public class HtmlExport extends SpecificExport { "web/js/charts/punchCard.js", "web/js/charts/serverPie.js", "web/js/charts/worldPie.js", - "web/js/charts/healthGauge.js" + "web/js/charts/healthGauge.js", + "web/js/charts/sessionCalendar.js" }; copyFromJar(resources); @@ -164,7 +165,10 @@ public class HtmlExport extends SpecificExport { "web/plugins/jquery/jquery.min.js", "web/plugins/bootstrap/js/bootstrap.js", "web/plugins/jquery-datatable/skin/bootstrap/js/dataTables.bootstrap.js", - "web/plugins/jquery-datatable/jquery.dataTables.js" + "web/plugins/jquery-datatable/jquery.dataTables.js", + "web/plugins/fullcalendar/fullcalendar.min.js", + "web/plugins/fullcalendar/fullcalendar.min.css", + "web/plugins/momentjs/moment.js", }; copyFromJar(resources); } diff --git a/Plan/src/main/java/com/djrapitops/plan/utilities/html/graphs/PlayerCalendar.java b/Plan/src/main/java/com/djrapitops/plan/utilities/html/graphs/PlayerCalendar.java new file mode 100644 index 000000000..1579d107f --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/utilities/html/graphs/PlayerCalendar.java @@ -0,0 +1,103 @@ +/* + * 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; + +import com.djrapitops.plan.data.container.PlayerKill; +import com.djrapitops.plan.data.container.Session; +import com.djrapitops.plan.utilities.FormatUtils; +import com.djrapitops.plugin.api.TimeAmount; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Utility for creating FullCalendar calendar event array on Player page. + * + * @author Rsl1122 + */ +public class PlayerCalendar { + + private final List allSessions; + private final long registered; + + public PlayerCalendar(List allSessions, long registered) { + this.allSessions = allSessions; + this.registered = registered; + } + + public String toCalendarSeries() { + StringBuilder series = new StringBuilder("["); + + appendRegister(series); + appendDailyPlaytime(series); + appendSessionsAndKills(series); + + return series.append("]").toString(); + } + + private void appendDailyPlaytime(StringBuilder series) { + Map> sessionsByDay = getSessionsByDay(); + + for (Map.Entry> entry : sessionsByDay.entrySet()) { + String day = entry.getKey(); + + List sessions = entry.getValue(); + int sessionCount = sessions.size(); + long playtime = sessions.stream().mapToLong(Session::getLength).sum(); + + 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("'}"); + } + } + + private Map> getSessionsByDay() { + Map> sessionsByDay = new HashMap<>(); + for (Session session : allSessions) { + String day = FormatUtils.formatTimeStampISO8601NoClock(session.getSessionStart()); + + List sessionsOfDay = sessionsByDay.getOrDefault(day, new ArrayList<>()); + sessionsOfDay.add(session); + sessionsByDay.put(day, sessionsOfDay); + } + return sessionsByDay; + } + + private void appendSessionsAndKills(StringBuilder series) { + long fiveMinutes = TimeAmount.MINUTE.ms() * 5L; + for (Session session : allSessions) { + String length = FormatUtils.formatTimeAmount(session.getLength()); + + series.append(",{title: 'Session: ").append(length) + .append("',start:").append(session.getSessionStart()) + .append(",end:").append(session.getSessionEnd()) + .append("}"); + + for (PlayerKill kill : session.getPlayerKills()) { + long time = kill.getTime(); + + series.append(",{title: 'Killed: ").append(kill.getVictim()) + .append("',start:").append(time) + .append(",end:").append(time + fiveMinutes) + .append(",color: 'red'") + .append("}"); + } + } + } + + private void appendRegister(StringBuilder series) { + String registered = FormatUtils.formatTimeStampYear(this.registered); + + series.append("{title: 'Registered: ").append(registered).append("'," + + "start: ").append(this.registered).append(",color: '#8BC34A'}"); + } +} \ No newline at end of file diff --git a/Plan/src/main/resources/web/js/charts/sessionCalendar.js b/Plan/src/main/resources/web/js/charts/sessionCalendar.js new file mode 100644 index 000000000..cfb954cb8 --- /dev/null +++ b/Plan/src/main/resources/web/js/charts/sessionCalendar.js @@ -0,0 +1,30 @@ +function sessionCalendar(id, events, firstDay) { + $(id).fullCalendar({ + eventColor: '#009688', + eventLimit: true, + firstDay: firstDay, + + eventRender: function (eventObj, $el) { + $el.popover({ + content: eventObj.title, + trigger: 'hover', + placement: 'top', + container: 'body' + }); + }, + + events: events, + + navLinks: true, + height: 'parent', + header: { + left: 'title', + center: '', + right: 'month agendaWeek agendaDay prev,next' + } + }); + + setTimeout(function () { + $(id).fullCalendar('render') + }, 1000); +} \ No newline at end of file diff --git a/Plan/src/main/resources/web/player.html b/Plan/src/main/resources/web/player.html index dcadd50fd..74a1aa668 100644 --- a/Plan/src/main/resources/web/player.html +++ b/Plan/src/main/resources/web/player.html @@ -450,6 +450,26 @@
+
+
+
+
+

Session Calendar

+
+ +
+
+
+
+
+
@@ -646,6 +666,11 @@ + + + + + @@ -656,13 +681,14 @@ +