diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/QueryJSONResolver.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/QueryJSONResolver.java index 7bd1421b0..028fdad28 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/QueryJSONResolver.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/QueryJSONResolver.java @@ -120,9 +120,11 @@ public class QueryJSONResolver implements Resolver { q = URLDecoder.decode(q, "UTF-8"); List queries = FilterQuery.parse(q); Filter.Result result = filters.apply(queries); + List resultPath = result.getInverseResultPath(); + Collections.reverse(resultPath); Map json = Maps.builder(String.class, Object.class) - .put("path", result.getResultPath()) + .put("path", resultPath) .put("view", new Gson().fromJson(view, FiltersJSONResolver.ViewJSON.class)) .put("timestamp", timestamp) .build(); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/filter/Filter.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/filter/Filter.java index cf42e9705..9925e74ee 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/filter/Filter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/filter/Filter.java @@ -68,7 +68,7 @@ public interface Filter { } public Result notApplied(Filter filter) { - return new Result(this, filter.getKind(), currentUUIDs); + return new Result(this, filter.getKind() + " (skip)", currentUUIDs); } public boolean isEmpty() { @@ -79,7 +79,7 @@ public interface Filter { return currentUUIDs; } - public List getResultPath() { + public List getInverseResultPath() { List path = new ArrayList<>(); Result current = this; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/filter/filters/MultiOptionFilter.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/filter/filters/MultiOptionFilter.java index 47e2d536f..a82f9935b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/filter/filters/MultiOptionFilter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/filter/filters/MultiOptionFilter.java @@ -16,6 +16,7 @@ */ package com.djrapitops.plan.storage.database.queries.filter.filters; +import com.djrapitops.plan.storage.database.queries.filter.CompleteSetException; import com.djrapitops.plan.storage.database.queries.filter.Filter; import com.djrapitops.plan.storage.database.queries.filter.FilterQuery; import com.google.gson.Gson; @@ -31,7 +32,9 @@ public abstract class MultiOptionFilter implements Filter { } protected List getSelected(FilterQuery query) { - String selected = query.get("selected").orElseThrow(IllegalArgumentException::new); - return new Gson().fromJson(selected, new TypeToken>() {}.getType()); + String selectedJSON = query.get("selected").orElseThrow(IllegalArgumentException::new); + List selected = new Gson().fromJson(selectedJSON, new TypeToken>() {}.getType()); + if (selected.isEmpty()) throw new CompleteSetException(); + return selected; } } diff --git a/Plan/common/src/main/resources/assets/plan/web/js/query.js b/Plan/common/src/main/resources/assets/plan/web/js/query.js index 4df7f0567..101ce2e54 100644 --- a/Plan/common/src/main/resources/assets/plan/web/js/query.js +++ b/Plan/common/src/main/resources/assets/plan/web/js/query.js @@ -420,84 +420,123 @@ function getQueryAddress() { } function runQuery() { - const queryButton = document.querySelector('#query-button'); + const queryButton = document.getElementById('query-button'); queryButton.setAttribute('disabled', 'true'); queryButton.classList.add('disabled'); - document.querySelector('#content .tab').innerHTML = - `
- -

Loading..

-
`; + // document.querySelector('#content .tab').innerHTML = + // `
+ // + //

Loading..

+ //
`; jsonRequest(getQueryAddress(), function (json, error) { - if (!json.data) { - // TODO write proper error messages - window.history.replaceState({}, '', `${location.pathname}?error=${encodeURIComponent(error ? error : 'Query produced 0 results')}`); + const previousPath = document.getElementById('result-path'); + if (previousPath) previousPath.remove(); + console.log(json); + if (json) { + if (json.data) { + renderResults(json); + } else if (json.path) { + // filters resulted in 0 players matched + renderResultPath(json); + // Reset query + queryButton.removeAttribute('disabled'); + queryButton.classList.remove('disabled'); + query.splice(0, query.length); + } else { + // Cached query expired + window.history.replaceState({}, '', `${location.pathname}?error=${encodeURIComponent('Cached query has expired')}`); + location.reload(); + } + } else if (error) { + window.history.replaceState({}, '', `${location.pathname}?error=${encodeURIComponent(error)}`); location.reload(); } - - renderDataResultScreen(json.data.players.data.length, json.view ? json.view : {}); - - // Set URL so that the query result can be shared - window.history.replaceState({}, '', `${location.pathname}?timestamp=${json.timestamp}`); - - // Player table - $('.player-table').DataTable({ - responsive: true, - columns: json.data.players.columns, - data: json.data.players.data, - order: [[5, "desc"]] - }); - const activityIndexHeader = document.querySelector("#DataTables_Table_0 thead th:nth-of-type(2)"); - const lastSeenHeader = document.querySelector("#DataTables_Table_0 thead th:nth-of-type(6)"); - activityIndexHeader.innerHTML += ` (${json.view.beforeDate})` - lastSeenHeader.innerHTML += ` (view)` - - // Activity graphs - const activity_data = json.data.activity; - activityPie('activityPie', { - name: 'Players', colorByPoint: true, data: activity_data.activity_pie_series - }); - stackChart('activityStackGraph', activity_data.activity_labels, activity_data.activity_series, 'Players'); - document.querySelector("#activity-date").innerHTML = json.view.beforeDate; - - // Geolocations - const geolocation_data = json.data.geolocation; - const geolocationSeries = { - name: 'Players', - type: 'map', - mapData: Highcharts.maps['custom/world'], - data: geolocation_data.geolocation_series, - joinBy: ['iso-a3', 'code'] - }; - const geolocationBarSeries = { - color: geolocation_data.colors.bars, - name: 'Players', - data: geolocation_data.geolocation_bar_series.map(function (bar) { - return bar.value - }) - }; - const geolocationBarCategories = geolocation_data.geolocation_bar_series.map(function (bar) { - return bar.label - }); - worldMap('worldMap', geolocation_data.colors.low, geolocation_data.colors.high, geolocationSeries); - horizontalBarChart('countryBarChart', geolocationBarCategories, [geolocationBarSeries], 'Players'); - - const session_data = json.data.sessions; - - document.querySelector("#data_total_playtime").innerHTML = session_data.total_playtime; - document.querySelector("#data_average_playtime").innerHTML = session_data.average_playtime; - document.querySelector("#data_total_afk_playtime").innerHTML = session_data.total_afk_playtime; - document.querySelector("#data_average_afk_playtime").innerHTML = session_data.average_afk_playtime; - document.querySelector("#data_total_active_playtime").innerHTML = session_data.total_active_playtime; - document.querySelector("#data_average_active_playtime").innerHTML = session_data.average_active_playtime; - document.querySelector("#data_total_sessions").innerHTML = session_data.total_sessions; - document.querySelector("#data_average_sessions").innerHTML = session_data.average_sessions; - document.querySelector("#data_average_session_length").innerHTML = session_data.average_session_length; }); } +function renderResultPath(json) { + let pathHtml = ``; + for (let i = 0; i < json.path.length; i++) { + const step = json.path[i]; + pathHtml += `

`; + for (let j = 0; j < i * 4; j++) { + pathHtml += " "; + } + pathHtml += ` ${step.kind} matched ${step.size} players

` + } + + const placeBefore = document.querySelector('.tab .row .card'); + const element = document.createElement('div'); + element.id = "result-path" + element.classList.add("alert", "alert-warning", "shadow"); + element.innerHTML = pathHtml + placeBefore.insertAdjacentElement('beforebegin', element); + window.scrollTo(0, 0); // Scroll to top +} + +function renderResults(json) { + renderDataResultScreen(json.data.players.data.length, json.view ? json.view : {}); + + // Set URL so that the query result can be shared + window.history.replaceState({}, '', `${location.pathname}?timestamp=${json.timestamp}`); + + // Player table + $('.player-table').DataTable({ + responsive: true, + columns: json.data.players.columns, + data: json.data.players.data, + order: [[5, "desc"]] + }); + const activityIndexHeader = document.querySelector("#DataTables_Table_0 thead th:nth-of-type(2)"); + const lastSeenHeader = document.querySelector("#DataTables_Table_0 thead th:nth-of-type(6)"); + activityIndexHeader.innerHTML += ` (${json.view.beforeDate})` + lastSeenHeader.innerHTML += ` (view)` + + // Activity graphs + const activity_data = json.data.activity; + activityPie('activityPie', { + name: 'Players', colorByPoint: true, data: activity_data.activity_pie_series + }); + stackChart('activityStackGraph', activity_data.activity_labels, activity_data.activity_series, 'Players'); + document.querySelector("#activity-date").innerHTML = json.view.beforeDate; + + // Geolocations + const geolocation_data = json.data.geolocation; + const geolocationSeries = { + name: 'Players', + type: 'map', + mapData: Highcharts.maps['custom/world'], + data: geolocation_data.geolocation_series, + joinBy: ['iso-a3', 'code'] + }; + const geolocationBarSeries = { + color: geolocation_data.colors.bars, + name: 'Players', + data: geolocation_data.geolocation_bar_series.map(function (bar) { + return bar.value + }) + }; + const geolocationBarCategories = geolocation_data.geolocation_bar_series.map(function (bar) { + return bar.label + }); + worldMap('worldMap', geolocation_data.colors.low, geolocation_data.colors.high, geolocationSeries); + horizontalBarChart('countryBarChart', geolocationBarCategories, [geolocationBarSeries], 'Players'); + + const session_data = json.data.sessions; + + document.querySelector("#data_total_playtime").innerHTML = session_data.total_playtime; + document.querySelector("#data_average_playtime").innerHTML = session_data.average_playtime; + document.querySelector("#data_total_afk_playtime").innerHTML = session_data.total_afk_playtime; + document.querySelector("#data_average_afk_playtime").innerHTML = session_data.average_afk_playtime; + document.querySelector("#data_total_active_playtime").innerHTML = session_data.total_active_playtime; + document.querySelector("#data_average_active_playtime").innerHTML = session_data.average_active_playtime; + document.querySelector("#data_total_sessions").innerHTML = session_data.total_sessions; + document.querySelector("#data_average_sessions").innerHTML = session_data.average_sessions; + document.querySelector("#data_average_session_length").innerHTML = session_data.average_session_length; +} + function renderDataResultScreen(resultCount, view) { const afterDate = filterView.afterDate ? filterView.afterDate : view.afterDate; const beforeDate = filterView.beforeDate ? filterView.beforeDate : view.beforeDate;