add issue filters to exam monitoring
This commit is contained in:
parent
7ffef0938f
commit
8f0f6918f1
11 changed files with 317 additions and 17 deletions
|
@ -219,6 +219,8 @@ public final class API {
|
|||
public static final String EXAM_MONITORING_SIGNATURE_KEY_ENDPOINT = "/signature";
|
||||
public static final String EXAM_MONITORING_STATE_FILTER = "hidden-states";
|
||||
public static final String EXAM_MONITORING_CLIENT_GROUP_FILTER = "hidden-client-group";
|
||||
public static final String EXAM_MONITORING_ISSUE_FILTER = "hidden-issues";
|
||||
|
||||
public static final String EXAM_MONITORING_FINISHED_ENDPOINT = "/finishedexams";
|
||||
public static final String EXAM_MONITORING_SEB_CONNECTION_TOKEN_PATH_SEGMENT =
|
||||
"/{" + EXAM_API_SEB_CONNECTION_TOKEN + "}";
|
||||
|
|
|
@ -57,6 +57,17 @@ public final class ClientConnection implements GrantEntity {
|
|||
}
|
||||
}
|
||||
|
||||
public enum ConnectionIssueStatus {
|
||||
ASK_GRANTED(0),
|
||||
SEB_VERSION_GRANTED(1);
|
||||
|
||||
public final int code;
|
||||
|
||||
ConnectionIssueStatus(final int code){
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
public final static List<String> ACTIVE_STATES = Utils.immutableListOf(
|
||||
ConnectionStatus.ACTIVE.name(),
|
||||
ConnectionStatus.READY.name(),
|
||||
|
|
|
@ -18,6 +18,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
|||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionIssueStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringDataView;
|
||||
|
||||
|
@ -27,6 +28,7 @@ public class MonitoringSEBConnectionData {
|
|||
public static final String ATTR_CONNECTIONS = "cons";
|
||||
public static final String ATTR_STATUS_MAPPING = "sm";
|
||||
public static final String ATTR_CLIENT_GROUP_MAPPING = "cgm";
|
||||
public static final String ATTR_ISSUE_MAPPING = "im";
|
||||
|
||||
@JsonProperty(ATTR_CONNECTIONS)
|
||||
public final Collection<? extends ClientMonitoringDataView> monitoringData;
|
||||
|
@ -37,25 +39,33 @@ public class MonitoringSEBConnectionData {
|
|||
@JsonProperty(ATTR_CLIENT_GROUP_MAPPING)
|
||||
public final Map<Long, Integer> connectionsPerClientGroup;
|
||||
|
||||
@JsonProperty(ATTR_ISSUE_MAPPING)
|
||||
public final int[] connectionPerIssue;
|
||||
|
||||
|
||||
@JsonCreator
|
||||
public MonitoringSEBConnectionData(
|
||||
@JsonProperty(ATTR_CONNECTIONS) final Collection<ClientMonitoringData> connections,
|
||||
@JsonProperty(ATTR_STATUS_MAPPING) final int[] connectionsPerStatus,
|
||||
@JsonProperty(ATTR_ISSUE_MAPPING) final int[] connectionPerIssue,
|
||||
@JsonProperty(ATTR_CLIENT_GROUP_MAPPING) final Map<Long, Integer> connectionsPerClientGroup) {
|
||||
|
||||
this.monitoringData = connections;
|
||||
this.connectionsPerStatus = connectionsPerStatus;
|
||||
this.connectionPerIssue = connectionPerIssue;
|
||||
this.connectionsPerClientGroup = connectionsPerClientGroup;
|
||||
}
|
||||
|
||||
public MonitoringSEBConnectionData(
|
||||
final int[] connectionsPerStatus,
|
||||
final Map<Long, Integer> connectionsPerClientGroup,
|
||||
final int[] connectionsPerIssue,
|
||||
final Collection<? extends ClientMonitoringDataView> connections) {
|
||||
|
||||
this.monitoringData = connections;
|
||||
this.connectionsPerStatus = connectionsPerStatus;
|
||||
this.connectionsPerClientGroup = connectionsPerClientGroup;
|
||||
this.connectionPerIssue = connectionsPerIssue;
|
||||
this.monitoringData = connections;
|
||||
}
|
||||
|
||||
public Collection<? extends ClientMonitoringDataView> getMonitoringData() {
|
||||
|
@ -82,6 +92,14 @@ public class MonitoringSEBConnectionData {
|
|||
return this.connectionsPerClientGroup.get(clientGroupId);
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public int getNumberOfConnection(final ConnectionIssueStatus connectionIssueStatus) {
|
||||
if (this.connectionPerIssue == null || this.connectionPerIssue.length <= connectionIssueStatus.code) {
|
||||
return -1;
|
||||
}
|
||||
return this.connectionPerIssue[connectionIssueStatus.code];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
|
|
@ -47,8 +47,9 @@ public enum ActionCategory {
|
|||
VARIA(new LocTextKey("sebserver.overall.action.category.varia"), 0),
|
||||
STATE_FILTER(new LocTextKey("sebserver.exam.monitoring.action.category.statefilter"), 40),
|
||||
GROUP_FILTER(new LocTextKey("sebserver.exam.monitoring.action.category.groupfilter"), 50),
|
||||
PROCTORING(new LocTextKey("sebserver.exam.overall.action.category.proctoring"), 60),
|
||||
SCREEN_PROCTORING(new LocTextKey("sebserver.exam.overall.action.category.screenproctoring"), 65),
|
||||
ISSUE_FILTER(new LocTextKey("sebserver.exam.monitoring.action.category.issuefilter"), 60),
|
||||
PROCTORING(new LocTextKey("sebserver.exam.overall.action.category.proctoring"), 70),
|
||||
SCREEN_PROCTORING(new LocTextKey("sebserver.exam.overall.action.category.screenproctoring"), 75),
|
||||
|
||||
FINISHED_EXAM_LIST(new LocTextKey("sebserver.finished.exam.list.actions"), 1);
|
||||
|
||||
|
|
|
@ -1030,6 +1030,30 @@ public enum ActionDefinition {
|
|||
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
||||
ActionCategory.GROUP_FILTER),
|
||||
|
||||
MONITOR_EXAM_HIDE_ASK_GRANTED(
|
||||
new LocTextKey("sebserver.monitoring.exam.connection.action.hide.askgranted"),
|
||||
ImageIcon.TOGGLE_OFF,
|
||||
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
||||
ActionCategory.ISSUE_FILTER),
|
||||
|
||||
MONITOR_EXAM_SHOW_ASK_GRANTED(
|
||||
new LocTextKey("sebserver.monitoring.exam.connection.action.show.askgranted"),
|
||||
ImageIcon.TOGGLE_ON,
|
||||
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
||||
ActionCategory.ISSUE_FILTER),
|
||||
|
||||
MONITOR_EXAM_HIDE_SEB_VERSION_GRANTED(
|
||||
new LocTextKey("sebserver.monitoring.exam.connection.action.hide.sebversiongranted"),
|
||||
ImageIcon.TOGGLE_OFF,
|
||||
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
||||
ActionCategory.ISSUE_FILTER),
|
||||
|
||||
MONITOR_EXAM_SHOW_SEB_VERSION_GRANTED(
|
||||
new LocTextKey("sebserver.monitoring.exam.connection.action.show.sebversiongranted"),
|
||||
ImageIcon.TOGGLE_ON,
|
||||
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
||||
ActionCategory.ISSUE_FILTER),
|
||||
|
||||
MONITORING_EXAM_SEARCH_CONNECTIONS(
|
||||
new LocTextKey("sebserver.monitoring.search.action"),
|
||||
ImageIcon.SEARCH,
|
||||
|
|
|
@ -43,6 +43,7 @@ import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings;
|
|||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringFeature;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ScreenProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionIssueStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.RemoteProctoringRoom;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ScreenProctoringGroup;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
|
||||
|
@ -437,6 +438,23 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
ActionDefinition.MONITOR_EXAM_SHOW_DISABLED_CONNECTION,
|
||||
ActionDefinition.MONITOR_EXAM_HIDE_DISABLED_CONNECTION);
|
||||
|
||||
addIssueFilterAction(
|
||||
monitoringStatus,
|
||||
statusFilterGUIUpdate,
|
||||
actionBuilder,
|
||||
clientTable,
|
||||
ConnectionIssueStatus.ASK_GRANTED,
|
||||
ActionDefinition.MONITOR_EXAM_SHOW_ASK_GRANTED,
|
||||
ActionDefinition.MONITOR_EXAM_HIDE_ASK_GRANTED);
|
||||
addIssueFilterAction(
|
||||
monitoringStatus,
|
||||
statusFilterGUIUpdate,
|
||||
actionBuilder,
|
||||
clientTable,
|
||||
ConnectionIssueStatus.SEB_VERSION_GRANTED,
|
||||
ActionDefinition.MONITOR_EXAM_SHOW_SEB_VERSION_GRANTED,
|
||||
ActionDefinition.MONITOR_EXAM_HIDE_SEB_VERSION_GRANTED);
|
||||
|
||||
if (clientGroups != null && !clientGroups.isEmpty()) {
|
||||
clientGroups.forEach(clientGroup -> {
|
||||
|
||||
|
@ -455,6 +473,34 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
return statusFilterGUIUpdate;
|
||||
}
|
||||
|
||||
private void addIssueFilterAction(
|
||||
final MonitoringFilter filter,
|
||||
final FilterGUIUpdate filterGUIUpdate,
|
||||
final PageActionBuilder actionBuilder,
|
||||
final ClientConnectionTable clientTable,
|
||||
final ConnectionIssueStatus connectionIssueStatus,
|
||||
final ActionDefinition showActionDef,
|
||||
final ActionDefinition hideActionDef) {
|
||||
|
||||
|
||||
final int numOfConnections = filter.getNumOfConnections(connectionIssueStatus);
|
||||
final PageAction action = actionBuilder.newAction(hideActionDef)
|
||||
.withExec(hideIssueViewAction(filter, clientTable, connectionIssueStatus))
|
||||
.noEventPropagation()
|
||||
.withSwitchAction(
|
||||
actionBuilder.newAction(showActionDef)
|
||||
.withExec(showIssueViewAction(filter, clientTable, connectionIssueStatus))
|
||||
.noEventPropagation()
|
||||
.withNameAttributes(numOfConnections)
|
||||
.create())
|
||||
.withNameAttributes(numOfConnections)
|
||||
.create();
|
||||
|
||||
this.pageService.publishAction(
|
||||
action,
|
||||
treeItem -> filterGUIUpdate.register(treeItem, connectionIssueStatus));
|
||||
}
|
||||
|
||||
private void addFilterAction(
|
||||
final MonitoringFilter filter,
|
||||
final FilterGUIUpdate filterGUIUpdate,
|
||||
|
@ -544,6 +590,7 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
|
||||
private final PolyglotPageService polyglotPageService;
|
||||
private final TreeItem[] actionItemPerStateFilter = new TreeItem[ConnectionStatus.values().length];
|
||||
private final TreeItem[] actionItemPerIssueFilter = new TreeItem[ConnectionIssueStatus.values().length];
|
||||
private final Map<Long, TreeItem> actionItemPerClientGroup = new HashMap<>();
|
||||
|
||||
public FilterGUIUpdate(final PolyglotPageService polyglotPageService) {
|
||||
|
@ -558,6 +605,10 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
this.actionItemPerClientGroup.put(clientGroupId, item);
|
||||
}
|
||||
|
||||
void register(final TreeItem item, final ConnectionIssueStatus status) {
|
||||
this.actionItemPerIssueFilter[status.code] = item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(final MonitoringFilter monitoringStatus) {
|
||||
final ConnectionStatus[] states = ConnectionStatus.values();
|
||||
|
@ -572,6 +623,19 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
}
|
||||
}
|
||||
|
||||
final ConnectionIssueStatus[] connectionIssueStates = ConnectionIssueStatus.values();
|
||||
for (int i = 0; i < connectionIssueStates.length; i++) {
|
||||
final ConnectionIssueStatus state = connectionIssueStates[i];
|
||||
final int numOfConnections = monitoringStatus.getNumOfConnections(state);
|
||||
if (numOfConnections >= 0 && this.actionItemPerIssueFilter[state.code] != null) {
|
||||
final TreeItem treeItem = this.actionItemPerIssueFilter[state.code];
|
||||
final PageAction action = (PageAction) treeItem.getData(ActionPane.ACTION_EVENT_CALL_KEY);
|
||||
action.setTitleArgument(0, numOfConnections);
|
||||
this.polyglotPageService.injectI18n(treeItem, action.getTitle());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!this.actionItemPerClientGroup.isEmpty()) {
|
||||
this.actionItemPerClientGroup.entrySet().stream().forEach(entry -> {
|
||||
final int numOfConnections = monitoringStatus.getNumOfConnections(entry.getKey());
|
||||
|
@ -639,6 +703,32 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
};
|
||||
}
|
||||
|
||||
private static Function<PageAction, PageAction> showIssueViewAction(
|
||||
final MonitoringFilter monitoringStatus,
|
||||
final ClientConnectionTable clientTable,
|
||||
final ConnectionIssueStatus connectionIssueStatus) {
|
||||
|
||||
return action -> {
|
||||
monitoringStatus.showIssue(connectionIssueStatus);
|
||||
clientTable.removeSelection();
|
||||
return action;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private static Function<PageAction, PageAction> hideIssueViewAction(
|
||||
final MonitoringFilter monitoringStatus,
|
||||
final ClientConnectionTable clientTable,
|
||||
final ConnectionIssueStatus connectionIssueStatus) {
|
||||
|
||||
return action -> {
|
||||
monitoringStatus.hideIssue(connectionIssueStatus);
|
||||
clientTable.removeSelection();
|
||||
return action;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private Set<EntityKey> selectionForInstruction(final ClientConnectionTable clientTable) {
|
||||
final Set<String> connectionTokens = clientTable.getConnectionTokens(
|
||||
cc -> cc.getStatus().clientActiveStatus,
|
||||
|
|
|
@ -15,6 +15,7 @@ import java.util.EnumSet;
|
|||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -26,6 +27,7 @@ import ch.ethz.seb.sebserver.gbl.async.AsyncRunner;
|
|||
import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionIssueStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringFullPageData;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
|
||||
|
@ -47,6 +49,9 @@ public class FullPageMonitoringUpdate implements MonitoringFilter {
|
|||
static final Logger log = LoggerFactory.getLogger(FullPageMonitoringUpdate.class);
|
||||
|
||||
private static final String USER_SESSION_STATUS_FILTER_ATTRIBUTE = "USER_SESSION_STATUS_FILTER";
|
||||
|
||||
private static final String USER_SESSION_ISSUE_FILTER_ATTRIBUTE = "USER_SESSION_ISSUE_FILTER";
|
||||
|
||||
private static final String USER_SESSION_GROUP_FILTER_ATTRIBUTE = "USER_SESSION_GROUP_FILTER";
|
||||
|
||||
private final ServerPushService serverPushService;
|
||||
|
@ -59,6 +64,10 @@ public class FullPageMonitoringUpdate implements MonitoringFilter {
|
|||
|
||||
private final EnumSet<ConnectionStatus> statusFilter;
|
||||
private String statusFilterParam = "";
|
||||
|
||||
private final EnumSet<ConnectionIssueStatus> issueFilter;
|
||||
private String issueFilterParam = "";
|
||||
|
||||
private final Set<Long> clientGroupFilter;
|
||||
private String clientGroupFilterParam = "";
|
||||
private boolean filterChanged = false;
|
||||
|
@ -83,7 +92,10 @@ public class FullPageMonitoringUpdate implements MonitoringFilter {
|
|||
this.guiUpdates = guiUpdates;
|
||||
|
||||
this.statusFilter = EnumSet.noneOf(ConnectionStatus.class);
|
||||
loadFilter();
|
||||
loadStatusFilter();
|
||||
|
||||
this.issueFilter = EnumSet.noneOf(ConnectionIssueStatus.class);
|
||||
loadIssueFilter();
|
||||
|
||||
final Collection<ClientGroup> clientGroups = pageService.getRestService()
|
||||
.getBuilder(GetClientGroups.class)
|
||||
|
@ -127,6 +139,16 @@ public class FullPageMonitoringUpdate implements MonitoringFilter {
|
|||
return this.statusFilterParam;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<ConnectionIssueStatus> getIssueFilter() {
|
||||
return this.issueFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIssueFilterParam() {
|
||||
return this.issueFilterParam;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean filterChanged() {
|
||||
return this.filterChanged;
|
||||
|
@ -184,6 +206,18 @@ public class FullPageMonitoringUpdate implements MonitoringFilter {
|
|||
saveFilter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideIssue(final ConnectionIssueStatus connectionIssueStatus) {
|
||||
this.issueFilter.add(connectionIssueStatus);
|
||||
saveFilter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showIssue(final ConnectionIssueStatus connectionIssueStatus){
|
||||
this.issueFilter.remove(connectionIssueStatus);
|
||||
saveFilter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MonitoringFullPageData getMonitoringFullPageData() {
|
||||
return this.monitoringFullPageData;
|
||||
|
@ -214,9 +248,10 @@ public class FullPageMonitoringUpdate implements MonitoringFilter {
|
|||
}
|
||||
|
||||
private void updateBusinessData() {
|
||||
|
||||
RestCall<MonitoringFullPageData>.RestCallBuilder restCallBuilder = this.restCallBuilder
|
||||
.withHeader(API.EXAM_MONITORING_STATE_FILTER, this.statusFilterParam);
|
||||
.withHeader(API.EXAM_MONITORING_STATE_FILTER, this.statusFilterParam)
|
||||
.withHeader(API.EXAM_MONITORING_ISSUE_FILTER, this.issueFilterParam);
|
||||
|
||||
if (hasClientGroupFilter()) {
|
||||
restCallBuilder = restCallBuilder
|
||||
.withHeader(API.EXAM_MONITORING_CLIENT_GROUP_FILTER, this.clientGroupFilterParam);
|
||||
|
@ -249,6 +284,11 @@ public class FullPageMonitoringUpdate implements MonitoringFilter {
|
|||
.putAttribute(
|
||||
USER_SESSION_STATUS_FILTER_ATTRIBUTE,
|
||||
StringUtils.join(this.statusFilter, Constants.LIST_SEPARATOR));
|
||||
this.pageService
|
||||
.getCurrentUser()
|
||||
.putAttribute(
|
||||
USER_SESSION_ISSUE_FILTER_ATTRIBUTE,
|
||||
StringUtils.join(this.issueFilter, Constants.LIST_SEPARATOR));
|
||||
if (hasClientGroupFilter()) {
|
||||
this.pageService
|
||||
.getCurrentUser()
|
||||
|
@ -260,6 +300,7 @@ public class FullPageMonitoringUpdate implements MonitoringFilter {
|
|||
log.warn("Failed to save status filter to user session");
|
||||
} finally {
|
||||
this.statusFilterParam = StringUtils.join(this.statusFilter, Constants.LIST_SEPARATOR);
|
||||
this.issueFilterParam = StringUtils.join(this.issueFilter, Constants.LIST_SEPARATOR);
|
||||
if (hasClientGroupFilter()) {
|
||||
this.clientGroupFilterParam = StringUtils.join(this.clientGroupFilter, Constants.LIST_SEPARATOR);
|
||||
}
|
||||
|
@ -267,7 +308,7 @@ public class FullPageMonitoringUpdate implements MonitoringFilter {
|
|||
}
|
||||
}
|
||||
|
||||
private void loadFilter() {
|
||||
private void loadStatusFilter() {
|
||||
try {
|
||||
final String attribute = this.pageService
|
||||
.getCurrentUser()
|
||||
|
@ -306,6 +347,26 @@ public class FullPageMonitoringUpdate implements MonitoringFilter {
|
|||
}
|
||||
}
|
||||
|
||||
private void loadIssueFilter() {
|
||||
try {
|
||||
final String attribute = this.pageService
|
||||
.getCurrentUser()
|
||||
.getAttribute(USER_SESSION_ISSUE_FILTER_ATTRIBUTE);
|
||||
this.issueFilter.clear();
|
||||
if (attribute != null) {
|
||||
Arrays.asList(StringUtils.split(attribute, Constants.LIST_SEPARATOR))
|
||||
.forEach(name -> this.issueFilter.add(ConnectionIssueStatus.valueOf(name)));
|
||||
}
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.warn("Failed to load status filter to user session");
|
||||
this.issueFilter.clear();
|
||||
} finally {
|
||||
this.issueFilterParam = StringUtils.join(this.issueFilter, Constants.LIST_SEPARATOR);
|
||||
this.filterChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void recoverFromDisposedRestTemplate(final Exception error) {
|
||||
try {
|
||||
if (log.isDebugEnabled()) {
|
||||
|
|
|
@ -14,6 +14,7 @@ import java.util.EnumSet;
|
|||
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionIssueStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.RemoteProctoringRoom;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ScreenProctoringGroup;
|
||||
|
@ -26,6 +27,10 @@ public interface MonitoringFilter {
|
|||
|
||||
String getStatusFilterParam();
|
||||
|
||||
EnumSet<ConnectionIssueStatus> getIssueFilter();
|
||||
|
||||
String getIssueFilterParam();
|
||||
|
||||
boolean filterChanged();
|
||||
|
||||
void resetFilterChanged();
|
||||
|
@ -48,6 +53,10 @@ public interface MonitoringFilter {
|
|||
|
||||
void showClientGroup(Long clientGroupId);
|
||||
|
||||
void hideIssue(ConnectionIssueStatus connectionIssueStatus);
|
||||
|
||||
void showIssue(ConnectionIssueStatus connectionIssueStatus);
|
||||
|
||||
MonitoringFullPageData getMonitoringFullPageData();
|
||||
|
||||
default MonitoringSEBConnectionData getMonitoringSEBConnectionData() {
|
||||
|
@ -87,6 +96,15 @@ public interface MonitoringFilter {
|
|||
}
|
||||
}
|
||||
|
||||
default int getNumOfConnections(final ConnectionIssueStatus connectionIssueStatus) {
|
||||
final MonitoringSEBConnectionData monitoringSEBConnectionData = getMonitoringSEBConnectionData();
|
||||
if (monitoringSEBConnectionData != null) {
|
||||
return monitoringSEBConnectionData.getNumberOfConnection(connectionIssueStatus);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
default Collection<RemoteProctoringRoom> proctoringData() {
|
||||
final MonitoringFullPageData monitoringFullPageData = getMonitoringFullPageData();
|
||||
if (monitoringFullPageData != null) {
|
||||
|
|
|
@ -13,6 +13,7 @@ import java.io.OutputStream;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -44,6 +45,7 @@ import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
|||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionIssueStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringDataView;
|
||||
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringSEBConnectionData;
|
||||
|
@ -437,6 +439,11 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
|||
? new HashMap<>()
|
||||
: null;
|
||||
|
||||
final int[] issueMapping = new int[ConnectionIssueStatus.values().length];
|
||||
for (int i = 0; i < issueMapping.length; i++) {
|
||||
issueMapping[i] = 0;
|
||||
}
|
||||
|
||||
updateClientConnections(examId);
|
||||
|
||||
final List<? extends ClientMonitoringDataView> filteredConnections = this.clientConnectionDAO
|
||||
|
@ -448,6 +455,7 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
|||
.map(c -> {
|
||||
statusMapping[c.clientConnection.status.code]++;
|
||||
processClientGroupMapping(c.groups, clientGroupMapping);
|
||||
processIssueMapping(c.clientConnection, issueMapping);
|
||||
return c;
|
||||
})
|
||||
.filter(filter)
|
||||
|
@ -457,6 +465,7 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
|||
return new MonitoringSEBConnectionData(
|
||||
statusMapping,
|
||||
clientGroupMapping,
|
||||
issueMapping,
|
||||
filteredConnections);
|
||||
});
|
||||
}
|
||||
|
@ -633,6 +642,20 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
|||
});
|
||||
}
|
||||
|
||||
private void processIssueMapping(final ClientConnection clientConnection, final int[] issueMapping){
|
||||
if (clientConnection == null || issueMapping == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(BooleanUtils.isFalse(clientConnection.securityCheckGranted)){
|
||||
issueMapping[0] += 1;
|
||||
}
|
||||
|
||||
if(BooleanUtils.isFalse(clientConnection.clientVersionGranted)){
|
||||
issueMapping[1] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<String, Long> duplicateCheck = new HashMap<>();
|
||||
|
||||
private ClientConnectionDataInternal getForTokenAndCheckDuplication(
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.util.stream.Stream;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.Valid;
|
||||
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -48,6 +49,7 @@ import ch.ethz.seb.sebserver.gbl.model.Page;
|
|||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||
import ch.ethz.seb.sebserver.gbl.model.institution.SecurityKey;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionIssueStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientNotification;
|
||||
|
@ -265,14 +267,14 @@ public class ExamMonitoringController {
|
|||
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
||||
@PathVariable(name = API.PARAM_PARENT_MODEL_ID, required = true) final Long examId,
|
||||
@RequestHeader(name = API.EXAM_MONITORING_STATE_FILTER, required = false) final String hiddenStates,
|
||||
@RequestHeader(
|
||||
name = API.EXAM_MONITORING_CLIENT_GROUP_FILTER,
|
||||
required = false) final String hiddenClientGroups) {
|
||||
@RequestHeader(name = API.EXAM_MONITORING_CLIENT_GROUP_FILTER, required = false) final String hiddenClientGroups,
|
||||
@RequestHeader(name = API.EXAM_MONITORING_ISSUE_FILTER, required = false) final String hiddenIssues){
|
||||
|
||||
|
||||
checkPrivileges(institutionId, examId);
|
||||
|
||||
return this.examSessionService
|
||||
.getConnectionData(examId, createMonitoringFilter(hiddenStates, hiddenClientGroups))
|
||||
.getConnectionData(examId, createMonitoringFilter(hiddenStates, hiddenClientGroups, hiddenIssues))
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
|
@ -315,9 +317,9 @@ public class ExamMonitoringController {
|
|||
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
||||
@PathVariable(name = API.PARAM_PARENT_MODEL_ID, required = true) final Long examId,
|
||||
@RequestHeader(name = API.EXAM_MONITORING_STATE_FILTER, required = false) final String hiddenStates,
|
||||
@RequestHeader(
|
||||
name = API.EXAM_MONITORING_CLIENT_GROUP_FILTER,
|
||||
required = false) final String hiddenClientGroups) {
|
||||
@RequestHeader(name = API.EXAM_MONITORING_CLIENT_GROUP_FILTER, required = false) final String hiddenClientGroups,
|
||||
@RequestHeader(name = API.EXAM_MONITORING_ISSUE_FILTER, required = false) final String hiddenIssues){
|
||||
|
||||
|
||||
// TODO respond this within another Thread-pool (Executor)
|
||||
// TODO try to cache some MonitoringSEBConnectionData throughout multiple requests (for about 2 sec.)
|
||||
|
@ -328,7 +330,7 @@ public class ExamMonitoringController {
|
|||
final MonitoringSEBConnectionData monitoringSEBConnectionData = this.examSessionService
|
||||
.getMonitoringSEBConnectionsData(
|
||||
examId,
|
||||
createMonitoringFilter(hiddenStates, hiddenClientGroups))
|
||||
createMonitoringFilter(hiddenStates, hiddenClientGroups, hiddenIssues))
|
||||
.getOrThrow();
|
||||
|
||||
final boolean proctoringEnabled = this.examAdminService.isProctoringEnabled(runningExam);
|
||||
|
@ -544,6 +546,20 @@ public class ExamMonitoringController {
|
|||
return conn -> conn != null && !filterStates.contains(conn.clientConnection.status);
|
||||
}
|
||||
|
||||
private Predicate<ClientConnectionData> activeIssueFilterSebVersion(final EnumSet<ConnectionIssueStatus> filterStates) {
|
||||
if(!filterStates.contains(ConnectionIssueStatus.SEB_VERSION_GRANTED)){
|
||||
return null;
|
||||
}
|
||||
return conn -> conn != null && BooleanUtils.isFalse(conn.clientConnection.clientVersionGranted);
|
||||
}
|
||||
|
||||
private Predicate<ClientConnectionData> activeIssueFilterAsk(final EnumSet<ConnectionIssueStatus> filterStates) {
|
||||
if(!filterStates.contains(ConnectionIssueStatus.ASK_GRANTED)){
|
||||
return null;
|
||||
}
|
||||
return conn -> conn != null && BooleanUtils.isFalse(conn.clientConnection.securityCheckGranted);
|
||||
}
|
||||
|
||||
/** If we have a filter criteria for ACTIVE connection, we shall filter only the active connections
|
||||
* that has no incident. */
|
||||
private Predicate<ClientConnectionData> withActiveFilter(final EnumSet<ConnectionStatus> filterStates) {
|
||||
|
@ -560,7 +576,8 @@ public class ExamMonitoringController {
|
|||
|
||||
private Predicate<ClientConnectionData> createMonitoringFilter(
|
||||
final String hiddenStates,
|
||||
final String hiddenClientGroups) {
|
||||
final String hiddenClientGroups,
|
||||
final String hiddenIssues) {
|
||||
|
||||
final EnumSet<ConnectionStatus> filterStates = EnumSet.noneOf(ConnectionStatus.class);
|
||||
if (StringUtils.isNotBlank(hiddenStates)) {
|
||||
|
@ -581,6 +598,25 @@ public class ExamMonitoringController {
|
|||
? withActiveFilter(filterStates)
|
||||
: noneActiveFilter(filterStates);
|
||||
|
||||
final EnumSet<ConnectionIssueStatus> filterIssues = EnumSet.noneOf(ConnectionIssueStatus.class);
|
||||
if (StringUtils.isNotBlank(hiddenIssues)) {
|
||||
final String[] split = StringUtils.split(hiddenIssues, Constants.LIST_SEPARATOR);
|
||||
for (final String s : split) {
|
||||
filterIssues.add(ConnectionIssueStatus.valueOf(s));
|
||||
}
|
||||
}
|
||||
|
||||
final Predicate<ClientConnectionData> issueFilterSebVersion =
|
||||
filterIssues.isEmpty()
|
||||
? null
|
||||
: activeIssueFilterSebVersion(filterIssues);
|
||||
|
||||
final Predicate<ClientConnectionData> issueFilterAsk =
|
||||
filterIssues.isEmpty()
|
||||
? null
|
||||
: activeIssueFilterAsk(filterIssues);
|
||||
|
||||
|
||||
Set<Long> filterClientGroups = null;
|
||||
if (StringUtils.isNotBlank(hiddenClientGroups)) {
|
||||
filterClientGroups = new HashSet<>();
|
||||
|
@ -595,7 +631,18 @@ public class ExamMonitoringController {
|
|||
if (ccd == null) {
|
||||
return false;
|
||||
}
|
||||
return stateFilter.test(ccd) && ccd.filter(_filterClientGroups);
|
||||
|
||||
boolean result = stateFilter.test(ccd) && ccd.filter(_filterClientGroups);
|
||||
|
||||
if (issueFilterSebVersion != null) {
|
||||
result = result && issueFilterSebVersion.test(ccd);
|
||||
}
|
||||
|
||||
if (issueFilterAsk != null) {
|
||||
result = result && issueFilterAsk.test(ccd);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -2191,6 +2191,7 @@ sebserver.monitoring.exam.action.viewroom=View {0} ( {1} / {2} )
|
|||
sebserver.monitoring.exam.action.viewgroup=View {0} ( {1} )
|
||||
sebserver.exam.monitoring.action.category.statefilter=State Filter
|
||||
sebserver.exam.monitoring.action.category.groupfilter=Client Group Filter
|
||||
sebserver.exam.monitoring.action.category.issuefilter=Issue Filter
|
||||
sebserver.exam.overall.action.category.proctoring=Live Proctoring
|
||||
sebserver.monitoring.exam.action.proctoring.openTownhall=Open Townhall
|
||||
sebserver.monitoring.exam.action.proctoring.showTownhall=Show Townhall
|
||||
|
@ -2282,6 +2283,10 @@ sebserver.monitoring.exam.connection.action.hide.undefined=Hide Undefined ( {0}
|
|||
sebserver.monitoring.exam.connection.action.show.undefined=Show Undefined ( {0} )
|
||||
sebserver.monitoring.exam.connection.action.hide.clientgroup=Hide {0} ( {1} )
|
||||
sebserver.monitoring.exam.connection.action.show.clientgroup=Show {0} ( {1} )
|
||||
sebserver.monitoring.exam.connection.action.hide.askgranted=Hide Ask Granted ( {0} )
|
||||
sebserver.monitoring.exam.connection.action.show.askgranted=Show Ask Granted ( {0} )
|
||||
sebserver.monitoring.exam.connection.action.hide.sebversiongranted=Hide SEB Version Granted ( {0} )
|
||||
sebserver.monitoring.exam.connection.action.show.sebversiongranted=Show SEB Version Granted ( {0} )
|
||||
sebserver.monitoring.exam.connection.action.proctoring=Single Room Proctoring
|
||||
sebserver.monitoring.exam.connection.action.proctoring.examroom=Exam Room Proctoring
|
||||
sebserver.monitoring.exam.connection.action.openTownhall.confirm=You are about to open the town-hall room and force all SEB clients to join the town-hall room.<br/>Are you sure to open the town-hall?
|
||||
|
|
Loading…
Reference in a new issue