From 085ec45fb168cf2a6e9d0505219a840dc170291c Mon Sep 17 00:00:00 2001
From: anhefti
Date: Mon, 28 Mar 2022 17:29:26 +0200
Subject: [PATCH] separated clientConnection and clientConnectionData page
filter and sort
---
.../model/session/ClientConnectionData.java | 22 +++
.../gui/content/monitoring/FinishedExam.java | 16 +-
.../api/ClientConnectionController.java | 147 ++++++++++++++++--
3 files changed, 153 insertions(+), 32 deletions(-)
diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientConnectionData.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientConnectionData.java
index 9fa6a567..a610f4c5 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientConnectionData.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientConnectionData.java
@@ -17,8 +17,10 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
+import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
+import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
import ch.ethz.seb.sebserver.gbl.util.Utils;
@JsonIgnoreProperties(ignoreUnknown = true)
@@ -100,6 +102,26 @@ public class ClientConnectionData implements GrantEntity {
return this.missingPing || this.pendingNotification;
}
+ @JsonIgnore
+ public Double getIndicatorValue(final Long indicatorId) {
+ return this.indicatorValues
+ .stream()
+ .filter(indicatorValue -> indicatorValue.getIndicatorId().equals(indicatorId))
+ .findFirst()
+ .map(iv -> iv.getValue())
+ .orElse(Double.NaN);
+ }
+
+ @JsonIgnore
+ public String getIndicatorDisplayValue(final Indicator indicator) {
+ return this.indicatorValues
+ .stream()
+ .filter(indicatorValue -> indicatorValue.getIndicatorId().equals(indicator.id))
+ .findFirst()
+ .map(iv -> IndicatorValue.getDisplayValue(iv, indicator.type))
+ .orElse(Constants.EMPTY_NOTE);
+ }
+
public ClientConnection getClientConnection() {
return this.clientConnection;
}
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/FinishedExam.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/FinishedExam.java
index dbe2ddfc..d25a1aae 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/FinishedExam.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/FinishedExam.java
@@ -10,14 +10,12 @@ package ch.ethz.seb.sebserver.gui.content.monitoring;
import java.util.Collection;
import java.util.function.BooleanSupplier;
-import java.util.function.Function;
import org.eclipse.swt.widgets.Composite;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
-import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
@@ -26,7 +24,6 @@ import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
-import ch.ethz.seb.sebserver.gbl.model.session.IndicatorValue;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
@@ -162,7 +159,7 @@ public class FinishedExam implements TemplateComposer {
tableBuilder.withColumn(new ColumnDefinition<>(
indicator.name,
new LocTextKey(indicator.name),
- indicatorValueFunction(indicator)));
+ cc -> cc.getIndicatorDisplayValue(indicator)));
});
final EntityTable table = tableBuilder.compose(pageContext.copyOf(content));
@@ -175,15 +172,4 @@ public class FinishedExam implements TemplateComposer {
.publishIf(isExamSupporter, false);
}
- public Function indicatorValueFunction(final Indicator indicator) {
- return clientConnectionData -> {
- return clientConnectionData.indicatorValues
- .stream()
- .filter(indicatorValue -> indicatorValue.getIndicatorId().equals(indicator.id))
- .findFirst()
- .map(iv -> IndicatorValue.getDisplayValue(iv, indicator.type))
- .orElse(Constants.EMPTY_NOTE);
- };
- }
-
}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientConnectionController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientConnectionController.java
index f4458849..daef8596 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientConnectionController.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientConnectionController.java
@@ -82,6 +82,73 @@ public class ClientConnectionController extends ReadonlyEntityController
+ * GET /{api}/{domain-entity-name}
+ *
+ * For example for the "exam" domain-entity
+ * GET /admin-api/v1/exam
+ * GET /admin-api/v1/exam?page_number=2&page_size=10&sort=-name
+ * GET /admin-api/v1/exam?name=seb&active=true
+ *
+ * Sorting: the sort parameter to sort the list of entities before paging
+ * the sort parameter is the name of the entity-model attribute to sort with a leading '-' sign for
+ * descending sort order. Note that not all entity-model attribute are suited for sorting while the most
+ * are.
+ *
+ * Filter: The filter attributes accepted by this API depend on the actual entity model (domain object)
+ * and are of the form [domain-attribute-name]=[filter-value]. E.g.: name=abc or type=EXAM. Usually
+ * filter attributes of text type are treated as SQL wildcard with %[text]% to filter all text containing
+ * a given text-snippet.
+ *
+ * @param institutionId The institution identifier of the request.
+ * Default is the institution identifier of the institution of the current user
+ * @param pageNumber the number of the page that is requested
+ * @param pageSize the size of the page that is requested
+ * @param sort the sort parameter to sort the list of entities before paging
+ * the sort parameter is the name of the entity-model attribute to sort with a leading '-' sign for
+ * descending sort order.
+ * @param allRequestParams a MultiValueMap of all request parameter that is used for filtering.
+ * @return Page of domain-model-entities of specified type */
+ @Override
+ @RequestMapping(
+ method = RequestMethod.GET,
+ consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
+ produces = MediaType.APPLICATION_JSON_VALUE)
+ public Page getPage(
+ @RequestParam(
+ name = API.PARAM_INSTITUTION_ID,
+ required = true,
+ defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
+ @RequestParam(name = Page.ATTR_PAGE_NUMBER, required = false) final Integer pageNumber,
+ @RequestParam(name = Page.ATTR_PAGE_SIZE, required = false) final Integer pageSize,
+ @RequestParam(name = Page.ATTR_SORT, required = false) final String sort,
+ @RequestParam final MultiValueMap allRequestParams,
+ final HttpServletRequest request) {
+
+ // at least current user must have read access for specified entity type within its own institution
+ checkReadPrivilege(institutionId);
+
+ final FilterMap filterMap = new FilterMap(allRequestParams, request.getQueryString());
+ populateFilterMap(filterMap, institutionId, sort);
+
+ if (StringUtils.isNotBlank(sort) || filterMap.containsAny(EXT_FILTER)) {
+
+ final Collection allConnections = getAll(filterMap)
+ .getOrThrow();
+
+ return this.paginationService.buildPageFromList(
+ pageNumber,
+ pageSize,
+ sort,
+ allConnections,
+ pageClientConnectionFunction(filterMap, sort));
+
+ } else {
+ return super.getPage(institutionId, pageNumber, pageSize, sort, allRequestParams, request);
+ }
+ }
+
@RequestMapping(
path = API.SEB_CLIENT_CONNECTION_DATA_ENDPOINT,
method = RequestMethod.GET,
@@ -114,7 +181,7 @@ public class ClientConnectionController extends ReadonlyEntityController> getAll(final FilterMap filterMap) {
-// final String infoFilter = filterMap.getString(ClientConnection.FILTER_ATTR_INFO);
-// if (StringUtils.isNotBlank(infoFilter)) {
-// return super.getAll(filterMap)
-// .map(all -> all.stream().filter(c -> c.getInfo() == null || c.getInfo().contains(infoFilter))
-// .collect(Collectors.toList()));
-// }
-//
-// return super.getAll(filterMap);
-// }
-
@Override
public Collection getDependencies(
final String modelId,
@@ -198,15 +253,35 @@ public class ClientConnectionController extends ReadonlyEntityController, List> pageFunction(
+ private Function, List> pageClientConnectionFunction(
final FilterMap filterMap,
final String sort) {
return connections -> {
- final List filtered = connections.stream()
- .filter(getFilter(filterMap))
+ final List filtered = connections
+ .stream()
+ .filter(getClientConnectionFilter(filterMap))
.collect(Collectors.toList());
+
+ if (StringUtils.isNotBlank(sort)) {
+ filtered.sort(new ClientConnectionComparator(sort));
+ }
+ return filtered;
+ };
+ }
+
+ private Function, List> pageClientConnectionDataFunction(
+ final FilterMap filterMap,
+ final String sort) {
+
+ return connections -> {
+
+ final List filtered = connections
+ .stream()
+ .filter(getClientConnectionDataFilter(filterMap))
+ .collect(Collectors.toList());
+
if (StringUtils.isNotBlank(sort)) {
filtered.sort(new ClientConnectionDataComparator(sort));
}
@@ -214,7 +289,16 @@ public class ClientConnectionController extends ReadonlyEntityController getFilter(final FilterMap filterMap) {
+ private Predicate getClientConnectionFilter(final FilterMap filterMap) {
+ final String infoFilter = filterMap.getString(ClientConnection.FILTER_ATTR_INFO);
+ Predicate filter = Utils.truePredicate();
+ if (StringUtils.isNotBlank(infoFilter)) {
+ filter = c -> c.getInfo() == null || c.getInfo().contains(infoFilter);
+ }
+ return filter;
+ }
+
+ private Predicate getClientConnectionDataFilter(final FilterMap filterMap) {
final String infoFilter = filterMap.getString(ClientConnection.FILTER_ATTR_INFO);
Predicate filter = Utils.truePredicate();
if (StringUtils.isNotBlank(infoFilter)) {
@@ -223,6 +307,35 @@ public class ClientConnectionController extends ReadonlyEntityController {
+
+ final String sortColumn;
+ final boolean descending;
+
+ ClientConnectionComparator(final String sort) {
+ this.sortColumn = PageSortOrder.decode(sort);
+ this.descending = PageSortOrder.getSortOrder(sort) == PageSortOrder.DESCENDING;
+ }
+
+ @Override
+ public int compare(final ClientConnection cc1, final ClientConnection cc2) {
+ int result = 0;
+ if (Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID.equals(this.sortColumn)) {
+ result = cc1.userSessionId
+ .compareTo(cc2.userSessionId);
+ } else if (ClientConnection.ATTR_INFO.equals(this.sortColumn)) {
+ result = cc1.getInfo().compareTo(cc2.getInfo());
+ } else if (Domain.CLIENT_CONNECTION.ATTR_STATUS.equals(this.sortColumn)) {
+ result = cc1.getStatus()
+ .compareTo(cc2.getStatus());
+ } else {
+ result = cc1.userSessionId
+ .compareTo(cc2.userSessionId);
+ }
+ return (this.descending) ? -result : result;
+ }
+ }
+
private static final class ClientConnectionDataComparator implements Comparator {
final String sortColumn;