Made online activity overview update again on load

This commit is contained in:
Risto Lahtela 2021-02-06 11:34:02 +02:00 committed by Risto Lahtela
parent 6b4fb12755
commit b8ec15e381
8 changed files with 111 additions and 61 deletions

View File

@ -104,8 +104,16 @@ public class AsyncJSONResolverService {
} else {
// If there is no version available, block thread until the new finishes being generated.
try {
// updatedJSON is not null in this case ever because previousUpdates.getOrDefault(..., 0L) gets 0.
//noinspection ConstantConditions
// Can be null if the last update was recent and the file is deleted before next update
if (updatedJSON == null) {
updatedJSON = processing.submitNonCritical(() -> {
JSONStorage.StoredJSON created = jsonStorage.storeJson(identifier, creator.apply(serverUUID));
currentlyProcessing.remove(identifier);
jsonStorage.invalidateOlder(identifier, created.timestamp);
previousUpdates.put(identifier, created.timestamp);
return created;
}); // TODO Refactor this spaghetti code
}
return updatedJSON.get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
@ -157,8 +165,16 @@ public class AsyncJSONResolverService {
} else {
// If there is no version available, block thread until the new finishes being generated.
try {
// updatedJSON is not null in this case ever because previousUpdates.getOrDefault(..., 0L) gets 0.
//noinspection ConstantConditions
// Can be null if the last update was recent and the file is deleted before next update.
if (updatedJSON == null) {
updatedJSON = processing.submitNonCritical(() -> {
JSONStorage.StoredJSON created = jsonStorage.storeJson(identifier, creator.get());
currentlyProcessing.remove(identifier);
jsonStorage.invalidateOlder(identifier, created.timestamp);
previousUpdates.put(identifier, created.timestamp);
return created;
}); // TODO Refactor this spaghetti code
}
return updatedJSON.get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();

View File

@ -131,6 +131,7 @@ public enum HtmlLang implements Lang {
SIDE_PVP_PVE("PvP & PvE"),
SIDE_PERFORMANCE("Performance"),
LABEL_RETENTION("New Player Retention"),
DESCRIBE_RETENTION_PREDICTION("This value is a prediction based on previous players."),
TITLE_SERVER_AS_NUMBERS("Server as Numbers"),
TITLE_ONLINE_ACTIVITY_AS_NUMBERS("Online Activity as Numbers"),
COMPARING_15_DAYS("Comparing 15 days"),

View File

@ -27,6 +27,7 @@ import com.djrapitops.plugin.api.TimeAmount;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.debug.DebugLogger;
import com.djrapitops.plugin.task.RunnableFactory;
import org.apache.commons.lang3.StringUtils;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -81,13 +82,33 @@ public class JSONFileStorage implements JSONStorage {
@Override
public StoredJSON storeJson(String identifier, String json, long timestamp) {
Path writingTo = jsonDirectory.resolve(identifier + '-' + timestamp + JSON_FILE_EXTENSION);
String jsonToWrite = addMissingTimestamp(json, timestamp);
try {
Files.createDirectories(jsonDirectory);
Files.write(writingTo, json.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);
Files.write(writingTo, jsonToWrite.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);
} catch (IOException e) {
logger.warn("Could not write a file to " + writingTo.toFile().getAbsolutePath() + ": " + e.getMessage());
}
return new StoredJSON(json, timestamp, dateFormatter);
return new StoredJSON(jsonToWrite, timestamp);
}
private String addMissingTimestamp(String json, long timestamp) {
String writtenJSON;
if (!json.startsWith("{\"") || json.contains("timestamp")) {
if (!json.contains("timestamp_f")) {
writtenJSON = StringUtils.replaceOnce(json,
"\"timestamp\"",
"\"timestamp_f\":\"" + dateFormatter.apply(timestamp) + "\",\"timestamp\""
);
} else {
writtenJSON = json;
}
} else {
writtenJSON = "{\"timestamp\": " + timestamp +
",\"timestamp_f\":\"" + dateFormatter.apply(timestamp) +
"\",\"" + json.substring(2);
}
return writtenJSON;
}
@Override
@ -110,7 +131,7 @@ public class JSONFileStorage implements JSONStorage {
long timestamp = Long.parseLong(timestampMatch.group(1));
StringBuilder json = new StringBuilder();
lines.forEach(json::append);
return new StoredJSON(json.toString(), timestamp, dateFormatter);
return new StoredJSON(json.toString(), timestamp);
} catch (IOException e) {
logger.warn(jsonDirectory.toFile().getAbsolutePath() + " file '" + from.getName() + "' could not be read: " + e.getMessage());
} catch (NumberFormatException e) {

View File

@ -17,7 +17,6 @@
package com.djrapitops.plan.storage.json;
import com.djrapitops.plan.SubSystem;
import com.djrapitops.plan.delivery.formatting.Formatter;
import com.google.gson.Gson;
import java.util.Objects;
@ -66,14 +65,8 @@ public interface JSONStorage extends SubSystem {
public final String json;
public final long timestamp;
public StoredJSON(String json, long timestamp, Formatter<Long> dateFormatter) {
if (!json.startsWith("{\"") || json.contains("timestamp")) {
public StoredJSON(String json, long timestamp) {
this.json = json;
} else {
this.json = "{\"timestamp\": " + timestamp +
",\"timestamp_f\":\"" + dateFormatter.apply(timestamp) +
"\",\"" + json.substring(2);
}
this.timestamp = timestamp;
}

View File

@ -1258,3 +1258,7 @@ body.sidebar-hidden .navbar-nav {
font-size: 1rem;
margin-left: 1rem;
}
.refresh-element > i {
cursor: pointer;
}

View File

@ -123,62 +123,62 @@ function loadserverOverviewValues(json, error) {
/* This function loads Online Activity Overview tab */
function loadOnlineActivityOverviewValues(json, error) {
tab = $('#online-activity-overview');
const tab = document.getElementById('online-activity-overview');
if (error) {
displayError(tab, error);
displayError($('#online-activity-overview'), error);
return;
}
// Online Activity as Numbers
data = json.numbers;
element = $(tab).find('#data_numbers');
let data = json.numbers;
let element = tab.querySelector('#data_numbers');
$(element).find('#data_unique_players_30d').replaceWith('<td>' + data.unique_players_30d + smallTrend(data.unique_players_30d_trend) + '</td>');
$(element).find('#data_unique_players_7d').text(data.unique_players_7d);
$(element).find('#data_unique_players_24h').text(data.unique_players_24h);
element.querySelector('#data_unique_players_30d').innerHTML = data.unique_players_30d + smallTrend(data.unique_players_30d_trend);
element.querySelector('#data_unique_players_7d').innerText = data.unique_players_7d;
element.querySelector('#data_unique_players_24h').innerText = data.unique_players_24h;
$(element).find('#data_unique_players_30d_avg').replaceWith('<td>' + data.unique_players_30d_avg + smallTrend(data.unique_players_30d_avg_trend) + '</td>');
$(element).find('#data_unique_players_7d_avg').text(data.unique_players_7d_avg);
$(element).find('#data_unique_players_24h_avg').text(data.unique_players_24h_avg);
element.querySelector('#data_unique_players_30d_avg').innerHTML = data.unique_players_30d_avg + smallTrend(data.unique_players_30d_avg_trend);
element.querySelector('#data_unique_players_7d_avg').innerText = data.unique_players_7d_avg;
element.querySelector('#data_unique_players_24h_avg').innerText = data.unique_players_24h_avg;
$(element).find('#data_new_players_30d').replaceWith('<td>' + data.new_players_30d + smallTrend(data.new_players_30d_trend) + '</td>');
$(element).find('#data_new_players_7d').text(data.new_players_7d);
$(element).find('#data_new_players_24h').text(data.new_players_24h);
element.querySelector('#data_new_players_30d').innerHTML = data.new_players_30d + smallTrend(data.new_players_30d_trend);
element.querySelector('#data_new_players_7d').innerText = data.new_players_7d;
element.querySelector('#data_new_players_24h').innerText = data.new_players_24h;
$(element).find('#data_new_players_30d_avg').replaceWith('<td>' + data.new_players_30d_avg + smallTrend(data.new_players_30d_avg_trend) + '</td>');
$(element).find('#data_new_players_7d_avg').text(data.new_players_7d_avg);
$(element).find('#data_new_players_24h_avg').text(data.new_players_24h_avg);
element.querySelector('#data_new_players_30d_avg').innerHTML = data.new_players_30d_avg + smallTrend(data.new_players_30d_avg_trend);
element.querySelector('#data_new_players_7d_avg').innerText = data.new_players_7d_avg;
element.querySelector('#data_new_players_24h_avg').innerText = data.new_players_24h_avg;
$(element).find('#data_new_players_retention_30d').text('(' + data.new_players_retention_30d + '/' + data.new_players_30d + ') ' + data.new_players_retention_30d_perc);
$(element).find('#data_new_players_retention_7d').text('(' + data.new_players_retention_7d + '/' + data.new_players_7d + ') ' + data.new_players_retention_7d_perc);
$(element).find('#data_new_players_retention_24h').replaceWith(`<td title="This value is a prediction based on previous players.">(` + data.new_players_retention_24h + '/' + data.new_players_24h + ') ' + data.new_players_retention_24h_perc + ' <i class="far fa-fw fa-eye"></i></td>');
element.querySelector('#data_new_players_retention_30d').innerText = '(' + data.new_players_retention_30d + '/' + data.new_players_30d + ') ' + data.new_players_retention_30d_perc;
element.querySelector('#data_new_players_retention_7d').innerText = '(' + data.new_players_retention_7d + '/' + data.new_players_7d + ') ' + data.new_players_retention_7d_perc;
element.querySelector('#data_new_players_retention_24h').innerHTML = '(' + data.new_players_retention_24h + '/' + data.new_players_24h + ') ' + data.new_players_retention_24h_perc + ' <i class="far fa-fw fa-eye"></i>';
$(element).find('#data_playtime_30d').replaceWith('<td>' + data.playtime_30d + smallTrend(data.playtime_30d_trend) + '</td>');
$(element).find('#data_playtime_7d').text(data.playtime_7d);
$(element).find('#data_playtime_24h').text(data.playtime_24h);
element.querySelector('#data_playtime_30d').innerHTML = data.playtime_30d + smallTrend(data.playtime_30d_trend);
element.querySelector('#data_playtime_7d').innerText = data.playtime_7d;
element.querySelector('#data_playtime_24h').innerText = data.playtime_24h;
$(element).find('#data_playtime_30d_avg').replaceWith('<td>' + data.playtime_30d_avg + smallTrend(data.playtime_30d_avg_trend) + '</td>');
$(element).find('#data_playtime_7d_avg').text(data.playtime_7d_avg);
$(element).find('#data_playtime_24h_avg').text(data.playtime_24h_avg);
element.querySelector('#data_playtime_30d_avg').innerHTML = data.playtime_30d_avg + smallTrend(data.playtime_30d_avg_trend);
element.querySelector('#data_playtime_7d_avg').innerText = data.playtime_7d_avg;
element.querySelector('#data_playtime_24h_avg').innerText = data.playtime_24h_avg;
$(element).find('#data_session_length_30d_avg').replaceWith('<td>' + data.session_length_30d_avg + smallTrend(data.session_length_30d_trend) + '</td>');
$(element).find('#data_session_length_7d_avg').text(data.session_length_7d_avg);
$(element).find('#data_session_length_24h_avg').text(data.session_length_24h_avg);
element.querySelector('#data_session_length_30d_avg').innerHTML = data.session_length_30d_avg + smallTrend(data.session_length_30d_trend);
element.querySelector('#data_session_length_7d_avg').innerText = data.session_length_7d_avg;
element.querySelector('#data_session_length_24h_avg').innerText = data.session_length_24h_avg;
$(element).find('#data_sessions_30d').replaceWith('<td>' + data.sessions_30d + smallTrend(data.sessions_30d_trend) + '</td>');
$(element).find('#data_sessions_7d').text(data.sessions_7d);
$(element).find('#data_sessions_24h').text(data.sessions_24h);
element.querySelector('#data_sessions_30d').innerHTML = data.sessions_30d + smallTrend(data.sessions_30d_trend);
element.querySelector('#data_sessions_7d').innerText = data.sessions_7d;
element.querySelector('#data_sessions_24h').innerText = data.sessions_24h;
// Insights
data = json.insights;
element = $(tab).find('#data_insights');
element = tab.querySelector('#data_insights');
$(element).find('#data_players_first_join_avg').replaceWith(data.players_first_join_avg + smallTrend(data.players_first_join_trend));
$(element).find('#data_first_session_length_avg').replaceWith(data.first_session_length_avg + smallTrend(data.first_session_length_trend));
$(element).find('#data_first_session_length_median').replaceWith(data.first_session_length_median + smallTrend(data.first_session_length_median_trend));
$(element).find('#data_lone_joins').replaceWith(data.lone_joins + smallTrend(data.lone_joins_trend));
$(element).find('#data_lone_new_joins').replaceWith(data.lone_new_joins + smallTrend(data.lone_new_joins_trend))
element.querySelector('#data_players_first_join_avg').innerHTML = data.players_first_join_avg + smallTrend(data.players_first_join_trend);
element.querySelector('#data_first_session_length_avg').innerHTML = data.first_session_length_avg + smallTrend(data.first_session_length_trend);
element.querySelector('#data_first_session_length_median').innerHTML = data.first_session_length_median + smallTrend(data.first_session_length_median_trend);
element.querySelector('#data_lone_joins').innerHTML = data.lone_joins + smallTrend(data.lone_joins_trend);
element.querySelector('#data_lone_new_joins').innerHTML = data.lone_new_joins + smallTrend(data.lone_new_joins_trend);
}
/* This function loads Sessions tab */

View File

@ -4,26 +4,35 @@ function refreshingJsonRequest(address, callback, tabID) {
? `${address}&timestamp=${timestamp}`
: `${address}?timestamp=${timestamp}`
const refreshElement = document.querySelector(`#${tabID} .refresh-element`);
refreshElement.querySelector('i').addEventListener('click', () => {
refreshElement.querySelector('.refresh-notice').innerHTML = '<i class="fa fa-fw fa-cog fa-spin"></i> Updating..';
refreshingJsonRequest(address, callback, tabID);
});
let timeout = 1000;
function makeTheRequest() {
jsonRequest(addressWithTimestamp, (json, error) => {
const refreshElement = document.querySelector(`#${tabID} .refresh-element`);
if (error) {
if (error.status === 400 && error.error.includes('Attempt to get data from the future!')) {
console.error(error.error); // System time not in sync with UTC
refreshElement.innerHTML = "System times out of sync with UTC";
return jsonRequest(address, callback);
}
refreshElement.querySelector('.refresh-notice').remove();
refreshElement.querySelector('.refresh-notice').innerHTML = "";
return callback(json, error);
}
refreshElement.querySelector('.refresh-time').innerText = json.timestamp_f;
const lastUpdated = json.timestamp;
// TODO Work out the kinks with the refresh barrier time
if (lastUpdated < timestamp) {
setTimeout(makeTheRequest, 5000);
setTimeout(makeTheRequest, timeout);
timeout = timeout >= 12000 ? timeout : timeout * 2;
} else {
refreshElement.querySelector('.refresh-notice').remove();
refreshElement.querySelector('.refresh-notice').innerHTML = "";
}
callback(json, error);
})

View File

@ -159,7 +159,7 @@
<h1 class="h3 mb-0 text-gray-800"><i class="sidebar-toggler fa fa-fw fa-bars"></i>${serverDisplayName}
&middot; Server Overview
<span class="refresh-element">
<i class="far fa-fw fa-clock"></i> <span class="refresh-time"></span>
<i class="fa fa-fw fa-sync"></i> <span class="refresh-time"></span>
<span class="refresh-notice"><i class="fa fa-fw fa-cog fa-spin"></i> Updating..</span>
</span>
</h1>
@ -333,7 +333,12 @@
<!-- Page Heading -->
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800"><i class="sidebar-toggler fa fa-fw fa-bars"></i>${serverDisplayName}
&middot; Online Activity Overview</h1>
&middot; Online Activity Overview
<span class="refresh-element">
<i class="fa fa-fw fa-sync"></i> <span class="refresh-time"></span>
<span class="refresh-notice"><i class="fa fa-fw fa-cog fa-spin"></i> Updating..</span>
</span>
</h1>
${backButton}
</div>
@ -451,7 +456,8 @@
</td>
<td id="data_new_players_retention_30d"></td>
<td id="data_new_players_retention_7d"></td>
<td id="data_new_players_retention_24h"></td>
<td id="data_new_players_retention_24h"
title="This value is a prediction based on previous players."></td>
</tr>
</tbody>
<tbody>
@ -1296,7 +1302,7 @@
try {
setLoadingText('Calculating values..');
refreshingJsonRequest("../v1/serverOverview?server=${serverUUID}", loadserverOverviewValues, 'server-overview');
jsonRequest("../v1/onlineOverview?server=${serverUUID}", loadOnlineActivityOverviewValues);
refreshingJsonRequest("../v1/onlineOverview?server=${serverUUID}", loadOnlineActivityOverviewValues, 'online-activity-overview');
jsonRequest("../v1/sessionsOverview?server=${serverUUID}", loadSessionValues);
jsonRequest("../v1/playerVersus?server=${serverUUID}", loadPvPPvEValues);
jsonRequest("../v1/playerbaseOverview?server=${serverUUID}", loadPlayerbaseOverviewValues);