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;